From b1931d65795274a9c15c6248b9d9bfeaaaf64732 Mon Sep 17 00:00:00 2001 From: Haojin Tang Date: Tue, 20 Jan 2026 11:12:57 +0800 Subject: [PATCH 01/18] feat(bsp): add xiangshan bsp for kmhv2 --- bsp/xiangshan/.config | 1581 +++++++++++++++++ bsp/xiangshan/.gitignore | 3 + bsp/xiangshan/Kconfig | 61 + bsp/xiangshan/README.md | 407 +++++ bsp/xiangshan/README_cn.md | 411 +++++ bsp/xiangshan/SConscript | 14 + bsp/xiangshan/SConstruct | 62 + bsp/xiangshan/applications/SConscript | 17 + bsp/xiangshan/applications/main.c | 19 + bsp/xiangshan/applications/test/SConscript | 17 + .../applications/test/test_vector/SConscript | 9 + .../test/test_vector/test_vector.c | 178 ++ bsp/xiangshan/driver/Kconfig | 43 + bsp/xiangshan/driver/SConscript | 19 + bsp/xiangshan/driver/asm/sbiasm.h | 10 + bsp/xiangshan/driver/asm/sbidef.h | 27 + bsp/xiangshan/driver/board.c | 118 ++ bsp/xiangshan/driver/board.h | 34 + bsp/xiangshan/driver/drv_uart.c | 165 ++ bsp/xiangshan/driver/drv_uart.h | 62 + bsp/xiangshan/driver/drv_virtio.c | 100 ++ bsp/xiangshan/driver/drv_virtio.h | 16 + bsp/xiangshan/driver/virt.h | 30 + bsp/xiangshan/link.lds | 201 +++ bsp/xiangshan/link_cpus.lds | 1 + bsp/xiangshan/link_smart.lds | 202 +++ bsp/xiangshan/link_stacksize.lds | 1 + bsp/xiangshan/qemu-dbg.sh | 16 + bsp/xiangshan/qemu-dumpdtb.sh | 1 + bsp/xiangshan/qemu-nographic.bat | 9 + bsp/xiangshan/qemu-rv64ilp32-nographic.sh | 1 + bsp/xiangshan/qemu-v-dbg.sh | 4 + bsp/xiangshan/qemu-v-nographic.sh | 9 + bsp/xiangshan/rtconfig.h | 554 ++++++ bsp/xiangshan/rtconfig.py | 51 + bsp/xiangshan/run.sh | 43 + bsp/xiangshan/smart-env.bat | 30 + .../compilers/common/extension/SConscript | 2 +- components/libc/cplusplus/cxx_crt.cpp | 4 +- components/libc/cplusplus/cxx_crt.h | 4 +- components/libc/posix/io/mman/mman.c | 28 +- libcpu/risc-v/virt64/plic.c | 6 +- 42 files changed, 4551 insertions(+), 19 deletions(-) create mode 100644 bsp/xiangshan/.config create mode 100644 bsp/xiangshan/.gitignore create mode 100644 bsp/xiangshan/Kconfig create mode 100644 bsp/xiangshan/README.md create mode 100644 bsp/xiangshan/README_cn.md create mode 100644 bsp/xiangshan/SConscript create mode 100644 bsp/xiangshan/SConstruct create mode 100644 bsp/xiangshan/applications/SConscript create mode 100644 bsp/xiangshan/applications/main.c create mode 100644 bsp/xiangshan/applications/test/SConscript create mode 100644 bsp/xiangshan/applications/test/test_vector/SConscript create mode 100644 bsp/xiangshan/applications/test/test_vector/test_vector.c create mode 100644 bsp/xiangshan/driver/Kconfig create mode 100644 bsp/xiangshan/driver/SConscript create mode 100644 bsp/xiangshan/driver/asm/sbiasm.h create mode 100644 bsp/xiangshan/driver/asm/sbidef.h create mode 100644 bsp/xiangshan/driver/board.c create mode 100644 bsp/xiangshan/driver/board.h create mode 100644 bsp/xiangshan/driver/drv_uart.c create mode 100644 bsp/xiangshan/driver/drv_uart.h create mode 100644 bsp/xiangshan/driver/drv_virtio.c create mode 100644 bsp/xiangshan/driver/drv_virtio.h create mode 100644 bsp/xiangshan/driver/virt.h create mode 100644 bsp/xiangshan/link.lds create mode 100644 bsp/xiangshan/link_cpus.lds create mode 100644 bsp/xiangshan/link_smart.lds create mode 100644 bsp/xiangshan/link_stacksize.lds create mode 100755 bsp/xiangshan/qemu-dbg.sh create mode 100755 bsp/xiangshan/qemu-dumpdtb.sh create mode 100644 bsp/xiangshan/qemu-nographic.bat create mode 100755 bsp/xiangshan/qemu-rv64ilp32-nographic.sh create mode 100644 bsp/xiangshan/qemu-v-dbg.sh create mode 100644 bsp/xiangshan/qemu-v-nographic.sh create mode 100644 bsp/xiangshan/rtconfig.h create mode 100644 bsp/xiangshan/rtconfig.py create mode 100755 bsp/xiangshan/run.sh create mode 100644 bsp/xiangshan/smart-env.bat diff --git a/bsp/xiangshan/.config b/bsp/xiangshan/.config new file mode 100644 index 00000000000..8ed52cd1c3e --- /dev/null +++ b/bsp/xiangshan/.config @@ -0,0 +1,1581 @@ + +# +# RT-Thread Kernel +# + +# +# klibc options +# + +# +# rt_vsnprintf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSNPRINTF is not set +CONFIG_RT_KLIBC_USING_VSNPRINTF_LONGLONG=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER=y +# CONFIG_RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS is not set +CONFIG_RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE=32 +CONFIG_RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE=32 +CONFIG_RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION=6 +CONFIG_RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9 +CONFIG_RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS=4 +# end of rt_vsnprintf options + +# +# rt_vsscanf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSSCANF is not set +# end of rt_vsscanf options + +# +# rt_memset options +# +# CONFIG_RT_KLIBC_USING_USER_MEMSET is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMSET is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMSET is not set +# end of rt_memset options + +# +# rt_memcpy options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMCPY is not set +# end of rt_memcpy options + +# +# rt_memmove options +# +# CONFIG_RT_KLIBC_USING_USER_MEMMOVE is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMMOVE is not set +# end of rt_memmove options + +# +# rt_memcmp options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCMP is not set +# end of rt_memcmp options + +# +# rt_strstr options +# +# CONFIG_RT_KLIBC_USING_USER_STRSTR is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRSTR is not set +# end of rt_strstr options + +# +# rt_strcasecmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCASECMP is not set +# end of rt_strcasecmp options + +# +# rt_strncpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCPY is not set +# end of rt_strncpy options + +# +# rt_strcpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCPY is not set +# end of rt_strcpy options + +# +# rt_strncmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCMP is not set +# end of rt_strncmp options + +# +# rt_strcmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCMP is not set +# end of rt_strcmp options + +# +# rt_strlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRLEN is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRLEN is not set +# end of rt_strlen options + +# +# rt_strnlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRNLEN is not set +# end of rt_strnlen options +# end of klibc options + +CONFIG_RT_NAME_MAX=24 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +# CONFIG_RT_USING_NANO is not set +# CONFIG_RT_USING_SMART is not set +# CONFIG_RT_USING_AMP is not set +# CONFIG_RT_USING_SMP is not set +CONFIG_RT_CPUS_NR=1 +CONFIG_RT_ALIGN_SIZE=8 +# CONFIG_RT_THREAD_PRIORITY_8 is not set +CONFIG_RT_THREAD_PRIORITY_32=y +# CONFIG_RT_THREAD_PRIORITY_256 is not set +CONFIG_RT_THREAD_PRIORITY_MAX=32 +CONFIG_RT_TICK_PER_SECOND=100 +CONFIG_RT_USING_OVERFLOW_CHECK=y +CONFIG_RT_USING_HOOK=y +CONFIG_RT_HOOK_USING_FUNC_PTR=y +# CONFIG_RT_USING_HOOKLIST is not set +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=16384 +CONFIG_RT_USING_TIMER_SOFT=y +CONFIG_RT_TIMER_THREAD_PRIO=4 +CONFIG_RT_TIMER_THREAD_STACK_SIZE=16384 +# CONFIG_RT_USING_TIMER_ALL_SOFT is not set +CONFIG_RT_USING_CPU_USAGE_TRACER=y + +# +# kservice options +# +# CONFIG_RT_USING_TINY_FFS is not set +# end of kservice options + +CONFIG_RT_USING_DEBUG=y +CONFIG_RT_DEBUGING_ASSERT=y +CONFIG_RT_DEBUGING_COLOR=y +CONFIG_RT_DEBUGING_CONTEXT=y +# CONFIG_RT_DEBUGING_AUTO_INIT is not set +# CONFIG_RT_USING_CI_ACTION is not set + +# +# Inter-Thread communication +# +CONFIG_RT_USING_SEMAPHORE=y +CONFIG_RT_USING_MUTEX=y +CONFIG_RT_USING_EVENT=y +CONFIG_RT_USING_MAILBOX=y +CONFIG_RT_USING_MESSAGEQUEUE=y +# CONFIG_RT_USING_MESSAGEQUEUE_PRIORITY is not set +CONFIG_RT_USING_SIGNALS=y +# end of Inter-Thread communication + +# +# Memory Management +# +CONFIG_RT_USING_MEMPOOL=y +# CONFIG_RT_USING_SMALL_MEM is not set +CONFIG_RT_USING_SLAB=y +# CONFIG_RT_USING_MEMHEAP is not set +# CONFIG_RT_USING_SMALL_MEM_AS_HEAP is not set +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set +CONFIG_RT_USING_SLAB_AS_HEAP=y +# CONFIG_RT_USING_USERHEAP is not set +# CONFIG_RT_USING_NOHEAP is not set +CONFIG_RT_USING_MEMTRACE=y +# CONFIG_RT_USING_HEAP_ISR is not set +CONFIG_RT_USING_HEAP=y +# end of Memory Management + +CONFIG_RT_USING_DEVICE=y +CONFIG_RT_USING_DEVICE_OPS=y +# CONFIG_RT_USING_INTERRUPT_INFO is not set +# CONFIG_RT_USING_THREADSAFE_PRINTF is not set +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=256 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart0" +CONFIG_RT_VER_NUM=0x50300 +# CONFIG_RT_USING_STDC_ATOMIC is not set +CONFIG_RT_BACKTRACE_LEVEL_MAX_NR=32 +# end of RT-Thread Kernel + +CONFIG_ARCH_CPU_64BIT=y +CONFIG_RT_USING_CACHE=y +CONFIG_ARCH_MM_MMU=y +CONFIG_ARCH_RISCV=y +CONFIG_ARCH_RISCV64=y +CONFIG_ARCH_USING_NEW_CTX_SWITCH=y +CONFIG_ARCH_USING_RISCV_COMMON64=y +CONFIG_ARCH_REMAP_KERNEL=y + +# +# RT-Thread Components +# +CONFIG_RT_USING_COMPONENTS_INIT=y +CONFIG_RT_USING_USER_MAIN=y +CONFIG_RT_MAIN_THREAD_STACK_SIZE=8388608 +CONFIG_RT_MAIN_THREAD_PRIORITY=10 +# CONFIG_RT_USING_LEGACY is not set +CONFIG_RT_USING_MSH=y +CONFIG_RT_USING_FINSH=y +CONFIG_FINSH_USING_MSH=y +CONFIG_FINSH_THREAD_NAME="tshell" +CONFIG_FINSH_THREAD_PRIORITY=20 +CONFIG_FINSH_THREAD_STACK_SIZE=16384 +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=10 +# CONFIG_FINSH_USING_WORD_OPERATION is not set +# CONFIG_FINSH_USING_FUNC_EXT is not set +CONFIG_FINSH_USING_SYMTAB=y +CONFIG_FINSH_CMD_SIZE=80 +CONFIG_MSH_USING_BUILT_IN_COMMANDS=y +CONFIG_FINSH_USING_DESCRIPTION=y +# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set +# CONFIG_FINSH_USING_AUTH is not set +CONFIG_FINSH_ARG_MAX=10 +CONFIG_FINSH_USING_OPTION_COMPLETION=y + +# +# DFS: device virtual file system +# +CONFIG_RT_USING_DFS=y +CONFIG_DFS_USING_POSIX=y +CONFIG_DFS_USING_WORKDIR=y +CONFIG_DFS_FD_MAX=32 +# CONFIG_RT_USING_DFS_V1 is not set +CONFIG_RT_USING_DFS_V2=y +CONFIG_RT_USING_DFS_ELMFAT=y + +# +# elm-chan's FatFs, Generic FAT Filesystem Module +# +CONFIG_RT_DFS_ELM_CODE_PAGE=437 +CONFIG_RT_DFS_ELM_WORD_ACCESS=y +# CONFIG_RT_DFS_ELM_USE_LFN_0 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_1 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_2 is not set +CONFIG_RT_DFS_ELM_USE_LFN_3=y +CONFIG_RT_DFS_ELM_USE_LFN=3 +CONFIG_RT_DFS_ELM_LFN_UNICODE_0=y +# CONFIG_RT_DFS_ELM_LFN_UNICODE_1 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_2 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_3 is not set +CONFIG_RT_DFS_ELM_LFN_UNICODE=0 +CONFIG_RT_DFS_ELM_MAX_LFN=255 +CONFIG_RT_DFS_ELM_DRIVES=2 +CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=512 +# CONFIG_RT_DFS_ELM_USE_ERASE is not set +CONFIG_RT_DFS_ELM_REENTRANT=y +CONFIG_RT_DFS_ELM_MUTEX_TIMEOUT=3000 +# CONFIG_RT_DFS_ELM_USE_EXFAT is not set +# end of elm-chan's FatFs, Generic FAT Filesystem Module + +CONFIG_RT_USING_DFS_DEVFS=y +CONFIG_RT_USING_DFS_ROMFS=y +# CONFIG_RT_USING_DFS_CROMFS is not set +# CONFIG_RT_USING_DFS_TMPFS is not set +# CONFIG_RT_USING_DFS_MQUEUE is not set +# end of DFS: device virtual file system + +# CONFIG_RT_USING_FAL is not set + +# +# Device Drivers +# +# CONFIG_RT_USING_DM is not set +# CONFIG_RT_USING_DEV_BUS is not set +CONFIG_RT_USING_DEVICE_IPC=y +CONFIG_RT_UNAMED_PIPE_NUMBER=64 +CONFIG_RT_USING_SYSTEM_WORKQUEUE=y +CONFIG_RT_SYSTEM_WORKQUEUE_STACKSIZE=8192 +CONFIG_RT_SYSTEM_WORKQUEUE_PRIORITY=23 +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_USING_SERIAL_V1=y +# CONFIG_RT_USING_SERIAL_V2 is not set +CONFIG_RT_SERIAL_USING_DMA=y +CONFIG_RT_SERIAL_RB_BUFSZ=64 +# CONFIG_RT_USING_SERIAL_BYPASS is not set +# CONFIG_RT_USING_CAN is not set +CONFIG_RT_USING_CPUTIME=y +CONFIG_RT_USING_CPUTIME_RISCV=y +CONFIG_CPUTIME_TIMER_FREQ=10000000 +# CONFIG_RT_USING_I2C is not set +# CONFIG_RT_USING_PHY is not set +# CONFIG_RT_USING_PHY_V2 is not set +# CONFIG_RT_USING_ADC is not set +# CONFIG_RT_USING_DAC is not set +CONFIG_RT_USING_NULL=y +CONFIG_RT_USING_ZERO=y +CONFIG_RT_USING_RANDOM=y +# CONFIG_RT_USING_PWM is not set +# CONFIG_RT_USING_PULSE_ENCODER is not set +# CONFIG_RT_USING_INPUT_CAPTURE is not set +# CONFIG_RT_USING_MTD_NOR is not set +# CONFIG_RT_USING_MTD_NAND is not set +# CONFIG_RT_USING_PM is not set +CONFIG_RT_USING_RTC=y +# CONFIG_RT_USING_ALARM is not set +CONFIG_RT_USING_SOFT_RTC=y +# CONFIG_RT_USING_SDIO is not set +# CONFIG_RT_USING_SPI is not set +# CONFIG_RT_USING_WDT is not set +# CONFIG_RT_USING_AUDIO is not set +# CONFIG_RT_USING_SENSOR is not set +# CONFIG_RT_USING_TOUCH is not set +# CONFIG_RT_USING_LCD is not set +# CONFIG_RT_USING_HWCRYPTO is not set +# CONFIG_RT_USING_WIFI is not set +# CONFIG_RT_USING_BLK is not set +# CONFIG_RT_USING_VIRTIO is not set +# CONFIG_RT_USING_VIRTIO_MMIO_ALIGN is not set +# CONFIG_RT_USING_PIN is not set +CONFIG_RT_USING_KTIME=y +# CONFIG_RT_USING_HWTIMER is not set +# CONFIG_RT_USING_CHERRYUSB is not set +# end of Device Drivers + +# +# C/C++ and POSIX layer +# + +# +# ISO-ANSI C layer +# + +# +# Timezone and Daylight Saving Time +# +# CONFIG_RT_LIBC_USING_FULL_TZ_DST is not set +CONFIG_RT_LIBC_USING_LIGHT_TZ_DST=y +CONFIG_RT_LIBC_TZ_DEFAULT_HOUR=8 +CONFIG_RT_LIBC_TZ_DEFAULT_MIN=0 +CONFIG_RT_LIBC_TZ_DEFAULT_SEC=0 +# end of Timezone and Daylight Saving Time +# end of ISO-ANSI C layer + +# +# POSIX (Portable Operating System Interface) layer +# +CONFIG_RT_USING_POSIX_FS=y +CONFIG_RT_USING_POSIX_DEVIO=y +CONFIG_RT_USING_POSIX_STDIO=y +CONFIG_RT_USING_POSIX_POLL=y +CONFIG_RT_USING_POSIX_SELECT=y +# CONFIG_RT_USING_POSIX_EVENTFD is not set +# CONFIG_RT_USING_POSIX_TIMERFD is not set +# CONFIG_RT_USING_POSIX_SOCKET is not set +CONFIG_RT_USING_POSIX_TERMIOS=y +CONFIG_RT_USING_POSIX_AIO=y +CONFIG_RT_USING_POSIX_MMAN=y +CONFIG_RT_USING_POSIX_DELAY=y +CONFIG_RT_USING_POSIX_CLOCK=y +CONFIG_RT_USING_POSIX_TIMER=y +# CONFIG_RT_USING_PTHREADS is not set +# CONFIG_RT_USING_MODULE is not set + +# +# Interprocess Communication (IPC) +# +CONFIG_RT_USING_POSIX_PIPE=y +CONFIG_RT_USING_POSIX_PIPE_SIZE=512 +# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set + +# +# Socket is in the 'Network' category +# +# end of Interprocess Communication (IPC) +# end of POSIX (Portable Operating System Interface) layer + +CONFIG_RT_USING_CPLUSPLUS=y +# CONFIG_RT_USING_CPLUSPLUS11 is not set +CONFIG_RT_USING_CPP_WRAPPER=y +# CONFIG_RT_USING_CPP_EXCEPTIONS is not set +# end of C/C++ and POSIX layer + +# +# Network +# +CONFIG_RT_USING_SAL=y +CONFIG_SAL_INTERNET_CHECK=y +CONFIG_SOCKET_TABLE_STEP_LEN=4 + +# +# Docking with protocol stacks +# +CONFIG_SAL_USING_LWIP=y +# CONFIG_SAL_USING_AT is not set +# CONFIG_SAL_USING_TLS is not set +# end of Docking with protocol stacks + +CONFIG_SAL_USING_POSIX=y +CONFIG_RT_USING_NETDEV=y +CONFIG_NETDEV_USING_IFCONFIG=y +CONFIG_NETDEV_USING_PING=y +CONFIG_NETDEV_USING_NETSTAT=y +CONFIG_NETDEV_USING_AUTO_DEFAULT=y +# CONFIG_NETDEV_USING_LINK_STATUS_CALLBACK is not set +# CONFIG_NETDEV_USING_IPV6 is not set +CONFIG_NETDEV_IPV4=1 +CONFIG_NETDEV_IPV6=0 +CONFIG_RT_USING_LWIP=y +# CONFIG_RT_USING_LWIP_LOCAL_VERSION is not set +# CONFIG_RT_USING_LWIP141 is not set +CONFIG_RT_USING_LWIP203=y +# CONFIG_RT_USING_LWIP212 is not set +# CONFIG_RT_USING_LWIP_LATEST is not set +CONFIG_RT_USING_LWIP_VER_NUM=0x20003 +# CONFIG_RT_USING_LWIP_IPV6 is not set +CONFIG_RT_LWIP_MEM_ALIGNMENT=4 +CONFIG_RT_LWIP_IGMP=y +CONFIG_RT_LWIP_ICMP=y +# CONFIG_RT_LWIP_SNMP is not set +CONFIG_RT_LWIP_DNS=y +CONFIG_RT_LWIP_DHCP=y +CONFIG_IP_SOF_BROADCAST=1 +CONFIG_IP_SOF_BROADCAST_RECV=1 + +# +# Static IPv4 Address +# +CONFIG_RT_LWIP_IPADDR="192.168.1.30" +CONFIG_RT_LWIP_GWADDR="192.168.1.1" +CONFIG_RT_LWIP_MSKADDR="255.255.255.0" +# end of Static IPv4 Address + +CONFIG_RT_LWIP_UDP=y +CONFIG_RT_LWIP_TCP=y +CONFIG_RT_LWIP_RAW=y +# CONFIG_RT_LWIP_PPP is not set +CONFIG_RT_MEMP_NUM_NETCONN=8 +CONFIG_RT_LWIP_PBUF_NUM=16 +CONFIG_RT_LWIP_RAW_PCB_NUM=4 +CONFIG_RT_LWIP_UDP_PCB_NUM=4 +CONFIG_RT_LWIP_TCP_PCB_NUM=4 +CONFIG_RT_LWIP_TCP_SEG_NUM=40 +CONFIG_RT_LWIP_TCP_SND_BUF=8196 +CONFIG_RT_LWIP_TCP_WND=8196 +CONFIG_RT_LWIP_TCPTHREAD_PRIORITY=10 +CONFIG_RT_LWIP_TCPTHREAD_MBOX_SIZE=8 +CONFIG_RT_LWIP_TCPTHREAD_STACKSIZE=8192 +# CONFIG_LWIP_NO_RX_THREAD is not set +# CONFIG_LWIP_NO_TX_THREAD is not set +CONFIG_RT_LWIP_ETHTHREAD_PRIORITY=12 +CONFIG_RT_LWIP_ETHTHREAD_STACKSIZE=8192 +CONFIG_RT_LWIP_ETHTHREAD_MBOX_SIZE=8 +# CONFIG_RT_LWIP_REASSEMBLY_FRAG is not set +CONFIG_LWIP_NETIF_STATUS_CALLBACK=1 +CONFIG_LWIP_NETIF_LINK_CALLBACK=1 +CONFIG_RT_LWIP_NETIF_NAMESIZE=6 +CONFIG_SO_REUSE=1 +CONFIG_LWIP_SO_RCVTIMEO=1 +CONFIG_LWIP_SO_SNDTIMEO=1 +CONFIG_LWIP_SO_RCVBUF=1 +CONFIG_LWIP_SO_LINGER=0 +# CONFIG_RT_LWIP_NETIF_LOOPBACK is not set +CONFIG_LWIP_NETIF_LOOPBACK=0 +# CONFIG_RT_LWIP_STATS is not set +# CONFIG_RT_LWIP_USING_HW_CHECKSUM is not set +CONFIG_RT_LWIP_USING_PING=y +# CONFIG_LWIP_USING_DHCPD is not set +# CONFIG_RT_LWIP_ENABLE_USER_HOOKS is not set +# CONFIG_RT_LWIP_DEBUG is not set +# CONFIG_RT_USING_AT is not set +# end of Network + +# +# Memory protection +# +# CONFIG_RT_USING_MEM_PROTECTION is not set +# CONFIG_RT_USING_HW_STACK_GUARD is not set +# end of Memory protection + +# +# Utilities +# +# CONFIG_RT_USING_RYM is not set +# CONFIG_RT_USING_ULOG is not set +CONFIG_RT_USING_UTEST=y +CONFIG_UTEST_THR_STACK_SIZE=4096 +CONFIG_UTEST_THR_PRIORITY=20 +# CONFIG_RT_UTEST_USING_AUTO_RUN is not set +CONFIG_RT_UTEST_MAX_OPTIONS=64 +# CONFIG_RT_USING_VAR_EXPORT is not set +CONFIG_RT_USING_RESOURCE_ID=y +CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y +CONFIG_RT_USING_ADT_BITMAP=y +CONFIG_RT_USING_ADT_HASHMAP=y +CONFIG_RT_USING_ADT_REF=y +# CONFIG_RT_USING_RT_LINK is not set +# end of Utilities + +# +# Memory management +# +# CONFIG_RT_PAGE_MPR_SIZE_DYNAMIC is not set +CONFIG_RT_PAGE_AFFINITY_BLOCK_SIZE=0x1000 +CONFIG_RT_PAGE_MAX_ORDER=11 +# CONFIG_RT_USING_MEMBLOCK is not set + +# +# Debugging +# +# CONFIG_RT_DEBUGGING_ALIASING is not set +# CONFIG_RT_DEBUGING_PAGE_LEAK is not set +# CONFIG_RT_DEBUGGING_PAGE_POISON is not set +# end of Debugging +# end of Memory management + +# +# Using USB legacy version +# +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set +# end of Using USB legacy version + +# CONFIG_RT_USING_FDT is not set +# CONFIG_RT_USING_RUST is not set +# end of RT-Thread Components + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set +# end of RT-Thread Utestcases + +# +# RT-Thread online packages +# + +# +# IoT - internet of things +# +# CONFIG_PKG_USING_LORAWAN_DRIVER is not set +# CONFIG_PKG_USING_PAHOMQTT is not set +# CONFIG_PKG_USING_UMQTT is not set +# CONFIG_PKG_USING_WEBCLIENT is not set +# CONFIG_PKG_USING_WEBNET is not set +# CONFIG_PKG_USING_MONGOOSE is not set +# CONFIG_PKG_USING_MYMQTT is not set +# CONFIG_PKG_USING_KAWAII_MQTT is not set +# CONFIG_PKG_USING_BC28_MQTT is not set +# CONFIG_PKG_USING_WEBTERMINAL is not set +# CONFIG_PKG_USING_FREEMODBUS is not set +# CONFIG_PKG_USING_NANOPB is not set +# CONFIG_PKG_USING_WIFI_HOST_DRIVER is not set +# CONFIG_PKG_USING_ESP_HOSTED is not set + +# +# Wi-Fi +# + +# +# Marvell WiFi +# +# CONFIG_PKG_USING_WLANMARVELL is not set +# end of Marvell WiFi + +# +# Wiced WiFi +# +# CONFIG_PKG_USING_WLAN_WICED is not set +# end of Wiced WiFi + +# CONFIG_PKG_USING_RW007 is not set + +# +# CYW43012 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43012 is not set +# end of CYW43012 WiFi + +# +# BL808 WiFi +# +# CONFIG_PKG_USING_WLAN_BL808 is not set +# end of BL808 WiFi + +# +# CYW43439 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43439 is not set +# end of CYW43439 WiFi +# end of Wi-Fi + +# CONFIG_PKG_USING_COAP is not set +# CONFIG_PKG_USING_NOPOLL is not set +# CONFIG_PKG_USING_NETUTILS is not set +# CONFIG_PKG_USING_CMUX is not set +# CONFIG_PKG_USING_PPP_DEVICE is not set +# CONFIG_PKG_USING_AT_DEVICE is not set +# CONFIG_PKG_USING_ATSRV_SOCKET is not set +# CONFIG_PKG_USING_WIZNET is not set +# CONFIG_PKG_USING_ZB_COORDINATOR is not set + +# +# IoT Cloud +# +# CONFIG_PKG_USING_ONENET is not set +# CONFIG_PKG_USING_GAGENT_CLOUD is not set +# CONFIG_PKG_USING_ALI_IOTKIT is not set +# CONFIG_PKG_USING_AZURE is not set +# CONFIG_PKG_USING_TENCENT_IOT_EXPLORER is not set +# CONFIG_PKG_USING_JIOT-C-SDK is not set +# CONFIG_PKG_USING_UCLOUD_IOT_SDK is not set +# CONFIG_PKG_USING_JOYLINK is not set +# CONFIG_PKG_USING_IOTSHARP_SDK is not set +# end of IoT Cloud + +# CONFIG_PKG_USING_NIMBLE is not set +# CONFIG_PKG_USING_LLSYNC_SDK_ADAPTER is not set +# CONFIG_PKG_USING_OTA_DOWNLOADER is not set +# CONFIG_PKG_USING_IPMSG is not set +# CONFIG_PKG_USING_LSSDP is not set +# CONFIG_PKG_USING_AIRKISS_OPEN is not set +# CONFIG_PKG_USING_LIBRWS is not set +# CONFIG_PKG_USING_TCPSERVER is not set +# CONFIG_PKG_USING_PROTOBUF_C is not set +# CONFIG_PKG_USING_DLT645 is not set +# CONFIG_PKG_USING_QXWZ is not set +# CONFIG_PKG_USING_SMTP_CLIENT is not set +# CONFIG_PKG_USING_ABUP_FOTA is not set +# CONFIG_PKG_USING_LIBCURL2RTT is not set +# CONFIG_PKG_USING_CAPNP is not set +# CONFIG_PKG_USING_AGILE_TELNET is not set +# CONFIG_PKG_USING_NMEALIB is not set +# CONFIG_PKG_USING_PDULIB is not set +# CONFIG_PKG_USING_BTSTACK is not set +# CONFIG_PKG_USING_BT_CYW43012 is not set +# CONFIG_PKG_USING_CYW43XX is not set +# CONFIG_PKG_USING_LORAWAN_ED_STACK is not set +# CONFIG_PKG_USING_WAYZ_IOTKIT is not set +# CONFIG_PKG_USING_MAVLINK is not set +# CONFIG_PKG_USING_BSAL is not set +# CONFIG_PKG_USING_AGILE_MODBUS is not set +# CONFIG_PKG_USING_AGILE_FTP is not set +# CONFIG_PKG_USING_EMBEDDEDPROTO is not set +# CONFIG_PKG_USING_RT_LINK_HW is not set +# CONFIG_PKG_USING_RYANMQTT is not set +# CONFIG_PKG_USING_RYANW5500 is not set +# CONFIG_PKG_USING_LORA_PKT_FWD is not set +# CONFIG_PKG_USING_LORA_GW_DRIVER_LIB is not set +# CONFIG_PKG_USING_LORA_PKT_SNIFFER is not set +# CONFIG_PKG_USING_HM is not set +# CONFIG_PKG_USING_SMALL_MODBUS is not set +# CONFIG_PKG_USING_NET_SERVER is not set +# CONFIG_PKG_USING_ZFTP is not set +# CONFIG_PKG_USING_WOL is not set +# CONFIG_PKG_USING_ZEPHYR_POLLING is not set +# CONFIG_PKG_USING_MATTER_ADAPTATION_LAYER is not set +# CONFIG_PKG_USING_LHC_MODBUS is not set +# CONFIG_PKG_USING_QMODBUS is not set +# CONFIG_PKG_USING_PNET is not set +# CONFIG_PKG_USING_OPENER is not set +# CONFIG_PKG_USING_FREEMQTT is not set +# end of IoT - internet of things + +# +# security packages +# +# CONFIG_PKG_USING_MBEDTLS is not set +# CONFIG_PKG_USING_LIBSODIUM is not set +# CONFIG_PKG_USING_LIBHYDROGEN is not set +# CONFIG_PKG_USING_TINYCRYPT is not set +# CONFIG_PKG_USING_TFM is not set +# CONFIG_PKG_USING_YD_CRYPTO is not set +# end of security packages + +# +# language packages +# + +# +# JSON: JavaScript Object Notation, a lightweight data-interchange format +# +# CONFIG_PKG_USING_CJSON is not set +# CONFIG_PKG_USING_LJSON is not set +# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set +# CONFIG_PKG_USING_RAPIDJSON is not set +# CONFIG_PKG_USING_JSMN is not set +# CONFIG_PKG_USING_AGILE_JSMN is not set +# CONFIG_PKG_USING_PARSON is not set +# CONFIG_PKG_USING_RYAN_JSON is not set +# end of JSON: JavaScript Object Notation, a lightweight data-interchange format + +# +# XML: Extensible Markup Language +# +# CONFIG_PKG_USING_SIMPLE_XML is not set +# CONFIG_PKG_USING_EZXML is not set +# end of XML: Extensible Markup Language + +# CONFIG_PKG_USING_LUATOS_SOC is not set +# CONFIG_PKG_USING_LUA is not set +# CONFIG_PKG_USING_JERRYSCRIPT is not set +# CONFIG_PKG_USING_MICROPYTHON is not set +# CONFIG_PKG_USING_PIKASCRIPT is not set +# CONFIG_PKG_USING_RTT_RUST is not set +# end of language packages + +# +# multimedia packages +# + +# +# LVGL: powerful and easy-to-use embedded GUI library +# +# CONFIG_PKG_USING_LVGL is not set +# CONFIG_PKG_USING_LV_MUSIC_DEMO is not set +# CONFIG_PKG_USING_GUI_GUIDER_DEMO is not set +# end of LVGL: powerful and easy-to-use embedded GUI library + +# +# u8g2: a monochrome graphic library +# +# CONFIG_PKG_USING_U8G2_OFFICIAL is not set +# CONFIG_PKG_USING_U8G2 is not set +# end of u8g2: a monochrome graphic library + +# CONFIG_PKG_USING_OPENMV is not set +# CONFIG_PKG_USING_MUPDF is not set +# CONFIG_PKG_USING_STEMWIN is not set +# CONFIG_PKG_USING_WAVPLAYER is not set +# CONFIG_PKG_USING_TJPGD is not set +# CONFIG_PKG_USING_PDFGEN is not set +# CONFIG_PKG_USING_HELIX is not set +# CONFIG_PKG_USING_AZUREGUIX is not set +# CONFIG_PKG_USING_TOUCHGFX2RTT is not set +# CONFIG_PKG_USING_NUEMWIN is not set +# CONFIG_PKG_USING_MP3PLAYER is not set +# CONFIG_PKG_USING_TINYJPEG is not set +# CONFIG_PKG_USING_UGUI is not set +# CONFIG_PKG_USING_MCURSES is not set +# CONFIG_PKG_USING_TERMBOX is not set +# CONFIG_PKG_USING_VT100 is not set +# CONFIG_PKG_USING_QRCODE is not set +# CONFIG_PKG_USING_GUIENGINE is not set +# CONFIG_PKG_USING_3GPP_AMRNB is not set +# end of multimedia packages + +# +# tools packages +# +# CONFIG_PKG_USING_CMBACKTRACE is not set +# CONFIG_PKG_USING_MCOREDUMP is not set +# CONFIG_PKG_USING_EASYFLASH is not set +# CONFIG_PKG_USING_EASYLOGGER is not set +# CONFIG_PKG_USING_SYSTEMVIEW is not set +# CONFIG_PKG_USING_SEGGER_RTT is not set +# CONFIG_PKG_USING_RTT_AUTO_EXE_CMD is not set +# CONFIG_PKG_USING_RDB is not set +# CONFIG_PKG_USING_ULOG_EASYFLASH is not set +# CONFIG_PKG_USING_LOGMGR is not set +# CONFIG_PKG_USING_ADBD is not set +# CONFIG_PKG_USING_COREMARK is not set +# CONFIG_PKG_USING_DHRYSTONE is not set +# CONFIG_PKG_USING_MEMORYPERF is not set +# CONFIG_PKG_USING_NR_MICRO_SHELL is not set +# CONFIG_PKG_USING_CHINESE_FONT_LIBRARY is not set +# CONFIG_PKG_USING_LUNAR_CALENDAR is not set +# CONFIG_PKG_USING_BS8116A is not set +# CONFIG_PKG_USING_GPS_RMC is not set +# CONFIG_PKG_USING_URLENCODE is not set +# CONFIG_PKG_USING_UMCN is not set +# CONFIG_PKG_USING_LWRB2RTT is not set +# CONFIG_PKG_USING_CPU_USAGE is not set +# CONFIG_PKG_USING_GBK2UTF8 is not set +# CONFIG_PKG_USING_VCONSOLE is not set +# CONFIG_PKG_USING_KDB is not set +# CONFIG_PKG_USING_WAMR is not set +# CONFIG_PKG_USING_MICRO_XRCE_DDS_CLIENT is not set +# CONFIG_PKG_USING_LWLOG is not set +# CONFIG_PKG_USING_ANV_TRACE is not set +# CONFIG_PKG_USING_ANV_MEMLEAK is not set +# CONFIG_PKG_USING_ANV_TESTSUIT is not set +# CONFIG_PKG_USING_ANV_BENCH is not set +# CONFIG_PKG_USING_DEVMEM is not set +# CONFIG_PKG_USING_REGEX is not set +# CONFIG_PKG_USING_MEM_SANDBOX is not set +# CONFIG_PKG_USING_SOLAR_TERMS is not set +# CONFIG_PKG_USING_GAN_ZHI is not set +# CONFIG_PKG_USING_FDT is not set +# CONFIG_PKG_USING_CBOX is not set +# CONFIG_PKG_USING_SNOWFLAKE is not set +# CONFIG_PKG_USING_HASH_MATCH is not set +# CONFIG_PKG_USING_ARMV7M_DWT_TOOL is not set +# CONFIG_PKG_USING_VOFA_PLUS is not set +# CONFIG_PKG_USING_ZDEBUG is not set +# CONFIG_PKG_USING_RVBACKTRACE is not set +# CONFIG_PKG_USING_HPATCHLITE is not set +# CONFIG_PKG_USING_THREAD_METRIC is not set +# end of tools packages + +# +# system packages +# + +# +# enhanced kernel services +# +# CONFIG_PKG_USING_RT_MEMCPY_CM is not set +# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set +# end of enhanced kernel services + +# CONFIG_PKG_USING_AUNITY is not set + +# +# acceleration: Assembly language or algorithmic acceleration packages +# +# CONFIG_PKG_USING_QFPLIB_M0_FULL is not set +# CONFIG_PKG_USING_QFPLIB_M0_TINY is not set +# CONFIG_PKG_USING_QFPLIB_M3 is not set +# end of acceleration: Assembly language or algorithmic acceleration packages + +# +# CMSIS: ARM Cortex-M Microcontroller Software Interface Standard +# +# CONFIG_PKG_USING_CMSIS_5 is not set +# CONFIG_PKG_USING_CMSIS_CORE is not set +# CONFIG_PKG_USING_CMSIS_NN is not set +# CONFIG_PKG_USING_CMSIS_RTOS1 is not set +# CONFIG_PKG_USING_CMSIS_RTOS2 is not set +# end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard + +# +# Micrium: Micrium software products porting for RT-Thread +# +# CONFIG_PKG_USING_UCOSIII_WRAPPER is not set +# CONFIG_PKG_USING_UCOSII_WRAPPER is not set +# CONFIG_PKG_USING_UC_CRC is not set +# CONFIG_PKG_USING_UC_CLK is not set +# CONFIG_PKG_USING_UC_COMMON is not set +# CONFIG_PKG_USING_UC_MODBUS is not set +# end of Micrium: Micrium software products porting for RT-Thread + +# CONFIG_PKG_USING_FREERTOS_WRAPPER is not set +# CONFIG_PKG_USING_LITEOS_SDK is not set +# CONFIG_PKG_USING_TZ_DATABASE is not set +# CONFIG_PKG_USING_CAIRO is not set +# CONFIG_PKG_USING_PIXMAN is not set +# CONFIG_PKG_USING_PARTITION is not set +# CONFIG_PKG_USING_PERF_COUNTER is not set +# CONFIG_PKG_USING_FILEX is not set +# CONFIG_PKG_USING_LEVELX is not set +# CONFIG_PKG_USING_FLASHDB is not set +# CONFIG_PKG_USING_SQLITE is not set +# CONFIG_PKG_USING_RTI is not set +# CONFIG_PKG_USING_DFS_YAFFS is not set +# CONFIG_PKG_USING_LITTLEFS is not set +# CONFIG_PKG_USING_DFS_JFFS2 is not set +# CONFIG_PKG_USING_DFS_UFFS is not set +# CONFIG_PKG_USING_LWEXT4 is not set +# CONFIG_PKG_USING_THREAD_POOL is not set +# CONFIG_PKG_USING_ROBOTS is not set +# CONFIG_PKG_USING_EV is not set +# CONFIG_PKG_USING_SYSWATCH is not set +# CONFIG_PKG_USING_SYS_LOAD_MONITOR is not set +# CONFIG_PKG_USING_PLCCORE is not set +# CONFIG_PKG_USING_RAMDISK is not set +# CONFIG_PKG_USING_MININI is not set +# CONFIG_PKG_USING_QBOOT is not set +# CONFIG_PKG_USING_PPOOL is not set +# CONFIG_PKG_USING_OPENAMP is not set +# CONFIG_PKG_USING_RPMSG_LITE is not set +# CONFIG_PKG_USING_LPM is not set +# CONFIG_PKG_USING_TLSF is not set +# CONFIG_PKG_USING_EVENT_RECORDER is not set +# CONFIG_PKG_USING_ARM_2D is not set +# CONFIG_PKG_USING_MCUBOOT is not set +# CONFIG_PKG_USING_TINYUSB is not set +# CONFIG_PKG_USING_KMULTI_RTIMER is not set +# CONFIG_PKG_USING_TFDB is not set +# CONFIG_PKG_USING_QPC is not set +# CONFIG_PKG_USING_AGILE_UPGRADE is not set +# CONFIG_PKG_USING_FLASH_BLOB is not set +# CONFIG_PKG_USING_MLIBC is not set +# CONFIG_PKG_USING_TASK_MSG_BUS is not set +# CONFIG_PKG_USING_UART_FRAMEWORK is not set +# CONFIG_PKG_USING_SFDB is not set +# CONFIG_PKG_USING_RTP is not set +# CONFIG_PKG_USING_REB is not set +# CONFIG_PKG_USING_RMP is not set +# CONFIG_PKG_USING_R_RHEALSTONE is not set +# CONFIG_PKG_USING_HEARTBEAT is not set +# CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set +# end of system packages + +# +# peripheral libraries and drivers +# + +# +# HAL & SDK Drivers +# + +# +# STM32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_STM32F0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB55_SDK is not set +# CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_STM32WL_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WL_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_CMSIS_DRIVER is not set +# end of STM32 HAL & SDK Drivers + +# +# Infineon HAL Packages +# +# CONFIG_PKG_USING_INFINEON_CAT1CM0P is not set +# CONFIG_PKG_USING_INFINEON_CMSIS is not set +# CONFIG_PKG_USING_INFINEON_CORE_LIB is not set +# CONFIG_PKG_USING_INFINEON_MTB_HAL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_MTB_PDL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_RETARGET_IO is not set +# CONFIG_PKG_USING_INFINEON_CAPSENSE is not set +# CONFIG_PKG_USING_INFINEON_CSDIDAC is not set +# CONFIG_PKG_USING_INFINEON_SERIAL_FLASH is not set +# CONFIG_PKG_USING_INFINEON_USBDEV is not set +# end of Infineon HAL Packages + +# CONFIG_PKG_USING_BLUETRUM_SDK is not set +# CONFIG_PKG_USING_EMBARC_BSP is not set +# CONFIG_PKG_USING_ESP_IDF is not set + +# +# Kendryte SDK +# +# CONFIG_PKG_USING_K210_SDK is not set +# CONFIG_PKG_USING_KENDRYTE_SDK is not set +# end of Kendryte SDK + +# CONFIG_PKG_USING_NRF5X_SDK is not set +# CONFIG_PKG_USING_NRFX is not set +# CONFIG_PKG_USING_NUCLEI_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_RP2350_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set +# CONFIG_PKG_USING_MM32 is not set + +# +# WCH HAL & SDK Drivers +# +# CONFIG_PKG_USING_CH32V20x_SDK is not set +# CONFIG_PKG_USING_CH32V307_SDK is not set +# end of WCH HAL & SDK Drivers + +# +# AT32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_AT32A403A_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A403A_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_CMSIS_DRIVER is not set +# end of AT32 HAL & SDK Drivers + +# +# HC32 DDL Drivers +# +# CONFIG_PKG_USING_HC32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F3_SERIES_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_SERIES_DRIVER is not set +# end of HC32 DDL Drivers + +# +# NXP HAL & SDK Drivers +# +# CONFIG_PKG_USING_NXP_MCX_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_NXP_MCX_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC55S_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6SX_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6UL_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMXRT_DRIVER is not set +# end of NXP HAL & SDK Drivers + +# +# NUVOTON Drivers +# +# CONFIG_PKG_USING_NUVOTON_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_NUVOTON_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NUVOTON_ARM926_LIB is not set +# end of NUVOTON Drivers + +# +# GD32 Drivers +# +# CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER is not set +# end of GD32 Drivers +# end of HAL & SDK Drivers + +# +# sensors drivers +# +# CONFIG_PKG_USING_LSM6DSM is not set +# CONFIG_PKG_USING_LSM6DSL is not set +# CONFIG_PKG_USING_LPS22HB is not set +# CONFIG_PKG_USING_HTS221 is not set +# CONFIG_PKG_USING_LSM303AGR is not set +# CONFIG_PKG_USING_BME280 is not set +# CONFIG_PKG_USING_BME680 is not set +# CONFIG_PKG_USING_BMA400 is not set +# CONFIG_PKG_USING_BMI160_BMX160 is not set +# CONFIG_PKG_USING_SPL0601 is not set +# CONFIG_PKG_USING_MS5805 is not set +# CONFIG_PKG_USING_DA270 is not set +# CONFIG_PKG_USING_DF220 is not set +# CONFIG_PKG_USING_HSHCAL001 is not set +# CONFIG_PKG_USING_BH1750 is not set +# CONFIG_PKG_USING_MPU6XXX is not set +# CONFIG_PKG_USING_AHT10 is not set +# CONFIG_PKG_USING_AP3216C is not set +# CONFIG_PKG_USING_TSL4531 is not set +# CONFIG_PKG_USING_DS18B20 is not set +# CONFIG_PKG_USING_DHT11 is not set +# CONFIG_PKG_USING_DHTXX is not set +# CONFIG_PKG_USING_GY271 is not set +# CONFIG_PKG_USING_GP2Y10 is not set +# CONFIG_PKG_USING_SGP30 is not set +# CONFIG_PKG_USING_HDC1000 is not set +# CONFIG_PKG_USING_BMP180 is not set +# CONFIG_PKG_USING_BMP280 is not set +# CONFIG_PKG_USING_SHTC1 is not set +# CONFIG_PKG_USING_BMI088 is not set +# CONFIG_PKG_USING_HMC5883 is not set +# CONFIG_PKG_USING_MAX6675 is not set +# CONFIG_PKG_USING_MAX31855 is not set +# CONFIG_PKG_USING_TMP1075 is not set +# CONFIG_PKG_USING_SR04 is not set +# CONFIG_PKG_USING_CCS811 is not set +# CONFIG_PKG_USING_PMSXX is not set +# CONFIG_PKG_USING_RT3020 is not set +# CONFIG_PKG_USING_MLX90632 is not set +# CONFIG_PKG_USING_MLX90382 is not set +# CONFIG_PKG_USING_MLX90393 is not set +# CONFIG_PKG_USING_MLX90392 is not set +# CONFIG_PKG_USING_MLX90394 is not set +# CONFIG_PKG_USING_MLX90397 is not set +# CONFIG_PKG_USING_MS5611 is not set +# CONFIG_PKG_USING_MAX31865 is not set +# CONFIG_PKG_USING_VL53L0X is not set +# CONFIG_PKG_USING_INA260 is not set +# CONFIG_PKG_USING_MAX30102 is not set +# CONFIG_PKG_USING_INA226 is not set +# CONFIG_PKG_USING_LIS2DH12 is not set +# CONFIG_PKG_USING_HS300X is not set +# CONFIG_PKG_USING_ZMOD4410 is not set +# CONFIG_PKG_USING_ISL29035 is not set +# CONFIG_PKG_USING_MMC3680KJ is not set +# CONFIG_PKG_USING_QMP6989 is not set +# CONFIG_PKG_USING_BALANCE is not set +# CONFIG_PKG_USING_SHT2X is not set +# CONFIG_PKG_USING_SHT3X is not set +# CONFIG_PKG_USING_SHT4X is not set +# CONFIG_PKG_USING_AD7746 is not set +# CONFIG_PKG_USING_ADT74XX is not set +# CONFIG_PKG_USING_MAX17048 is not set +# CONFIG_PKG_USING_AS7341 is not set +# CONFIG_PKG_USING_CW2015 is not set +# CONFIG_PKG_USING_ICM20608 is not set +# CONFIG_PKG_USING_PAJ7620 is not set +# CONFIG_PKG_USING_STHS34PF80 is not set +# CONFIG_PKG_USING_P3T1755 is not set +# CONFIG_PKG_USING_QMI8658 is not set +# CONFIG_PKG_USING_ICM20948 is not set +# end of sensors drivers + +# +# touch drivers +# +# CONFIG_PKG_USING_GT9147 is not set +# CONFIG_PKG_USING_GT1151 is not set +# CONFIG_PKG_USING_GT917S is not set +# CONFIG_PKG_USING_GT911 is not set +# CONFIG_PKG_USING_FT6206 is not set +# CONFIG_PKG_USING_FT5426 is not set +# CONFIG_PKG_USING_FT6236 is not set +# CONFIG_PKG_USING_XPT2046_TOUCH is not set +# CONFIG_PKG_USING_CST816X is not set +# CONFIG_PKG_USING_CST812T is not set +# end of touch drivers + +# CONFIG_PKG_USING_REALTEK_AMEBA is not set +# CONFIG_PKG_USING_BUTTON is not set +# CONFIG_PKG_USING_PCF8574 is not set +# CONFIG_PKG_USING_SX12XX is not set +# CONFIG_PKG_USING_SIGNAL_LED is not set +# CONFIG_PKG_USING_LEDBLINK is not set +# CONFIG_PKG_USING_LITTLED is not set +# CONFIG_PKG_USING_LKDGUI is not set +# CONFIG_PKG_USING_INFRARED is not set +# CONFIG_PKG_USING_MULTI_INFRARED is not set +# CONFIG_PKG_USING_AGILE_BUTTON is not set +# CONFIG_PKG_USING_AGILE_LED is not set +# CONFIG_PKG_USING_AT24CXX is not set +# CONFIG_PKG_USING_MOTIONDRIVER2RTT is not set +# CONFIG_PKG_USING_PCA9685 is not set +# CONFIG_PKG_USING_ILI9341 is not set +# CONFIG_PKG_USING_I2C_TOOLS is not set +# CONFIG_PKG_USING_NRF24L01 is not set +# CONFIG_PKG_USING_RPLIDAR is not set +# CONFIG_PKG_USING_AS608 is not set +# CONFIG_PKG_USING_RC522 is not set +# CONFIG_PKG_USING_WS2812B is not set +# CONFIG_PKG_USING_EXTERN_RTC_DRIVERS is not set +# CONFIG_PKG_USING_MULTI_RTIMER is not set +# CONFIG_PKG_USING_MAX7219 is not set +# CONFIG_PKG_USING_BEEP is not set +# CONFIG_PKG_USING_EASYBLINK is not set +# CONFIG_PKG_USING_PMS_SERIES is not set +# CONFIG_PKG_USING_CAN_YMODEM is not set +# CONFIG_PKG_USING_LORA_RADIO_DRIVER is not set +# CONFIG_PKG_USING_QLED is not set +# CONFIG_PKG_USING_AGILE_CONSOLE is not set +# CONFIG_PKG_USING_LD3320 is not set +# CONFIG_PKG_USING_WK2124 is not set +# CONFIG_PKG_USING_LY68L6400 is not set +# CONFIG_PKG_USING_DM9051 is not set +# CONFIG_PKG_USING_SSD1306 is not set +# CONFIG_PKG_USING_QKEY is not set +# CONFIG_PKG_USING_RS485 is not set +# CONFIG_PKG_USING_RS232 is not set +# CONFIG_PKG_USING_NES is not set +# CONFIG_PKG_USING_VIRTUAL_SENSOR is not set +# CONFIG_PKG_USING_VDEVICE is not set +# CONFIG_PKG_USING_SGM706 is not set +# CONFIG_PKG_USING_RDA58XX is not set +# CONFIG_PKG_USING_LIBNFC is not set +# CONFIG_PKG_USING_MFOC is not set +# CONFIG_PKG_USING_TMC51XX is not set +# CONFIG_PKG_USING_TCA9534 is not set +# CONFIG_PKG_USING_KOBUKI is not set +# CONFIG_PKG_USING_ROSSERIAL is not set +# CONFIG_PKG_USING_MICRO_ROS is not set +# CONFIG_PKG_USING_MCP23008 is not set +# CONFIG_PKG_USING_MISAKA_AT24CXX is not set +# CONFIG_PKG_USING_MISAKA_RGB_BLING is not set +# CONFIG_PKG_USING_LORA_MODEM_DRIVER is not set +# CONFIG_PKG_USING_SOFT_SERIAL is not set +# CONFIG_PKG_USING_MB85RS16 is not set +# CONFIG_PKG_USING_RFM300 is not set +# CONFIG_PKG_USING_IO_INPUT_FILTER is not set +# CONFIG_PKG_USING_LRF_NV7LIDAR is not set +# CONFIG_PKG_USING_AIP650 is not set +# CONFIG_PKG_USING_FINGERPRINT is not set +# CONFIG_PKG_USING_BT_ECB02C is not set +# CONFIG_PKG_USING_UAT is not set +# CONFIG_PKG_USING_ST7789 is not set +# CONFIG_PKG_USING_VS1003 is not set +# CONFIG_PKG_USING_X9555 is not set +# CONFIG_PKG_USING_SYSTEM_RUN_LED is not set +# CONFIG_PKG_USING_BT_MX01 is not set +# CONFIG_PKG_USING_RGPOWER is not set +# CONFIG_PKG_USING_BT_MX02 is not set +# CONFIG_PKG_USING_GC9A01 is not set +# CONFIG_PKG_USING_IK485 is not set +# CONFIG_PKG_USING_SERVO is not set +# CONFIG_PKG_USING_SEAN_WS2812B is not set +# CONFIG_PKG_USING_IC74HC165 is not set +# CONFIG_PKG_USING_IST8310 is not set +# CONFIG_PKG_USING_ST7789_SPI is not set +# CONFIG_PKG_USING_SPI_TOOLS is not set +# end of peripheral libraries and drivers + +# +# AI packages +# +# CONFIG_PKG_USING_LIBANN is not set +# CONFIG_PKG_USING_NNOM is not set +# CONFIG_PKG_USING_ONNX_BACKEND is not set +# CONFIG_PKG_USING_ONNX_PARSER is not set +# CONFIG_PKG_USING_TENSORFLOWLITEMICRO is not set +# CONFIG_PKG_USING_ELAPACK is not set +# CONFIG_PKG_USING_ULAPACK is not set +# CONFIG_PKG_USING_QUEST is not set +# CONFIG_PKG_USING_NAXOS is not set +# CONFIG_PKG_USING_R_TINYMAIX is not set +# CONFIG_PKG_USING_LLMCHAT is not set +# end of AI packages + +# +# Signal Processing and Control Algorithm Packages +# +# CONFIG_PKG_USING_APID is not set +# CONFIG_PKG_USING_FIRE_PID_CURVE is not set +# CONFIG_PKG_USING_QPID is not set +# CONFIG_PKG_USING_UKAL is not set +# CONFIG_PKG_USING_DIGITALCTRL is not set +# CONFIG_PKG_USING_KISSFFT is not set +# end of Signal Processing and Control Algorithm Packages + +# +# miscellaneous packages +# + +# +# project laboratory +# +# end of project laboratory + +# +# samples: kernel and components samples +# +# CONFIG_PKG_USING_KERNEL_SAMPLES is not set +# CONFIG_PKG_USING_FILESYSTEM_SAMPLES is not set +# CONFIG_PKG_USING_NETWORK_SAMPLES is not set +# CONFIG_PKG_USING_PERIPHERAL_SAMPLES is not set +# end of samples: kernel and components samples + +# +# entertainment: terminal games and other interesting software packages +# +# CONFIG_PKG_USING_CMATRIX is not set +# CONFIG_PKG_USING_SL is not set +# CONFIG_PKG_USING_CAL is not set +# CONFIG_PKG_USING_ACLOCK is not set +# CONFIG_PKG_USING_THREES is not set +# CONFIG_PKG_USING_2048 is not set +# CONFIG_PKG_USING_SNAKE is not set +# CONFIG_PKG_USING_TETRIS is not set +# CONFIG_PKG_USING_DONUT is not set +# CONFIG_PKG_USING_COWSAY is not set +# CONFIG_PKG_USING_MORSE is not set +# end of entertainment: terminal games and other interesting software packages + +# CONFIG_PKG_USING_LIBCSV is not set +# CONFIG_PKG_USING_OPTPARSE is not set +# CONFIG_PKG_USING_FASTLZ is not set +# CONFIG_PKG_USING_MINILZO is not set +# CONFIG_PKG_USING_QUICKLZ is not set +# CONFIG_PKG_USING_LZMA is not set +# CONFIG_PKG_USING_RALARAM is not set +# CONFIG_PKG_USING_MULTIBUTTON is not set +# CONFIG_PKG_USING_FLEXIBLE_BUTTON is not set +# CONFIG_PKG_USING_CANFESTIVAL is not set +# CONFIG_PKG_USING_ZLIB is not set +# CONFIG_PKG_USING_MINIZIP is not set +# CONFIG_PKG_USING_HEATSHRINK is not set +# CONFIG_PKG_USING_DSTR is not set +# CONFIG_PKG_USING_TINYFRAME is not set +# CONFIG_PKG_USING_KENDRYTE_DEMO is not set +# CONFIG_PKG_USING_UPACKER is not set +# CONFIG_PKG_USING_UPARAM is not set +# CONFIG_PKG_USING_HELLO is not set +# CONFIG_PKG_USING_VI is not set +# CONFIG_PKG_USING_KI is not set +# CONFIG_PKG_USING_ARMv7M_DWT is not set +# CONFIG_PKG_USING_CRCLIB is not set +# CONFIG_PKG_USING_LIBCRC is not set +# CONFIG_PKG_USING_LWGPS is not set +# CONFIG_PKG_USING_STATE_MACHINE is not set +# CONFIG_PKG_USING_DESIGN_PATTERN is not set +# CONFIG_PKG_USING_CONTROLLER is not set +# CONFIG_PKG_USING_PHASE_LOCKED_LOOP is not set +# CONFIG_PKG_USING_MFBD is not set +# CONFIG_PKG_USING_SLCAN2RTT is not set +# CONFIG_PKG_USING_SOEM is not set +# CONFIG_PKG_USING_QPARAM is not set +# CONFIG_PKG_USING_CorevMCU_CLI is not set +# CONFIG_PKG_USING_DRMP is not set +# end of miscellaneous packages + +# +# Arduino libraries +# +# CONFIG_PKG_USING_RTDUINO is not set + +# +# Projects and Demos +# +# CONFIG_PKG_USING_ARDUINO_MSGQ_C_CPP_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_SKETCH_LOADER_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_ULTRASOUND_RADAR is not set +# CONFIG_PKG_USING_ARDUINO_RTDUINO_SENSORFUSION_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_NINEINONE_SENSOR_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_SENSOR_KIT is not set +# CONFIG_PKG_USING_ARDUINO_MATLAB_SUPPORT is not set +# end of Projects and Demos + +# +# Sensors +# +# CONFIG_PKG_USING_ARDUINO_SENSOR_DEVICE_DRIVERS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSOR is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSORLAB is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL375 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L1X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL6180X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31855 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31865 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31856 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX6675 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90614 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS1 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AHTX0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADT7410 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME680 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9808 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4728 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA219 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR390 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DHT is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM6DS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO055 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX1704X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMC56X3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90393 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90395 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ICM20X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DPS310 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTS221 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT4X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL343 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS726X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AMG88XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2320 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2315 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR329_LTR303 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP3XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MS8607 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90640 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMA8451 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MSA301 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X_RVC is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS2MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303DLH_MAG is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LC709203F is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CAP1188 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CCS811 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_NAU7802 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS331 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS2X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS35HW is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303_ACCEL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3DH is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8591 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL3115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPR121 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPRLS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPU6050 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCT2075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PM25AQI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_EMC2101 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXAS21002C is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SCD30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXOS8700 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HMC5883_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP006 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TLA202X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCS34725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI7021 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP40 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHTC3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU21DF is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS7341 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU31D is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA260 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP007_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_L3GD20 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP117 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSC2007 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2591_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VCNL4040 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML7700 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LIS3DHTR is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DHT is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL335 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_H3LIS331DL is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MMA7660 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PAJ7620 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ITG3200 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HP20X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DRV2605L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BBM150 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HMC5883L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM303DLH is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TCS3414CS is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MP503 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HIGHTEMP is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT35 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_AT42QT1070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM6DS3 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HM3301 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LTC2941 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LDC1612 is not set +# CONFIG_PKG_USING_ARDUINO_CAPACITIVESENSOR is not set +# CONFIG_PKG_USING_ARDUINO_JARZEBSKI_MPU6050 is not set +# end of Sensors + +# +# Display +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_GFX_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_U8G2 is not set +# CONFIG_PKG_USING_ARDUINO_TFT_ESPI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ST7735 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SSD1306 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ILI9341 is not set +# CONFIG_PKG_USING_SEEED_TM1637 is not set +# end of Display + +# +# Timing +# +# CONFIG_PKG_USING_ARDUINO_RTCLIB is not set +# CONFIG_PKG_USING_ARDUINO_MSTIMER2 is not set +# CONFIG_PKG_USING_ARDUINO_TICKER is not set +# CONFIG_PKG_USING_ARDUINO_TASKSCHEDULER is not set +# end of Timing + +# +# Data Processing +# +# CONFIG_PKG_USING_ARDUINO_KALMANFILTER is not set +# CONFIG_PKG_USING_ARDUINO_ARDUINOJSON is not set +# CONFIG_PKG_USING_ARDUINO_TENSORFLOW_LITE_MICRO is not set +# CONFIG_PKG_USING_ARDUINO_RUNNINGMEDIAN is not set +# end of Data Processing + +# +# Data Storage +# + +# +# Communication +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PN532 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI4713 is not set +# end of Communication + +# +# Device Control +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8574 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCA9685 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TPA2016 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DRV2605 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS1841 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS3502 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PCF85063TP is not set +# end of Device Control + +# +# Other +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MFRC630 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI5351 is not set +# end of Other + +# +# Signal IO +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BUSIO is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCA8418 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP23017 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADS1X15 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AW9523 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP3008 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BD3491FS is not set +# end of Signal IO + +# +# Uncategorized +# +# end of Arduino libraries +# end of RT-Thread online packages + +# +# XiangShan configs +# +# CONFIG_BSP_USING_VIRTIO is not set +# end of XiangShan configs + +CONFIG_BOARD_XIANGSHAN=y +# CONFIG_ENABLE_FPU is not set +# CONFIG_ENABLE_VECTOR is not set +# CONFIG_RT_USING_USERSPACE_32BIT_LIMIT is not set +CONFIG_PLIC_BASE=0x3c000000 +CONFIG___STACKSIZE__=16384 diff --git a/bsp/xiangshan/.gitignore b/bsp/xiangshan/.gitignore new file mode 100644 index 00000000000..341f703a73f --- /dev/null +++ b/bsp/xiangshan/.gitignore @@ -0,0 +1,3 @@ +mnt.c +romfs_data.c +opensbi diff --git a/bsp/xiangshan/Kconfig b/bsp/xiangshan/Kconfig new file mode 100644 index 00000000000..b68f17f9c83 --- /dev/null +++ b/bsp/xiangshan/Kconfig @@ -0,0 +1,61 @@ +mainmenu "RT-Thread Project Configuration" + +BSP_DIR := . + +RTT_DIR := ../../ + +PKGS_DIR := packages + +source "$(RTT_DIR)/Kconfig" +osource "$PKGS_DIR/Kconfig" +rsource "driver/Kconfig" + +config BOARD_XIANGSHAN + bool + select ARCH_RISCV64 + select ARCH_USING_RISCV_COMMON64 + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + select RT_USING_CACHE + select ARCH_MM_MMU + select ARCH_REMAP_KERNEL + default y + +config ENABLE_FPU + bool "Enable FPU" + select ARCH_RISCV_FPU + default n + +config ENABLE_VECTOR + bool "Using RISC-V Vector Extension" + select ARCH_RISCV_VECTOR + default n + +if ENABLE_VECTOR + choice + prompt "Vector Registers Length in Bits" + default ARCH_VECTOR_VLEN_128 + + config ARCH_VECTOR_VLEN_128 + bool "128" + + config ARCH_VECTOR_VLEN_256 + bool "256" + endchoice +endif + +config RT_USING_USERSPACE_32BIT_LIMIT + bool "Enable userspace 32bit limit" + default n + +config RT_USING_VIRTIO_MMIO_ALIGN + bool "Open packed attribution, this may caused an error on virtio" + default n + +config PLIC_BASE + hex "PLIC base address" + default 0x3c000000 + +config __STACKSIZE__ + int "stack size for interrupt" + default 4096 diff --git a/bsp/xiangshan/README.md b/bsp/xiangshan/README.md new file mode 100644 index 00000000000..5a41a1f6080 --- /dev/null +++ b/bsp/xiangshan/README.md @@ -0,0 +1,407 @@ +**XiangShan/RISCV64 Board Support Package User Guide** + +English | [中文](./README_cn.md) + + + +- [1. Introduction](#1-introduction) +- [2. Building](#2-building) + - [2.1. Installing the toolchain](#21-installing-the-toolchain) + - [2.2. Setting RT-Thread toolchain environment variables](#22-setting-rt-thread-toolchain-environment-variables) + - [2.3. Downloading the kernel](#23-downloading-the-kernel) + - [2.4. Configuring the kernel](#24-configuring-the-kernel) + - [2.5. Compiling the kernel](#25-compiling-the-kernel) +- [3. Running](#3-running) + - [3.1. Installing QEMU](#31-installing-qemu) + - [3.2. Running QEMU](#32-running-qemu) + - [3.2.1. Running RT-Thread Standard Edition](#321-running-rt-thread-standard-edition) + - [3.2.2. Running RT-Thread Smart version](#322-running-rt-thread-smart-version) + - [3.2.3. Running RT-Thread Smart version + Root file-system](#323-running-rt-thread-smart-version--root-file-system) +- [4. How to use rv64ilp32 toolchain](#4-how-to-use-rv64ilp32-toolchain) +- [5. Contact information](#5-contact-information) + + + +# 1. Introduction + +RISC-V is an open and free instruction set architecture (ISA). This BSP targets the XiangShan environment and is derived from the RISCV64 VIRT BSP. + +> Note: The QEMU-specific build/run steps below are inherited from the original BSP and may not apply to XiangShan. Please adapt them to your XiangShan runtime environment. + +This project supports the world's first rv64ilp32 product-level open source toolchain jointly launched by the Xuantie team and the Institute of Software of the Chinese Academy of Sciences. + +# 2. Building + +Working system: take Ubuntu 22.04 as an example: + +```shell +$ lsb_release -a +No LSB modules are available. +Distributor ID: Ubuntu +Description: Ubuntu 22.04.2 LTS +Release: 22.04 +Codename: jammy +``` + +## 2.1. Installing the toolchain + +The specific toolchain used is consistent with the official RT-Thread. For the specific toolchain version, please refer to the file in the RT-Thread repository. + +```yaml + - name: Install RISC-V ToolChains + if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST != 'rtsmart/riscv64' && success() }} + run: | + wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.4/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz + sudo tar zxvf riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz -C /opt + /opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gcc --version + echo "RTT_EXEC_PATH=/opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin" >> $GITHUB_ENV + + - name: Install RISC-V Musl ToolChains + if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST == 'rtsmart/riscv64' && success() }} + shell: bash + run: | + wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.7/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 + sudo tar xjf riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 -C /opt + /opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin/riscv64-unknown-linux-musl-gcc --version + echo "RTT_EXEC_PATH=/opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin" >> $GITHUB_ENV + echo "RTT_CC_PREFIX=riscv64-unknown-linux-musl-" >> $GITHUB_ENV +``` + +Among them, `riscv64-unknown-elf-gcc` is used to build the RT-Thread Standard version, and `riscv64-unknown-linux-musl-gcc` is used to build the RT-Thread Smart version. Download them to your local computer according to the links shown above and decompress them. + +## 2.2. Setting RT-Thread toolchain environment variables + +There are three environment variables related to the RT-Thread toolchain + +- `RTT_CC` is the toolchain name, which is `"gcc"` here +- `RTT_CC_PREFIX`: is the toolchain prefix, which is `"riscv64-unknown-elf-"` for the Standard version and `"riscv64-unknown-linux-musl-"` for the Smart version. +- `RTT_EXEC_PATH`: the path where the bin folder of the toolchain is located, such as `"$HOME/tools/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin"`. This is set according to the actual path after personal download and decompression. Note that the toolchains of the RT-Thread standard version and the Smart version are two different versions, and the path name of `RTT_EXEC_PATH` must be set to `bin`. + +If you use them all the time, it is recommended to export these three environment variables in the `.bashrc` file. + +## 2.3. Downloading the kernel + +Assume that the working path is `$WORKSPACE`. + +```shell +$ cd $WORKSPACE +$ git clone git@github.com:RT-Thread/rt-thread.git +``` + +Enter the BSP directory where xiangshan is located. The following operations will not be introduced separately. By default, it is in this directory. + +```shell +$ cd $WORKSPACE/rt-thread/bsp/xiangshan +``` + +## 2.4. Configuring the kernel + +Refresh the configuration file before compiling for the first time. + +```shell +$ scons --menuconfig +``` + +The default configuration is the RT-Thread standard version, so if you don't have any special requirements, don't change anything, just save and exit. + +If you want to use the RT-Thread Smart version, at least turn on the `RT_USING_SMART` option after entering the configuration menu (see the figure below), and the rest depends on your needs. + +``` +(Top) → RT-Thread Kernel +RT-Thread Project Configuration +(24) The maximal size of kernel object name +[ ] Use the data types defined in ARCH_CPU +[*] Enable RT-Thread Smart (microkernel on kernel/userland) +[ ] Enable RT-Thread Nano +... +``` + +Save and exit after modification. + +## 2.5. Compiling the kernel + +If you have compiled before, you can clean it up: + +```shell +$ scons --clean +``` + +Or compile directly: + +```shell +$ scons -j$(nproc) +``` + +The kernel binary file `rtthread.bin` will be generated in the `$WORKSPACE/rt-thread/bsp/xiangshan`. + +# 3. Running + +## 3.1. Installing QEMU + +```shell +$ sudo apt update +$ sudo apt install qemu-system-misc +``` + +After the installation is complete, you can check the version. + +```shell +$ qemu-system-riscv64 --version +QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.24) +Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers +``` + +## 3.2. Running QEMU + +The repository has provided a ready-made execution script, which can be executed directly: + +```shell +$ ./run.sh +``` + +### 3.2.1. Running RT-Thread Standard Edition + +The following is an example: + +```shell +$ ./run.sh + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x8028d8a8 - 0x8428d8a8] + + \ | / +- RT - Thread Operating System + / | \ 5.2.0 build Nov 14 2024 15:41:57 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +file system initialization done! +Hello RISC-V +msh /> +``` +### 3.2.2. Running RT-Thread Smart version + +The following is an example: + +```shell +$ ./run.sh + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x002ef030 - 0x042ef030] + + \ | / +- RT - Thread Smart Operating System + / | \ 5.2.0 build Nov 14 2024 15:48:43 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +[I/drivers.serial] Using /dev/ttyS0 as default console +file system initialization done! +Hello RISC-V +msh /> +``` + +### 3.2.3. Running RT-Thread Smart version + Root file-system + +For the Smart version of the kernel, you can also specify the path of the root file-system image file when executing the `run.sh` script to mount the root file-system during the startup process. + +It should be noted that the kernel supports fat by default. If you want to mount the ext4 file-system, you need to install the lwext4 package additionally, i.e. to enable the `PKG_USING_LWEXT4` option (the specific menuconfig path is (Top) -> RT-Thread online packages -> system packages -> lwext4: an excellent choice of ext2/3/4 filesystem for microcontrollers.). If you can't find the item in the menu, you can exit menuconfig and execute `pkgs --upgrade` to update the package index and then try to enable the package. + +After checking this option, you also need to perform the following operations to update the software and install the source code to the packages directory of bsp (this operation only needs to be performed once): + +```shell +$ source ~/.env/env.sh +$ pkgs --update +``` + +Save and recompile the kernel. + +For how to make a root file-system, please refer to , which will not be repeated here. + +The example is as follows: + +```shell +$ ./run.sh /home/u/ws/duo/userapps/apps/build/ext4.img + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x00326438 - 0x04326438] + + \ | / +- RT - Thread Smart Operating System + / | \ 5.2.0 build Dec 17 2024 11:49:39 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +[I/drivers.serial] Using /dev/ttyS0 as default console +[W/DFS.fs] mount / failed with file system type: elm +file system initialization done! +Hello RISC-V +msh />[E/sal.skt] not find network interface device by protocol family(1). +[E/sal.skt] SAL socket protocol family input failed, return error -3. +/ # ls +bin lib proc sbin tmp +dev lost+found root services usr +etc mnt run tc var +/ # +``` + +# 4. How to use rv64ilp32 toolchain + +- Toolchain address: + +- Usage: + + - Configure toolchain path + + - Modify ABI parameter to: `-mabi=ilp32d` + + - Then perform regular compilation + + - Use [script](./qemu-rv64ilp32-nographic.sh) to start QEMU (INFO: QEMU binary is also in the toolchain directory) + +- Compare the firmware size of the same project compiled using the traditional 64-bit toolchain and the new 32-bit toolchain: + + Traditional 64-bit toolchain firmware size: + + ```bash + Memory region Used Size Region Size %age Used + SRAM: 225856 B 16 MB 1.35% + riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin + riscv64-unknown-elf-size rtthread.elf + text data bss dec hex filename + 150907 3664 71268 225839 3722f rtthread.elf + ``` + + New 32-bit toolchain firmware size: + + ```bash + Memory region Used Size Region Size %age Used + SRAM: 209376 B 16 MB 1.25% + riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin + riscv64-unknown-elf-size rtthread.elf + text data bss dec hex filename + 138739 1356 69276 209371 331db rtthread.elf + ``` + +# 5. Contact information + +Maintainer: [bernard][1] + +[1]: https://github.com/BernardXiong \ No newline at end of file diff --git a/bsp/xiangshan/README_cn.md b/bsp/xiangshan/README_cn.md new file mode 100644 index 00000000000..5990ab316dc --- /dev/null +++ b/bsp/xiangshan/README_cn.md @@ -0,0 +1,411 @@ +**XiangShan/RISCV64 板级支持包使用说明** + +中文页 | [English](./README.md) + + + +- [1. 简介](#1-简介) +- [2. 构建](#2-构建) + - [2.1. 安装工具链](#21-安装工具链) + - [2.2. 设置 RT-Thread 工具链环境变量](#22-设置-rt-thread-工具链环境变量) + - [2.3. 下载内核](#23-下载内核) + - [2.4. 配置内核](#24-配置内核) + - [2.5. 编译内核](#25-编译内核) +- [3. 运行](#3-运行) + - [3.1. 安装 QEMU](#31-安装-qemu) + - [3.2. 运行 QEMU](#32-运行-qemu) + - [3.2.1. 运行 RT-Thread 标准版](#321-运行-rt-thread-标准版) + - [3.2.2. 运行 RT-Thread Smart 版](#322-运行-rt-thread-smart-版) + - [3.2.3. 运行 RT-Thread Smart 版 + 根文件系统](#323-运行-rt-thread-smart-版--根文件系统) +- [4. 如何使用 rv64ilp32 工具链](#4-如何使用-rv64ilp32-工具链) +- [5. 联系人信息](#5-联系人信息) + + + +# 1. 简介 + +RISC-V 是一种开放和免费的指令集体系结构 (ISA)。本 BSP 面向 XiangShan 环境,派生自 RISCV64 VIRT BSP。 + +> 说明:下方的 QEMU 构建/运行步骤继承自原 BSP,可能不适用于 XiangShan,请根据 XiangShan 运行环境自行调整。 + +本工程支持玄铁团队联合中科院软件所共同推出的全球首款 rv64ilp32 产品级开源工具链。 + +# 2. 构建 + +工作系统:以 Ubuntu 22.04 为例: + +```shell +$ lsb_release -a +No LSB modules are available. +Distributor ID: Ubuntu +Description: Ubuntu 22.04.2 LTS +Release: 22.04 +Codename: jammy +``` + +## 2.1. 安装工具链 + +具体使用的工具链,和 RT-Thread 官方保持一致,具体的工具链版本可以参考 RT-Thread 仓库的 这个文件。 + +```yaml + - name: Install RISC-V ToolChains + if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST != 'rtsmart/riscv64' && success() }} + run: | + wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.4/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz + sudo tar zxvf riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz -C /opt + /opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gcc --version + echo "RTT_EXEC_PATH=/opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin" >> $GITHUB_ENV + + - name: Install RISC-V Musl ToolChains + if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST == 'rtsmart/riscv64' && success() }} + shell: bash + run: | + wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.7/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 + sudo tar xjf riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 -C /opt + /opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin/riscv64-unknown-linux-musl-gcc --version + echo "RTT_EXEC_PATH=/opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin" >> $GITHUB_ENV + echo "RTT_CC_PREFIX=riscv64-unknown-linux-musl-" >> $GITHUB_ENV +``` + +其中 `riscv64-unknown-elf-gcc` 用于构建 RT-Thread 标准版,`riscv64-unknown-linux-musl-gcc` 用于构建 RT-Thread Smart 版。根据上面所示链接分别下载到本地后解压缩。 + +## 2.2. 设置 RT-Thread 工具链环境变量 + +和 RT-Thread 工具链相关的环境变量有三个 + +- `RTT_CC` 为工具链名称, 这里统一为 `"gcc"` +- `RTT_CC_PREFIX`: 为工具链前缀, 这里对于标准版是 `"riscv64-unknown-elf-"`,对于 Smart 版是 `"riscv64-unknown-linux-musl-"`。 +- `RTT_EXEC_PATH`: 工具链的 bin 文件夹所在路径, 如 `"$HOME/tools/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin"`, 这个根据个人下载解压后的实际路径进行设置,注意 RT-Thread 标准版和 Smart 版本的工具链是两套不同的版本,而且设置 `RTT_EXEC_PATH` 的路径名时要一直到 `bin`。 + +如果一直使用的话,建议将这三个环境变量在 `.bashrc` 文件中 export。 + +## 2.3. 下载内核 + +假设工作路径是 `$WORKSPACE`。 + +```shell +$ cd $WORKSPACE +$ git clone git@github.com:RT-Thread/rt-thread.git +``` + +进入 xiangshan 所在 BSP 目录,后面的操作不做另外介绍,默认就在这个目录下。 + +```shell +$ cd $WORKSPACE/rt-thread/bsp/xiangshan +``` + +## 2.4. 配置内核 + +第一次编译前先刷新一下配置文件。 + +```shell +$ scons --menuconfig +``` + +默认配置就是 RT-Thread 标准版,所以如果没有什么特别需求,什么都不要改动,直接保存退出即可。 + +如果要使用 RT-Thread Smart 版,进入配置菜单后至少要打开 `RT_USING_SMART` 这个选项(见下图),其他的看自己的需求。 + +``` +(Top) → RT-Thread Kernel + RT-Thread Project Configuration +(24) The maximal size of kernel object name +[ ] Use the data types defined in ARCH_CPU +[*] Enable RT-Thread Smart (microkernel on kernel/userland) +[ ] Enable RT-Thread Nano +... +``` + +修改后保存退出。 + +## 2.5. 编译内核 + +如果以前编译后,可以清理一下: + +```shell +$ scons --clean +``` + +或者直接编译: + +```shell +$ scons -j$(nproc) +``` + +在 `$WORKSPACE/rt-thread/bsp/xiangshan` 路径下会生成内核的二进制文件 `rtthread.bin`。 + +# 3. 运行 + +## 3.1. 安装 QEMU + +```shell +$ sudo apt update +$ sudo apt install qemu-system-misc +``` + +安装完毕后可以看一下版本。 + +```shell +$ qemu-system-riscv64 --version +QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.24) +Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers +``` + +## 3.2. 运行 QEMU + +仓库里已经提供了现成的执行脚本,可以直接执行: + +```shell +$ ./run.sh +``` + +### 3.2.1. 运行 RT-Thread 标准版 + +示例如下: + +```shell +$ ./run.sh + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x8028d8a8 - 0x8428d8a8] + + \ | / +- RT - Thread Operating System + / | \ 5.2.0 build Nov 14 2024 15:41:57 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +file system initialization done! +Hello RISC-V +msh /> +``` + +### 3.2.2. 运行 RT-Thread Smart 版 + +示例如下: + +```shell +$ ./run.sh + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x002ef030 - 0x042ef030] + + \ | / +- RT - Thread Smart Operating System + / | \ 5.2.0 build Nov 14 2024 15:48:43 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +[I/drivers.serial] Using /dev/ttyS0 as default console +file system initialization done! +Hello RISC-V +msh /> +``` + +### 3.2.3. 运行 RT-Thread Smart 版 + 根文件系统 + +对于 Smart 版本的内核,也可以在执行 `run.sh` 脚本时指定根文件系统镜像文件的路径在启动过程中挂载根文件系统。 + +需要注意的是,内核默认支持 fat, 如果要挂载 ext4 的文件系统,则还需要额外安装 lwext4 软件包,即使能 `PKG_USING_LWEXT4`(具体 menuconfig 路径是 (Top) -> RT-Thread online packages -> system packages -> lwext4: an excellent choice of ext2/3/4 filesystem for microcontrollers.)。如果在菜单中找不到该软件包,可以退出 menuconfig 并执行 `pkgs --upgrade` 更新软件包索引后再尝试使能软件包。 + +勾选该选项后还需要执行如下操作更新软件并安装源码到 bsp 的 packages 目录下(该操作只要执行一次即可): + +```shell +$ source ~/.env/env.sh +$ pkgs --update +``` + +保存后重新编译内核。 + +有关如何制作根文件系统,请参考 ,这里不再赘述。 + +示例如下: + +```shell +$ ./run.sh /home/u/ws/duo/userapps/apps/build/ext4.img + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x00326438 - 0x04326438] + + \ | / +- RT - Thread Smart Operating System + / | \ 5.2.0 build Dec 17 2024 11:49:39 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +[I/drivers.serial] Using /dev/ttyS0 as default console +[W/DFS.fs] mount / failed with file system type: elm +file system initialization done! +Hello RISC-V +msh />[E/sal.skt] not find network interface device by protocol family(1). +[E/sal.skt] SAL socket protocol family input failed, return error -3. +/ # ls +bin lib proc sbin tmp +dev lost+found root services usr +etc mnt run tc var +/ # +``` + +# 4. 如何使用 rv64ilp32 工具链 + +- 工具链地址:https://github.com/ruyisdk/riscv-gnu-toolchain-rv64ilp32/tags + +- 使用方法: + + - 配置工具链路径 + + - 修改ABI参数为:`-mabi=ilp32d` + + - 然后执行常规编译 + + - 使用 [脚本](./qemu-rv64ilp32-nographic.sh) 启动 QEMU (INFO: QEMU 二进制同样在工具链目录) + +- 使用传统 64 位工具链与使用新 32 位工具链编译相同工程的固件大小对比: + + 传统 64 位工具链固件大小: + + ```bash + Memory region Used Size Region Size %age Used + SRAM: 225856 B 16 MB 1.35% + riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin + riscv64-unknown-elf-size rtthread.elf + text data bss dec hex filename + 150907 3664 71268 225839 3722f rtthread.elf + ``` + + 新 32 位工具链固件大小: + + ```bash + Memory region Used Size Region Size %age Used + SRAM: 209376 B 16 MB 1.25% + riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin + riscv64-unknown-elf-size rtthread.elf + text data bss dec hex filename + 138739 1356 69276 209371 331db rtthread.elf + ``` + +# 5. 联系人信息 + +维护人:[bernard][1] + +[1]: https://github.com/BernardXiong + + + diff --git a/bsp/xiangshan/SConscript b/bsp/xiangshan/SConscript new file mode 100644 index 00000000000..c7ef7659ece --- /dev/null +++ b/bsp/xiangshan/SConscript @@ -0,0 +1,14 @@ +# for module compiling +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/bsp/xiangshan/SConstruct b/bsp/xiangshan/SConstruct new file mode 100644 index 00000000000..ae0e3375e11 --- /dev/null +++ b/bsp/xiangshan/SConstruct @@ -0,0 +1,62 @@ +import os +import sys +import rtconfig + +from rtconfig import RTT_ROOT + +sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')] +from building import * + +TARGET = 'rtthread.' + rtconfig.TARGET_EXT + +DefaultEnvironment(tools=[]) +env = Environment(tools = ['mingw'], + AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, + CC = rtconfig.CC, CCFLAGS = rtconfig.CFLAGS, + CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS, + AR = rtconfig.AR, ARFLAGS = '-rc', + LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS) +env.PrependENVPath('PATH', rtconfig.EXEC_PATH) +env['ASCOM'] = env['ASPPCOM'] + +Export('RTT_ROOT') +Export('rtconfig') +rtconfig.CPU='virt64' +rtconfig.ARCH='risc-v' + +# prepare building environment +objs = PrepareBuilding(env, RTT_ROOT, has_libcpu = False) + +stack_size = 4096 + +if GetDepend('RT_USING_SMART'): + # use smart link.lds + env['LINKFLAGS'] = env['LINKFLAGS'].replace('link.lds', 'link_smart.lds') + +stack_lds = open('link_stacksize.lds', 'w') +if GetDepend('__STACKSIZE__'): stack_size = GetDepend('__STACKSIZE__') +stack_lds.write('__STACKSIZE__ = %d;\n' % stack_size) +stack_lds.close() + +# Obtain the number of harts from rtconfig.h and write +# it into link_cpus.lds for the linker script +try: + with open('rtconfig.h', 'r') as f: + rtconfig_content = f.readlines() +except FileNotFoundError: + cpus_nr = 1 +else: + cpus_nr = 1 # default value + for line in rtconfig_content: + line = line.strip() + if line.startswith('#define') and 'RT_CPUS_NR' in line: + parts = line.split() + if len(parts) >= 3 and parts[2].isdigit(): + cpus_nr = int(parts[2]) + break + +with open('link_cpus.lds', 'w') as cpus_lds: + cpus_lds.write(f'RT_CPUS_NR = {cpus_nr};\n') + +# make a building +DoBuilding(TARGET, objs) diff --git a/bsp/xiangshan/applications/SConscript b/bsp/xiangshan/applications/SConscript new file mode 100644 index 00000000000..7e4f7d3ac37 --- /dev/null +++ b/bsp/xiangshan/applications/SConscript @@ -0,0 +1,17 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH) + +objs = [group] + +list = os.listdir(cwd) + +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/bsp/xiangshan/applications/main.c b/bsp/xiangshan/applications/main.c new file mode 100644 index 00000000000..b55df4da2b1 --- /dev/null +++ b/bsp/xiangshan/applications/main.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include + +int main(void) +{ + rt_kprintf("Hello RISC-V\n"); + + return 0; +} diff --git a/bsp/xiangshan/applications/test/SConscript b/bsp/xiangshan/applications/test/SConscript new file mode 100644 index 00000000000..2597249cbd9 --- /dev/null +++ b/bsp/xiangshan/applications/test/SConscript @@ -0,0 +1,17 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('Testcase', src, depend = [''], CPPPATH = CPPPATH) + +list = os.listdir(cwd) + +objs = [group] + +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/bsp/xiangshan/applications/test/test_vector/SConscript b/bsp/xiangshan/applications/test/test_vector/SConscript new file mode 100644 index 00000000000..0827b048b28 --- /dev/null +++ b/bsp/xiangshan/applications/test/test_vector/SConscript @@ -0,0 +1,9 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('Vector', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/xiangshan/applications/test/test_vector/test_vector.c b/bsp/xiangshan/applications/test/test_vector/test_vector.c new file mode 100644 index 00000000000..50aae802480 --- /dev/null +++ b/bsp/xiangshan/applications/test/test_vector/test_vector.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#include +#include +#include +#include + +#if defined(RT_USING_UTEST) && defined(ENABLE_VECTOR) +#include +#include + +void rt_hw_vector_ctx_restore(void *buf); +void rt_hw_vector_ctx_save(void *buf); + +/** + * ============================================================== + * TEST FEATURE + * Use libc `memcpy` which employing V extension codes + * to test V extension features + * ============================================================== + */ +static char *constant = "hello,it's a nice day and i'm happy to see you\n"; +#define ARR_SIZE 4096 +static char array[ARR_SIZE]; + +static void test_feature(void) +{ + memcpy(array, constant, sizeof array); + char *src = constant; + char *dst = array; + int error = 0; + + for (size_t i = 0; i < ARR_SIZE; i++) + { + if (src[i] != dst[i]) + { + error = 1; + break; + } + } + + uassert_false(error); +} + +/** + * ============================================================== + * TEST CONTEXT SAVING + * Create 2 threads employing V extension, verify V states are + * not modified by each other + * ============================================================== + */ +#define TEST_THREAD 2 +#define VECTOR_CTX_BYTES (CTX_VECTOR_REG_NR * REGBYTES) +void *ctx_vector[TEST_THREAD * 2]; + +static rt_sem_t sem; + +void dump_frame(void *frame) +{ + uint64_t *content = frame; + for (size_t i = 0; i < VECTOR_CTX_BYTES / 8; i++) + { + rt_kprintf("%x ", content[i]); + } + rt_kprintf("\n"); +} + +static void vector_child(void *param) +{ + void **ctx = param; + uint64_t *reg = ctx[0]; + uint64_t vtype; + uint64_t vl; + + rt_sem_release(sem); + + rt_hw_vector_ctx_restore(ctx[0]); + + /* STAGE 2, save t2 context */ + test_feature(); + + /** + * @brief vtype & vl will be modified after context saving, + * it's ok because it will be recover after context restoring + * We restore these states manually here. + */ + asm volatile("csrr %0, vtype":"=r"(vtype)); + asm volatile("csrr %0, vl":"=r"(vl)); + rt_hw_vector_ctx_save(ctx[0]); + + rt_memcpy(ctx[1], ctx[0], VECTOR_CTX_BYTES); + + rt_thread_yield(); + + asm volatile("vsetvl x0, %0, %1"::"r"(vl), "r"(vtype)); + rt_hw_vector_ctx_save(ctx[0]); + + uassert_false(rt_memcmp(ctx[1], ctx[0], VECTOR_CTX_BYTES)); +} + +/** + * @brief Test if context save/restore codes work properly + */ +static void test_context() +{ + rt_thread_t child; + uint64_t vtype; + uint64_t vl; + + for (size_t i = 0; i < TEST_THREAD; i++) + { + ctx_vector[i * 2] = calloc(VECTOR_CTX_BYTES, 1); + ctx_vector[i * 2 + 1] = calloc(VECTOR_CTX_BYTES, 1); + } + rt_hw_vector_ctx_restore(ctx_vector[0]); + + child = rt_thread_create("test_vector_child", vector_child, &ctx_vector[2], 4096, 10, 20); + + /* STAGE 1, save t1 context */ + /* assuming that rt libc memcpy do not use vector instruction */ + asm volatile("csrr %0, vtype":"=r"(vtype)); + asm volatile("csrr %0, vl":"=r"(vl)); + rt_hw_vector_ctx_save(ctx_vector[0]); + + rt_memcpy(ctx_vector[1], ctx_vector[0], VECTOR_CTX_BYTES); + + rt_thread_startup(child); + rt_sem_take(sem, 0); + + /* STAGE 3, verify t1 context */ + asm volatile("vsetvl x0, %0, %1"::"r"(vl), "r"(vtype)); + rt_hw_vector_ctx_save(ctx_vector[0]); + uassert_false(rt_memcmp(ctx_vector[1], ctx_vector[0], VECTOR_CTX_BYTES)); + + rt_thread_yield(); +} + +/** + * ============================================================== + * TEST NO VECTOR raise error and recover + * ============================================================== + */ + +static void test_no_vector() +{ + asm volatile ("li t0, 0x600\n" + "csrc sstatus, t0"); + test_feature(); + uassert_true(1); +} + +static rt_err_t utest_tc_init(void) +{ + sem = rt_sem_create("test_ctx", 0, RT_IPC_FLAG_FIFO); + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + rt_sem_delete(sem); + return RT_EOK; +} + +static void testcase(void) +{ + UTEST_UNIT_RUN(test_feature); + UTEST_UNIT_RUN(test_context); + UTEST_UNIT_RUN(test_no_vector); +} + +UTEST_TC_EXPORT(testcase, "testcases.libcpu.vector", utest_tc_init, utest_tc_cleanup, 10); +#endif /* RT_USING_UTEST && ENABLE_VECTOR */ diff --git a/bsp/xiangshan/driver/Kconfig b/bsp/xiangshan/driver/Kconfig new file mode 100644 index 00000000000..e0df212a98b --- /dev/null +++ b/bsp/xiangshan/driver/Kconfig @@ -0,0 +1,43 @@ +menu "XiangShan configs" + +config BSP_USING_VIRTIO + bool "Using VirtIO" + default n + depends on RT_USING_DEVICE_OPS + +config BSP_USING_VIRTIO_BLK + bool "Using VirtIO BLK" + select RT_USING_VIRTIO + select RT_USING_VIRTIO_BLK + default n + depends on BSP_USING_VIRTIO + +config BSP_USING_VIRTIO_NET + bool "Using VirtIO NET" + select RT_USING_VIRTIO + select RT_USING_VIRTIO_NET + default n + depends on BSP_USING_VIRTIO + +config BSP_USING_VIRTIO_CONSOLE + bool "Using VirtIO Console" + select RT_USING_VIRTIO + select RT_USING_VIRTIO_CONSOLE + default n + depends on BSP_USING_VIRTIO + +config BSP_USING_VIRTIO_GPU + bool "Using VirtIO GPU" + select RT_USING_VIRTIO + select RT_USING_VIRTIO_GPU + default n + depends on BSP_USING_VIRTIO + +config BSP_USING_VIRTIO_INPUT + bool "Using VirtIO Input" + select RT_USING_VIRTIO + select RT_USING_VIRTIO_INPUT + default n + depends on BSP_USING_VIRTIO + +endmenu diff --git a/bsp/xiangshan/driver/SConscript b/bsp/xiangshan/driver/SConscript new file mode 100644 index 00000000000..faea9c1bd9b --- /dev/null +++ b/bsp/xiangshan/driver/SConscript @@ -0,0 +1,19 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH) + +objs = [group] + +list = os.listdir(cwd) + +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/bsp/xiangshan/driver/asm/sbiasm.h b/bsp/xiangshan/driver/asm/sbiasm.h new file mode 100644 index 00000000000..4639fba68cf --- /dev/null +++ b/bsp/xiangshan/driver/asm/sbiasm.h @@ -0,0 +1,10 @@ +#ifndef _SBI_ASM_H +#define _SBI_ASM_H + +.macro SBI_CALL which + li a7, \which + ecall + nop +.endm + +#endif /* _SBI_ASM_H */ diff --git a/bsp/xiangshan/driver/asm/sbidef.h b/bsp/xiangshan/driver/asm/sbidef.h new file mode 100644 index 00000000000..5bcf58ade7c --- /dev/null +++ b/bsp/xiangshan/driver/asm/sbidef.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2019-2020, Xim + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +#ifndef _ASM_SBI_DEF_H +#define _ASM_SBI_DEF_H + +#define SBI_SET_TIMER 0 +#define SBI_CONSOLE_PUTCHAR 1 +#define SBI_CONSOLE_GETCHAR 2 +#define SBI_CLEAR_IPI 3 +#define SBI_SEND_IPI 4 +#define SBI_REMOTE_FENCE_I 5 +#define SBI_REMOTE_SFENCE_VMA 6 +#define SBI_REMOTE_SFENCE_VMA_ASID 7 +#define SBI_SHUTDOWN 8 + +#define SBI_CONSOLE_PUTSTR 9 + +#define SBI_SD_WRITE 10 +#define SBI_SD_READ 11 +#define SBI_NET_WRITE 12 +#define SBI_NET_READ 13 + +#endif /* _ASM_SBI_DEF_H */ diff --git a/bsp/xiangshan/driver/board.c b/bsp/xiangshan/driver/board.c new file mode 100644 index 00000000000..092244278ed --- /dev/null +++ b/bsp/xiangshan/driver/board.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-01-30 lizhirui first version + */ + +#include +#include +#include + +#include "board.h" +#include "mm_aspace.h" +#include "tick.h" + +#include "drv_uart.h" +#include "encoding.h" +#include "stack.h" +#include "sbi.h" +#include "riscv.h" +#include "plic.h" +#include "stack.h" + +#ifdef RT_USING_SMP +#include "interrupt.h" +#endif /* RT_USING_SMP */ + +#ifdef RT_USING_SMART +#include "riscv_mmu.h" +#include "mmu.h" +#include "page.h" +#include "lwp_arch.h" + +rt_region_t init_page_region = {(rt_size_t)RT_HW_PAGE_START, (rt_size_t)RT_HW_PAGE_END}; + +extern size_t MMUTable[]; + +struct mem_desc platform_mem_desc[] = { + {KERNEL_VADDR_START, (rt_size_t)RT_HW_PAGE_END - 1, (rt_size_t)ARCH_MAP_FAILED, NORMAL_MEM}, +}; + +#define NUM_MEM_DESC (sizeof(platform_mem_desc) / sizeof(platform_mem_desc[0])) + +#endif + +void primary_cpu_entry(void) +{ + /* disable global interrupt */ + rt_hw_interrupt_disable(); + + entry(); +} + +#define IOREMAP_SIZE (1ul << 30) + +#ifndef ARCH_REMAP_KERNEL +#define IOREMAP_VEND USER_VADDR_START +#else +#define IOREMAP_VEND 0ul +#endif /* ARCH_REMAP_KERNEL */ + +void rt_hw_board_init(void) +{ +#ifdef RT_USING_SMART + /* init data structure */ + rt_hw_mmu_map_init(&rt_kernel_space, (void *)(IOREMAP_VEND - IOREMAP_SIZE), IOREMAP_SIZE, (rt_size_t *)MMUTable, PV_OFFSET); + + /* init page allocator */ + rt_page_init(init_page_region); + + /* setup region, and enable MMU */ + rt_hw_mmu_setup(&rt_kernel_space, platform_mem_desc, NUM_MEM_DESC); +#endif + +#ifdef RT_USING_HEAP + /* initialize memory system */ + rt_system_heap_init(RT_HW_HEAP_BEGIN, RT_HW_HEAP_END); +#endif + + plic_init(); + + rt_hw_interrupt_init(); + + rt_hw_uart_init(); + +#ifdef RT_USING_CONSOLE + /* set console device */ + rt_console_set_device(RT_CONSOLE_DEVICE_NAME); +#endif /* RT_USING_CONSOLE */ + + rt_hw_tick_init(); + +#ifdef RT_USING_SMP + /* ipi init */ + rt_hw_ipi_init(); +#endif /* RT_USING_SMP */ + +#ifdef RT_USING_COMPONENTS_INIT + rt_components_board_init(); +#endif + +#ifdef RT_USING_HEAP + rt_kprintf("heap: [0x%08x - 0x%08x]\n", (rt_ubase_t)RT_HW_HEAP_BEGIN, (rt_ubase_t)RT_HW_HEAP_END); +#endif /* RT_USING_HEAP */ +} + +void rt_hw_cpu_reset(void) +{ + sbi_shutdown(); + + while (1) + ; +} +MSH_CMD_EXPORT_ALIAS(rt_hw_cpu_reset, reboot, reset machine); + diff --git a/bsp/xiangshan/driver/board.h b/bsp/xiangshan/driver/board.h new file mode 100644 index 00000000000..9c74ae6b267 --- /dev/null +++ b/bsp/xiangshan/driver/board.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-01-30 lizhirui first version + */ + +#ifndef BOARD_H__ +#define BOARD_H__ + +#include + +extern unsigned int __bss_start; +extern unsigned int __bss_end; + +#ifndef RT_USING_SMART +#define KERNEL_VADDR_START 0x0 +#endif + +#define VIRT64_SBI_MEMSZ (0x200000) + +#define RT_HW_HEAP_BEGIN ((void *)&__bss_end) +#define RT_HW_HEAP_END ((void *)(RT_HW_HEAP_BEGIN + 64 * 1024 * 1024)) +#define RT_HW_PAGE_START RT_HW_HEAP_END +#define RT_HW_PAGE_END ((void *)(KERNEL_VADDR_START + (128 * 1024 * 1024 - VIRT64_SBI_MEMSZ))) + +void rt_hw_board_init(void); +void rt_init_user_mem(struct rt_thread *thread, const char *name, + unsigned long *entry); + +#endif diff --git a/bsp/xiangshan/driver/drv_uart.c b/bsp/xiangshan/driver/drv_uart.c new file mode 100644 index 00000000000..11755761e75 --- /dev/null +++ b/bsp/xiangshan/driver/drv_uart.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include + +#include "board.h" +#include "drv_uart.h" + +#include +#ifdef RT_USING_SMART +#include +#endif +#include "sbi.h" + +struct device_uart +{ + rt_ubase_t hw_base; + rt_uint32_t irqno; +}; + +void *uart0_base = (void*)0x310b0000; +struct rt_serial_device serial0; +struct device_uart uart0; + +void uart_init(void) +{ + rt_uint32_t div = UART_REFERENCE_CLOCK / (UART_DEFAULT_BAUDRATE * 16); + + write8_uart0(UART_IER, 0x00); + write8_uart0(UART_LCR, UART_LCR_BAUD_LATCH); + + // LSB + write8_uart0(0, div & 0xff); + // MSB + write8_uart0((1 << 2), (div >> 8) & 0xff); + + // set word length to 8 bits, no parity + write8_uart0(UART_LCR, UART_LCR_EIGHT_BITS); + + write8_uart0(UART_FCR, UART_FCR_FIFO_ENABLE | UART_FCR_FIFO_CLEAR); + + return; +} + +static rt_err_t _uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + return (RT_EOK); +} + +static rt_err_t _uart_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + struct device_uart *uart = (struct device_uart*)serial->parent.user_data; + + switch (cmd) + { + case RT_DEVICE_CTRL_CLR_INT: + if ((size_t)arg == RT_DEVICE_FLAG_INT_RX) + { + rt_uint8_t value = read8_uart0(UART_IER); + write8_uart0(UART_IER, value & ~UART_IER_RX_ENABLE); + } + break; + + case RT_DEVICE_CTRL_SET_INT: + if ((size_t)arg == RT_DEVICE_FLAG_INT_RX) + { + rt_uint8_t value = read8_uart0(UART_IER); + write8_uart0(UART_IER, value | UART_IER_RX_ENABLE); + } + break; + } + + return (RT_EOK); +} + +static int _uart_putc(struct rt_serial_device *serial, char c) +{ + struct device_uart *uart; + uart = (struct device_uart*)serial->parent.user_data; + + // wait for Transmit Holding Empty to be set in LSR. + while((read8_uart0(UART_LSR) & UART_LSR_TX_IDLE) == 0) + ; + write8_uart0(UART_THR, c); + + return (1); +} + +static int _uart_getc(struct rt_serial_device *serial) +{ + struct device_uart *uart; + volatile rt_uint32_t lsr; + int ch = -1; + + uart = (struct device_uart*)serial->parent.user_data; + lsr = read8_uart0(UART_LSR); + + if (lsr & UART_LSR_RX_READY) + { + ch = read8_uart0(UART_RHR); + } + return ch; +} + +const struct rt_uart_ops _uart_ops = { + _uart_configure, + _uart_control, + _uart_putc, + _uart_getc, + // TODO: add DMA support + RT_NULL}; + +static void rt_hw_uart_isr(int irqno, void *param) +{ + rt_ubase_t level = rt_hw_interrupt_disable(); + + struct rt_serial_device *serial = (struct rt_serial_device *)param; + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + + rt_hw_interrupt_enable(level); +} + +/* + * UART Initiation + */ +int rt_hw_uart_init(void) +{ + struct rt_serial_device *serial; + struct device_uart *uart; + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; +#ifdef RT_USING_SMART + uart0_base = rt_ioremap(uart0_base, 4096); +#endif + // register device + serial = &serial0; + uart = &uart0; + + serial->ops = &_uart_ops; + serial->config = config; + serial->config.baud_rate = UART_DEFAULT_BAUDRATE; + uart->hw_base = (rt_ubase_t)uart0_base; + uart->irqno = 0x0a; + + rt_hw_serial_register(serial, + RT_CONSOLE_DEVICE_NAME, + RT_DEVICE_FLAG_STREAM | RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + uart); + rt_hw_interrupt_install(uart->irqno, rt_hw_uart_isr, serial, RT_CONSOLE_DEVICE_NAME); + rt_hw_interrupt_umask(uart->irqno); + return 0; +} + +/* WEAK for SDK 0.5.6 */ +rt_weak void uart_debug_init(int uart_channel) +{ +} diff --git a/bsp/xiangshan/driver/drv_uart.h b/bsp/xiangshan/driver/drv_uart.h new file mode 100644 index 00000000000..d40295d8188 --- /dev/null +++ b/bsp/xiangshan/driver/drv_uart.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __DRV_UART_H__ +#define __DRV_UART_H__ + +#include "riscv_io.h" + +/** + * uart ns16550a + * http://byterunner.com/16550.html + */ + +/* TRANSMIT AND RECEIVE HOLDING REGISTER */ +#define UART_RHR 0 +#define UART_THR 0 + +/* INTERRUPT ENABLE REGISTER */ +#define UART_IER (1 << 2) +#define UART_IER_RX_ENABLE (1 << 0) +#define UART_IER_TX_ENABLE (1 << 1) + +/* FIFO CONTROL REGISTER */ +#define UART_FCR (2 << 2) +#define UART_FCR_FIFO_ENABLE (1 << 0) +#define UART_FCR_FIFO_CLEAR (3 << 1) + +/* INTERRUPT STATUS REGISTER */ +#define UART_ISR (2 << 2) + +/* LINE CONTROL REGISTER */ +#define UART_LCR (3 << 2) +#define UART_LCR_EIGHT_BITS (3 << 0) +// special mode to set baud rate +#define UART_LCR_BAUD_LATCH (1 << 7) + +/* LINE STATUS REGISTER */ +#define UART_LSR (5 << 2) +// input is waiting to be read from RHR +#define UART_LSR_RX_READY (1 << 0) +// THR can accept another character to send +#define UART_LSR_TX_IDLE (1 << 5) + +#define UART_REFERENCE_CLOCK 1843200 +#define UART_DEFAULT_BAUDRATE 115200 + +extern void *uart0_base; + +#define write8_uart0(idx, value) __raw_writeb(((rt_uint8_t)value), (void*)((size_t)uart0_base + (idx))) +#define read8_uart0(idx) __raw_readb((void*)((size_t)uart0_base + (idx))) + +void rt_hw_uart_start_rx_thread(); +int rt_hw_uart_init(void); +void drv_uart_puts(char *str); // for syscall + +#endif /* __DRV_UART_H__ */ diff --git a/bsp/xiangshan/driver/drv_virtio.c b/bsp/xiangshan/driver/drv_virtio.c new file mode 100644 index 00000000000..e1289863b7e --- /dev/null +++ b/bsp/xiangshan/driver/drv_virtio.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#include +#include + +#ifdef BSP_USING_VIRTIO + +#include +#ifdef BSP_USING_VIRTIO_BLK +#include +#endif +#ifdef BSP_USING_VIRTIO_NET +#include +#endif +#ifdef BSP_USING_VIRTIO_CONSOLE +#include +#endif +#ifdef BSP_USING_VIRTIO_GPU +#include +#endif +#ifdef BSP_USING_VIRTIO_INPUT +#include +#endif + +#include + +static virtio_device_init_handler virtio_device_init_handlers[] = +{ +#ifdef BSP_USING_VIRTIO_BLK + [VIRTIO_DEVICE_ID_BLOCK] = rt_virtio_blk_init, +#endif +#ifdef BSP_USING_VIRTIO_NET + [VIRTIO_DEVICE_ID_NET] = rt_virtio_net_init, +#endif +#ifdef BSP_USING_VIRTIO_CONSOLE + [VIRTIO_DEVICE_ID_CONSOLE] = rt_virtio_console_init, +#endif +#ifdef BSP_USING_VIRTIO_GPU + [VIRTIO_DEVICE_ID_GPU] = rt_virtio_gpu_init, +#endif +#ifdef BSP_USING_VIRTIO_INPUT + [VIRTIO_DEVICE_ID_INPUT] = rt_virtio_input_init, +#endif + [VIRTIO_DEVICE_TYPE_SIZE] = RT_NULL +}; + +int rt_virtio_devices_init(void) +{ + int i; + rt_uint32_t irq = VIRTIO_IRQ_BASE; + rt_ubase_t mmio_base = VIRTIO_MMIO_BASE; + struct virtio_mmio_config *mmio_config; + virtio_device_init_handler init_handler; + + if (sizeof(virtio_device_init_handlers) == 0) + { + /* The compiler will optimize the codes after here. */ + return 0; + } + +#ifdef RT_USING_SMART + mmio_base = (rt_ubase_t)rt_ioremap((void *)mmio_base, VIRTIO_MMIO_SIZE * VIRTIO_MAX_NR); + + if (mmio_base == RT_NULL) + { + return -RT_ERROR; + } +#endif + + for (i = 0; i < VIRTIO_MAX_NR; ++i, ++irq, mmio_base += VIRTIO_MMIO_SIZE) + { + mmio_config = (struct virtio_mmio_config *)mmio_base; + + if (mmio_config->magic != VIRTIO_MAGIC_VALUE || + mmio_config->version != RT_USING_VIRTIO_VERSION || + mmio_config->vendor_id != VIRTIO_VENDOR_ID) + { + continue; + } + + init_handler = virtio_device_init_handlers[mmio_config->device_id]; + + if (init_handler != RT_NULL) + { + init_handler((rt_ubase_t *)mmio_base, irq); + } + } + + return 0; +} +INIT_DEVICE_EXPORT(rt_virtio_devices_init); +#endif /* BSP_USING_VIRTIO */ diff --git a/bsp/xiangshan/driver/drv_virtio.h b/bsp/xiangshan/driver/drv_virtio.h new file mode 100644 index 00000000000..954338a3864 --- /dev/null +++ b/bsp/xiangshan/driver/drv_virtio.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#ifndef __DRV_VIRTIO_H__ +#define __DRV_VIRTIO_H__ + +int rt_virtio_devices_init(void); + +#endif /* __DRV_VIRTIO_H__ */ diff --git a/bsp/xiangshan/driver/virt.h b/bsp/xiangshan/driver/virt.h new file mode 100644 index 00000000000..f059feb50e6 --- /dev/null +++ b/bsp/xiangshan/driver/virt.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-02-17 GuEe-GUI the first version + */ + +#ifndef VIRT_H__ +#define VIRT_H__ + +#include + +#ifdef RT_USING_SMART +#include +#include + +#endif + +/* VirtIO */ +#define VIRTIO_MMIO_BASE 0x10001000 +#define VIRTIO_MMIO_SIZE 0x00001000 +#define VIRTIO_MAX_NR 8 +#define VIRTIO_IRQ_BASE 1 +#define VIRTIO_VENDOR_ID 0x554d4551 /* "QEMU" */ + +#define MAX_HANDLERS 128 +#endif diff --git a/bsp/xiangshan/link.lds b/bsp/xiangshan/link.lds new file mode 100644 index 00000000000..da750aca9bb --- /dev/null +++ b/bsp/xiangshan/link.lds @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020/12/12 bernard The first version + */ + +INCLUDE "link_stacksize.lds" +INCLUDE "link_cpus.lds" + +OUTPUT_ARCH( "riscv" ) + +/* + * Memory layout: + * 0x80000000 - 0x80200000: SBI + * 0x80200000 - 0x81200000: Kernel + */ + +MEMORY +{ + SRAM : ORIGIN = 0x80200000, LENGTH = 0x1000000 +} + +ENTRY(_start) +SECTIONS +{ + . = 0x80200000 ; + + /* __STACKSIZE__ = 4096; */ + __text_start = .; + .start : + { + *(.start); + } > SRAM + + . = ALIGN(8); + + .text : + { + *(.text) /* remaining code */ + *(.text.*) /* remaining code */ + *(.rodata) /* read-only data (constants) */ + *(.rodata*) + *(.glue_7) + *(.glue_7t) + *(.gnu.linkonce.t*) + + /* section information for finsh shell */ + . = ALIGN(8); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + . = ALIGN(8); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + . = ALIGN(8); + + /* section information for initial. */ + . = ALIGN(8); + __rt_init_start = .; + KEEP(*(SORT(.rti_fn*))) + __rt_init_end = .; + . = ALIGN(8); + + __rt_utest_tc_tab_start = .; + KEEP(*(UtestTcTab)) + __rt_utest_tc_tab_end = .; + + . = ALIGN(8); + _etext = .; + } > SRAM + + .eh_frame_hdr : + { + *(.eh_frame_hdr) + *(.eh_frame_entry) + } > SRAM + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } > SRAM + + . = ALIGN(8); + __text_end = .; + __text_size = __text_end - __text_start; + + .data : + { + *(.data) + *(.data.*) + + *(.data1) + *(.data1.*) + + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + + *(.sdata) + *(.sdata.*) + } > SRAM + + . = ALIGN(8); + .ctors : + { + PROVIDE(__ctors_start__ = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE(__ctors_end__ = .); + } > SRAM + + .dtors : + { + PROVIDE(__dtors_start__ = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE(__dtors_end__ = .); + } > SRAM + + /* stack for dual core */ + .stack : + { + . = ALIGN(64); + __stack_start__ = .; + /* Dynamically allocate stack areas according to RT_CPUS_NR */ + . += (__STACKSIZE__ * RT_CPUS_NR); + __stack_end__ = .; + } > SRAM + + .sbss : + { + __bss_start = .; + *(.sbss) + *(.sbss.*) + *(.dynsbss) + *(.scommon) + } > SRAM + + .percpu (NOLOAD) : + { + /* 2MB Align for MMU early map */ + . = ALIGN(0x200000); + PROVIDE(__percpu_start = .); + + *(.percpu) + + /* 2MB Align for MMU early map */ + . = ALIGN(0x200000); + + PROVIDE(__percpu_end = .); + + /* Clone the area */ + . = __percpu_end + (__percpu_end - __percpu_start) * (RT_CPUS_NR - 1); + PROVIDE(__percpu_real_end = .); + } > SRAM + + .bss : + { + *(.bss) + *(.bss.*) + *(.dynbss) + *(COMMON) + __bss_end = .; + } > SRAM + + _end = .; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + * Symbols in the DWARF debugging sections are relative to the beginning + * of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} diff --git a/bsp/xiangshan/link_cpus.lds b/bsp/xiangshan/link_cpus.lds new file mode 100644 index 00000000000..e4cd5b88712 --- /dev/null +++ b/bsp/xiangshan/link_cpus.lds @@ -0,0 +1 @@ +RT_CPUS_NR = 1; diff --git a/bsp/xiangshan/link_smart.lds b/bsp/xiangshan/link_smart.lds new file mode 100644 index 00000000000..29d33fdbb1c --- /dev/null +++ b/bsp/xiangshan/link_smart.lds @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020/12/12 bernard The first version + */ + +INCLUDE "link_stacksize.lds" +INCLUDE "link_cpus.lds" + +OUTPUT_ARCH( "riscv" ) + +/* + * Memory layout: + * 0x80000000 - 0x80200000: SBI + * 0x80200000 - 0x81200000: Kernel + */ + +MEMORY +{ + SRAM : ORIGIN = 0xFFFFFFC000200000, LENGTH = 0x1000000 - 0x200000 +} + +ENTRY(_start) +SECTIONS +{ + /* . = 0x80200000 ; */ + . = 0xFFFFFFC000200000; + + /* __STACKSIZE__ = 4096; */ + __text_start = .; + .start : + { + *(.start); + } > SRAM + + . = ALIGN(8); + + .text : + { + *(.text) /* remaining code */ + *(.text.*) /* remaining code */ + *(.rodata) /* read-only data (constants) */ + *(.rodata*) + *(.glue_7) + *(.glue_7t) + *(.gnu.linkonce.t*) + + /* section information for finsh shell */ + . = ALIGN(8); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + . = ALIGN(8); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + . = ALIGN(8); + + /* section information for initial. */ + . = ALIGN(8); + __rt_init_start = .; + KEEP(*(SORT(.rti_fn*))) + __rt_init_end = .; + . = ALIGN(8); + + __rt_utest_tc_tab_start = .; + KEEP(*(UtestTcTab)) + __rt_utest_tc_tab_end = .; + + . = ALIGN(8); + _etext = .; + } > SRAM + + .eh_frame_hdr : + { + *(.eh_frame_hdr) + *(.eh_frame_entry) + } > SRAM + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } > SRAM + + . = ALIGN(8); + __text_end = .; + __text_size = __text_end - __text_start; + + .data : + { + *(.data) + *(.data.*) + + *(.data1) + *(.data1.*) + + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + + *(.sdata) + *(.sdata.*) + } > SRAM + + . = ALIGN(8); + .ctors : + { + PROVIDE(__ctors_start__ = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE(__ctors_end__ = .); + } > SRAM + + .dtors : + { + PROVIDE(__dtors_start__ = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE(__dtors_end__ = .); + } > SRAM + + /* stack for dual core */ + .stack : + { + . = ALIGN(64); + __stack_start__ = .; + /* Dynamically allocate stack areas according to RT_CPUS_NR */ + . += (__STACKSIZE__ * RT_CPUS_NR); + __stack_end__ = .; + } > SRAM + + .sbss : + { + __bss_start = .; + *(.sbss) + *(.sbss.*) + *(.dynsbss) + *(.scommon) + } > SRAM + + .percpu (NOLOAD) : + { + /* 2MB Align for MMU early map */ + . = ALIGN(0x200000); + PROVIDE(__percpu_start = .); + + *(.percpu) + + /* 2MB Align for MMU early map */ + . = ALIGN(0x200000); + + PROVIDE(__percpu_end = .); + + /* Clone the area */ + . = __percpu_end + (__percpu_end - __percpu_start) * (RT_CPUS_NR - 1); + PROVIDE(__percpu_real_end = .); + } > SRAM + + .bss : + { + *(.bss) + *(.bss.*) + *(.dynbss) + *(COMMON) + __bss_end = .; + } > SRAM + + _end = .; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + * Symbols in the DWARF debugging sections are relative to the beginning + * of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} diff --git a/bsp/xiangshan/link_stacksize.lds b/bsp/xiangshan/link_stacksize.lds new file mode 100644 index 00000000000..14c2aad91f8 --- /dev/null +++ b/bsp/xiangshan/link_stacksize.lds @@ -0,0 +1 @@ +__STACKSIZE__ = 16384; diff --git a/bsp/xiangshan/qemu-dbg.sh b/bsp/xiangshan/qemu-dbg.sh new file mode 100755 index 00000000000..69f62e7f6fb --- /dev/null +++ b/bsp/xiangshan/qemu-dbg.sh @@ -0,0 +1,16 @@ +QEMU_CMD="qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin -s -S" + +if grep -q "#define RT_USING_SMP" ./rtconfig.h 2>/dev/null; then + hart_num=$(grep "RT_CPUS_NR = [0-9]*;" ./link_cpus.lds | awk -F'[=;]' '{gsub(/ /, "", $2); print $2}') + if [ -z "$hart_num" ]; then + hart_num=1 + fi + QEMU_CMD="$QEMU_CMD -smp $hart_num" +fi + +QEMU_CMD="$QEMU_CMD \ +-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \ +-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 \ +-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0" + +eval $QEMU_CMD \ No newline at end of file diff --git a/bsp/xiangshan/qemu-dumpdtb.sh b/bsp/xiangshan/qemu-dumpdtb.sh new file mode 100755 index 00000000000..12068b571a9 --- /dev/null +++ b/bsp/xiangshan/qemu-dumpdtb.sh @@ -0,0 +1 @@ +qemu-system-riscv64 -nographic -machine virt,dumpdtb=virt.dtb -m 256M -kernel rtthread.bin diff --git a/bsp/xiangshan/qemu-nographic.bat b/bsp/xiangshan/qemu-nographic.bat new file mode 100644 index 00000000000..df55b35e84f --- /dev/null +++ b/bsp/xiangshan/qemu-nographic.bat @@ -0,0 +1,9 @@ +@echo off +if exist sd.bin goto run +qemu-img create -f raw sd.bin 64M + +:run +qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin ^ +-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 ^ +-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 ^ +-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0 diff --git a/bsp/xiangshan/qemu-rv64ilp32-nographic.sh b/bsp/xiangshan/qemu-rv64ilp32-nographic.sh new file mode 100755 index 00000000000..798cb9d3d70 --- /dev/null +++ b/bsp/xiangshan/qemu-rv64ilp32-nographic.sh @@ -0,0 +1 @@ +qemu-system-riscv64ilp32 -cpu rv64 -M virt -m 256M -nographic -kernel rtthread.elf diff --git a/bsp/xiangshan/qemu-v-dbg.sh b/bsp/xiangshan/qemu-v-dbg.sh new file mode 100644 index 00000000000..d22af856cbf --- /dev/null +++ b/bsp/xiangshan/qemu-v-dbg.sh @@ -0,0 +1,4 @@ +qemu-system-riscv64 -nographic -machine virt -cpu rv64,v=true,vlen=128,vext_spec=v1.0 -m 256M -kernel rtthread.bin \ +-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \ +-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 -s -S \ +-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0 diff --git a/bsp/xiangshan/qemu-v-nographic.sh b/bsp/xiangshan/qemu-v-nographic.sh new file mode 100644 index 00000000000..068ff4fa6c3 --- /dev/null +++ b/bsp/xiangshan/qemu-v-nographic.sh @@ -0,0 +1,9 @@ +if [ ! -f "sd.bin" ]; then +dd if=/dev/zero of=sd.bin bs=1024 count=65536 +mkfs.fat sd.bin +fi + +qemu-system-riscv64 -nographic -machine virt -cpu rv64,v=true,vlen=128,vext_spec=v1.0 -m 256M -kernel rtthread.bin \ +-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \ +-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 \ +-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0 diff --git a/bsp/xiangshan/rtconfig.h b/bsp/xiangshan/rtconfig.h new file mode 100644 index 00000000000..d610a63bd8a --- /dev/null +++ b/bsp/xiangshan/rtconfig.h @@ -0,0 +1,554 @@ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* RT-Thread Kernel */ + +/* klibc options */ + +/* rt_vsnprintf options */ + +#define RT_KLIBC_USING_VSNPRINTF_LONGLONG +#define RT_KLIBC_USING_VSNPRINTF_STANDARD +#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS +#define RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS +#define RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER +#define RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER +#define RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE 32 +#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE 32 +#define RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION 6 +#define RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9 +#define RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS 4 +/* end of rt_vsnprintf options */ + +/* rt_vsscanf options */ + +/* end of rt_vsscanf options */ + +/* rt_memset options */ + +/* end of rt_memset options */ + +/* rt_memcpy options */ + +/* end of rt_memcpy options */ + +/* rt_memmove options */ + +/* end of rt_memmove options */ + +/* rt_memcmp options */ + +/* end of rt_memcmp options */ + +/* rt_strstr options */ + +/* end of rt_strstr options */ + +/* rt_strcasecmp options */ + +/* end of rt_strcasecmp options */ + +/* rt_strncpy options */ + +/* end of rt_strncpy options */ + +/* rt_strcpy options */ + +/* end of rt_strcpy options */ + +/* rt_strncmp options */ + +/* end of rt_strncmp options */ + +/* rt_strcmp options */ + +/* end of rt_strcmp options */ + +/* rt_strlen options */ + +/* end of rt_strlen options */ + +/* rt_strnlen options */ + +/* end of rt_strnlen options */ +/* end of klibc options */ +#define RT_NAME_MAX 24 +#define RT_CPUS_NR 1 +#define RT_ALIGN_SIZE 8 +#define RT_THREAD_PRIORITY_32 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 100 +#define RT_USING_OVERFLOW_CHECK +#define RT_USING_HOOK +#define RT_HOOK_USING_FUNC_PTR +#define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 4 +#define IDLE_THREAD_STACK_SIZE 16384 +#define RT_USING_TIMER_SOFT +#define RT_TIMER_THREAD_PRIO 4 +#define RT_TIMER_THREAD_STACK_SIZE 16384 +#define RT_USING_CPU_USAGE_TRACER + +/* kservice options */ + +/* end of kservice options */ +#define RT_USING_DEBUG +#define RT_DEBUGING_ASSERT +#define RT_DEBUGING_COLOR +#define RT_DEBUGING_CONTEXT + +/* Inter-Thread communication */ + +#define RT_USING_SEMAPHORE +#define RT_USING_MUTEX +#define RT_USING_EVENT +#define RT_USING_MAILBOX +#define RT_USING_MESSAGEQUEUE +#define RT_USING_SIGNALS +/* end of Inter-Thread communication */ + +/* Memory Management */ + +#define RT_USING_MEMPOOL +#define RT_USING_SLAB +#define RT_USING_SLAB_AS_HEAP +#define RT_USING_MEMTRACE +#define RT_USING_HEAP +/* end of Memory Management */ +#define RT_USING_DEVICE +#define RT_USING_DEVICE_OPS +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 256 +#define RT_CONSOLE_DEVICE_NAME "uart0" +#define RT_VER_NUM 0x50300 +#define RT_BACKTRACE_LEVEL_MAX_NR 32 +/* end of RT-Thread Kernel */ +#define ARCH_CPU_64BIT +#define RT_USING_CACHE +#define ARCH_MM_MMU +#define ARCH_RISCV +#define ARCH_RISCV64 +#define ARCH_USING_NEW_CTX_SWITCH +#define ARCH_USING_RISCV_COMMON64 +#define ARCH_REMAP_KERNEL + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 8388608 +#define RT_MAIN_THREAD_PRIORITY 10 +#define RT_USING_MSH +#define RT_USING_FINSH +#define FINSH_USING_MSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 16384 +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 10 +#define FINSH_USING_SYMTAB +#define FINSH_CMD_SIZE 80 +#define MSH_USING_BUILT_IN_COMMANDS +#define FINSH_USING_DESCRIPTION +#define FINSH_ARG_MAX 10 +#define FINSH_USING_OPTION_COMPLETION + +/* DFS: device virtual file system */ + +#define RT_USING_DFS +#define DFS_USING_POSIX +#define DFS_USING_WORKDIR +#define DFS_FD_MAX 32 +#define RT_USING_DFS_V2 +#define RT_USING_DFS_ELMFAT + +/* elm-chan's FatFs, Generic FAT Filesystem Module */ + +#define RT_DFS_ELM_CODE_PAGE 437 +#define RT_DFS_ELM_WORD_ACCESS +#define RT_DFS_ELM_USE_LFN_3 +#define RT_DFS_ELM_USE_LFN 3 +#define RT_DFS_ELM_LFN_UNICODE_0 +#define RT_DFS_ELM_LFN_UNICODE 0 +#define RT_DFS_ELM_MAX_LFN 255 +#define RT_DFS_ELM_DRIVES 2 +#define RT_DFS_ELM_MAX_SECTOR_SIZE 512 +#define RT_DFS_ELM_REENTRANT +#define RT_DFS_ELM_MUTEX_TIMEOUT 3000 +/* end of elm-chan's FatFs, Generic FAT Filesystem Module */ +#define RT_USING_DFS_DEVFS +#define RT_USING_DFS_ROMFS +/* end of DFS: device virtual file system */ + +/* Device Drivers */ + +#define RT_USING_DEVICE_IPC +#define RT_UNAMED_PIPE_NUMBER 64 +#define RT_USING_SYSTEM_WORKQUEUE +#define RT_SYSTEM_WORKQUEUE_STACKSIZE 8192 +#define RT_SYSTEM_WORKQUEUE_PRIORITY 23 +#define RT_USING_SERIAL +#define RT_USING_SERIAL_V1 +#define RT_SERIAL_USING_DMA +#define RT_SERIAL_RB_BUFSZ 64 +#define RT_USING_CPUTIME +#define RT_USING_CPUTIME_RISCV +#define CPUTIME_TIMER_FREQ 10000000 +#define RT_USING_NULL +#define RT_USING_ZERO +#define RT_USING_RANDOM +#define RT_USING_RTC +#define RT_USING_SOFT_RTC +#define RT_USING_KTIME +/* end of Device Drivers */ + +/* C/C++ and POSIX layer */ + +/* ISO-ANSI C layer */ + +/* Timezone and Daylight Saving Time */ + +#define RT_LIBC_USING_LIGHT_TZ_DST +#define RT_LIBC_TZ_DEFAULT_HOUR 8 +#define RT_LIBC_TZ_DEFAULT_MIN 0 +#define RT_LIBC_TZ_DEFAULT_SEC 0 +/* end of Timezone and Daylight Saving Time */ +/* end of ISO-ANSI C layer */ + +/* POSIX (Portable Operating System Interface) layer */ + +#define RT_USING_POSIX_FS +#define RT_USING_POSIX_DEVIO +#define RT_USING_POSIX_STDIO +#define RT_USING_POSIX_POLL +#define RT_USING_POSIX_SELECT +#define RT_USING_POSIX_TERMIOS +#define RT_USING_POSIX_AIO +#define RT_USING_POSIX_MMAN +#define RT_USING_POSIX_DELAY +#define RT_USING_POSIX_CLOCK +#define RT_USING_POSIX_TIMER + +/* Interprocess Communication (IPC) */ + +#define RT_USING_POSIX_PIPE +#define RT_USING_POSIX_PIPE_SIZE 512 + +/* Socket is in the 'Network' category */ + +/* end of Interprocess Communication (IPC) */ +/* end of POSIX (Portable Operating System Interface) layer */ +#define RT_USING_CPLUSPLUS +#define RT_USING_CPP_WRAPPER +/* end of C/C++ and POSIX layer */ + +/* Network */ + +#define RT_USING_SAL +#define SAL_INTERNET_CHECK +#define SOCKET_TABLE_STEP_LEN 4 + +/* Docking with protocol stacks */ + +#define SAL_USING_LWIP +/* end of Docking with protocol stacks */ +#define SAL_USING_POSIX +#define RT_USING_NETDEV +#define NETDEV_USING_IFCONFIG +#define NETDEV_USING_PING +#define NETDEV_USING_NETSTAT +#define NETDEV_USING_AUTO_DEFAULT +#define NETDEV_IPV4 1 +#define NETDEV_IPV6 0 +#define RT_USING_LWIP +#define RT_USING_LWIP203 +#define RT_USING_LWIP_VER_NUM 0x20003 +#define RT_LWIP_MEM_ALIGNMENT 4 +#define RT_LWIP_IGMP +#define RT_LWIP_ICMP +#define RT_LWIP_DNS +#define RT_LWIP_DHCP +#define IP_SOF_BROADCAST 1 +#define IP_SOF_BROADCAST_RECV 1 + +/* Static IPv4 Address */ + +#define RT_LWIP_IPADDR "192.168.1.30" +#define RT_LWIP_GWADDR "192.168.1.1" +#define RT_LWIP_MSKADDR "255.255.255.0" +/* end of Static IPv4 Address */ +#define RT_LWIP_UDP +#define RT_LWIP_TCP +#define RT_LWIP_RAW +#define RT_MEMP_NUM_NETCONN 8 +#define RT_LWIP_PBUF_NUM 16 +#define RT_LWIP_RAW_PCB_NUM 4 +#define RT_LWIP_UDP_PCB_NUM 4 +#define RT_LWIP_TCP_PCB_NUM 4 +#define RT_LWIP_TCP_SEG_NUM 40 +#define RT_LWIP_TCP_SND_BUF 8196 +#define RT_LWIP_TCP_WND 8196 +#define RT_LWIP_TCPTHREAD_PRIORITY 10 +#define RT_LWIP_TCPTHREAD_MBOX_SIZE 8 +#define RT_LWIP_TCPTHREAD_STACKSIZE 8192 +#define RT_LWIP_ETHTHREAD_PRIORITY 12 +#define RT_LWIP_ETHTHREAD_STACKSIZE 8192 +#define RT_LWIP_ETHTHREAD_MBOX_SIZE 8 +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_LINK_CALLBACK 1 +#define RT_LWIP_NETIF_NAMESIZE 6 +#define SO_REUSE 1 +#define LWIP_SO_RCVTIMEO 1 +#define LWIP_SO_SNDTIMEO 1 +#define LWIP_SO_RCVBUF 1 +#define LWIP_SO_LINGER 0 +#define LWIP_NETIF_LOOPBACK 0 +#define RT_LWIP_USING_PING +/* end of Network */ + +/* Memory protection */ + +/* end of Memory protection */ + +/* Utilities */ + +#define RT_USING_UTEST +#define UTEST_THR_STACK_SIZE 4096 +#define UTEST_THR_PRIORITY 20 +#define RT_UTEST_MAX_OPTIONS 64 +#define RT_USING_RESOURCE_ID +#define RT_USING_ADT +#define RT_USING_ADT_AVL +#define RT_USING_ADT_BITMAP +#define RT_USING_ADT_HASHMAP +#define RT_USING_ADT_REF +/* end of Utilities */ + +/* Memory management */ + +#define RT_PAGE_AFFINITY_BLOCK_SIZE 0x1000 +#define RT_PAGE_MAX_ORDER 11 + +/* Debugging */ + +/* end of Debugging */ +/* end of Memory management */ + +/* Using USB legacy version */ + +/* end of Using USB legacy version */ +/* end of RT-Thread Components */ + +/* RT-Thread Utestcases */ + +/* end of RT-Thread Utestcases */ + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + + +/* Wi-Fi */ + +/* Marvell WiFi */ + +/* end of Marvell WiFi */ + +/* Wiced WiFi */ + +/* end of Wiced WiFi */ + +/* CYW43012 WiFi */ + +/* end of CYW43012 WiFi */ + +/* BL808 WiFi */ + +/* end of BL808 WiFi */ + +/* CYW43439 WiFi */ + +/* end of CYW43439 WiFi */ +/* end of Wi-Fi */ + +/* IoT Cloud */ + +/* end of IoT Cloud */ +/* end of IoT - internet of things */ + +/* security packages */ + +/* end of security packages */ + +/* language packages */ + +/* JSON: JavaScript Object Notation, a lightweight data-interchange format */ + +/* end of JSON: JavaScript Object Notation, a lightweight data-interchange format */ + +/* XML: Extensible Markup Language */ + +/* end of XML: Extensible Markup Language */ +/* end of language packages */ + +/* multimedia packages */ + +/* LVGL: powerful and easy-to-use embedded GUI library */ + +/* end of LVGL: powerful and easy-to-use embedded GUI library */ + +/* u8g2: a monochrome graphic library */ + +/* end of u8g2: a monochrome graphic library */ +/* end of multimedia packages */ + +/* tools packages */ + +/* end of tools packages */ + +/* system packages */ + +/* enhanced kernel services */ + +/* end of enhanced kernel services */ + +/* acceleration: Assembly language or algorithmic acceleration packages */ + +/* end of acceleration: Assembly language or algorithmic acceleration packages */ + +/* CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + +/* end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + +/* Micrium: Micrium software products porting for RT-Thread */ + +/* end of Micrium: Micrium software products porting for RT-Thread */ +/* end of system packages */ + +/* peripheral libraries and drivers */ + +/* HAL & SDK Drivers */ + +/* STM32 HAL & SDK Drivers */ + +/* end of STM32 HAL & SDK Drivers */ + +/* Infineon HAL Packages */ + +/* end of Infineon HAL Packages */ + +/* Kendryte SDK */ + +/* end of Kendryte SDK */ + +/* WCH HAL & SDK Drivers */ + +/* end of WCH HAL & SDK Drivers */ + +/* AT32 HAL & SDK Drivers */ + +/* end of AT32 HAL & SDK Drivers */ + +/* HC32 DDL Drivers */ + +/* end of HC32 DDL Drivers */ + +/* NXP HAL & SDK Drivers */ + +/* end of NXP HAL & SDK Drivers */ + +/* NUVOTON Drivers */ + +/* end of NUVOTON Drivers */ + +/* GD32 Drivers */ + +/* end of GD32 Drivers */ +/* end of HAL & SDK Drivers */ + +/* sensors drivers */ + +/* end of sensors drivers */ + +/* touch drivers */ + +/* end of touch drivers */ +/* end of peripheral libraries and drivers */ + +/* AI packages */ + +/* end of AI packages */ + +/* Signal Processing and Control Algorithm Packages */ + +/* end of Signal Processing and Control Algorithm Packages */ + +/* miscellaneous packages */ + +/* project laboratory */ + +/* end of project laboratory */ + +/* samples: kernel and components samples */ + +/* end of samples: kernel and components samples */ + +/* entertainment: terminal games and other interesting software packages */ + +/* end of entertainment: terminal games and other interesting software packages */ +/* end of miscellaneous packages */ + +/* Arduino libraries */ + + +/* Projects and Demos */ + +/* end of Projects and Demos */ + +/* Sensors */ + +/* end of Sensors */ + +/* Display */ + +/* end of Display */ + +/* Timing */ + +/* end of Timing */ + +/* Data Processing */ + +/* end of Data Processing */ + +/* Data Storage */ + +/* Communication */ + +/* end of Communication */ + +/* Device Control */ + +/* end of Device Control */ + +/* Other */ + +/* end of Other */ + +/* Signal IO */ + +/* end of Signal IO */ + +/* Uncategorized */ + +/* end of Arduino libraries */ +/* end of RT-Thread online packages */ + +/* XiangShan configs */ + +/* end of XiangShan configs */ +#define BOARD_XIANGSHAN +#define PLIC_BASE 0x3c000000 +#define __STACKSIZE__ 16384 + +#endif diff --git a/bsp/xiangshan/rtconfig.py b/bsp/xiangshan/rtconfig.py new file mode 100644 index 00000000000..d70d9fc1a02 --- /dev/null +++ b/bsp/xiangshan/rtconfig.py @@ -0,0 +1,51 @@ +import os + +# toolchains options +ARCH ='risc-v' +CPU ='virt64' +CROSS_TOOL ='llvm-riscv' + +RTT_ROOT = os.getenv('RTT_ROOT') or os.path.join(os.getcwd(), '..', '..') + +if os.getenv('RTT_CC'): + CROSS_TOOL = os.getenv('RTT_CC') + +if CROSS_TOOL == 'llvm-riscv': + PLATFORM = 'llvm-riscv' + EXEC_PATH = os.getenv('RTT_EXEC_PATH') or '/usr/bin' +else: + print('Please make sure your toolchains is LLVM RISC-V!') + exit(0) + +BUILD = 'release' + +if PLATFORM == 'llvm-riscv': + # toolchains + PREFIX = os.getenv('RTT_CC_PREFIX') or 'riscv64-unknown-elf-' + CC = PREFIX + 'clang' + CXX = PREFIX + 'clang++' + AS = PREFIX + 'clang' + AR = PREFIX + 'llvm-ar' + LINK = PREFIX + 'clang++' + TARGET_EXT = 'elf' + SIZE = PREFIX + 'size' + OBJDUMP = PREFIX + 'objdump' + OBJCPY = PREFIX + 'objcopy' + + DEVICE = ' -mcmodel=medany -march=rv64imac -mabi=lp64 ' + CFLAGS = DEVICE + '-fno-omit-frame-pointer -flax-vector-conversions -Wno-cpp -fno-common -ffunction-sections -fdata-sections -fdiagnostics-color=always -Xclang -fexperimental-max-bitint-width=20000 -fbracket-depth=2048 -Wno-parentheses-equality -DGSIM' + AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -D__ASSEMBLY__ ' + LFLAGS = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,_start -T link.lds' + ' -stdlib=libstdc++ -lc -lsupc++ -lgcc -lstdc++ -static' + CPATH = '' + LPATH = '' + + if BUILD == 'debug': + CFLAGS += ' -O0 -ggdb ' + AFLAGS += ' -ggdb' + else: + CFLAGS += ' -O3' + + CXXFLAGS = CFLAGS + +DUMP_ACTION = OBJDUMP + ' -D -S $TARGET > rtthread.asm\n' +POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread.bin\n' + SIZE + ' $TARGET \n' diff --git a/bsp/xiangshan/run.sh b/bsp/xiangshan/run.sh new file mode 100755 index 00000000000..c332915098c --- /dev/null +++ b/bsp/xiangshan/run.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +usage () +{ + echo "Usage:" + echo "./run.sh []" + echo "Note: if is not provided, will create a 'sd.bin'" + echo "in the current directory and load it by default." +} + +path_image=${1} + +if [ -z $path_image ]; then + path_image="./sd.bin" + if [ ! -f $path_image ]; then + dd if=/dev/zero of=$path_image bs=1024 count=65536 + mkfs.fat $path_image + fi +fi + +if [ ! -f $path_image ]; then + echo "ERROR: $path_image does not exist!" + usage + exit +fi + +QEMU_CMD="qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin" + +if grep -q "#define RT_USING_SMP" ./rtconfig.h 2>/dev/null; then + hart_num=$(grep "RT_CPUS_NR = [0-9]*;" ./link_cpus.lds 2>/dev/null | awk -F'[=;]' '{gsub(/ /, "", $2); print $2}') + if [ -z "$hart_num" ] || [ "$hart_num" -lt 1 ]; then + echo "Warning: Invalid or missing RT_CPUS_NR, defaulting to 1" + hart_num=1 + fi + QEMU_CMD="$QEMU_CMD -smp $hart_num" +fi + +QEMU_CMD="$QEMU_CMD \ +-drive if=none,file=$path_image,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \ +-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 \ +-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0" + +eval $QEMU_CMD \ No newline at end of file diff --git a/bsp/xiangshan/smart-env.bat b/bsp/xiangshan/smart-env.bat new file mode 100644 index 00000000000..87c310560fc --- /dev/null +++ b/bsp/xiangshan/smart-env.bat @@ -0,0 +1,30 @@ + +@set def_arch=arm + +@if not "%1"=="" ( + @set def_arch=%1 +) + +@if %def_arch%==arm ( + @set RTT_CC=gcc + @set RTT_EXEC_PATH=%cd%\tools\gnu_gcc\arm-linux-musleabi_for_i686-w64-mingw32\bin + @set RTT_CC_PREFIX=arm-linux-musleabi- + @copy configs\def_config_arm .config +) else if %def_arch%==riscv64 ( + @set RTT_CC=gcc + @set RTT_EXEC_PATH=E:\workspace\rt-smart\userapps\tools\gnu_gcc\riscv64-linux-musleabi_for_i686-w64-mingw32\bin + @set RTT_CC_PREFIX=riscv64-unknown-linux-musl- + @copy configs\def_config_riscv64 .config +) else ( + @echo "ERROR:supported_arch=arm riscv64!" + @goto EXIT +) + +@set PATH=%RTT_EXEC_PATH%;%PATH% + +@echo "Arch => %def_arch%" +@echo "CC => %RTT_CC%" +@echo "PREFIX => %RTT_CC_PREFIX%" +@echo "EXEC_PATH => %RTT_EXEC_PATH%" + +:EXIT \ No newline at end of file diff --git a/components/libc/compilers/common/extension/SConscript b/components/libc/compilers/common/extension/SConscript index ed632efac2b..74e7eaeb670 100644 --- a/components/libc/compilers/common/extension/SConscript +++ b/components/libc/compilers/common/extension/SConscript @@ -9,7 +9,7 @@ group = [] src += Glob('*.c') -if rtconfig.PLATFORM not in ['gcc', 'llvm-arm']: +if rtconfig.PLATFORM not in ['gcc', 'llvm-arm', 'llvm-riscv']: group = DefineGroup('Libc', src, depend = [''], CPPPATH = CPPPATH) list = os.listdir(cwd) diff --git a/components/libc/cplusplus/cxx_crt.cpp b/components/libc/cplusplus/cxx_crt.cpp index de2fe3cd740..adabc4f5528 100644 --- a/components/libc/cplusplus/cxx_crt.cpp +++ b/components/libc/cplusplus/cxx_crt.cpp @@ -21,12 +21,12 @@ void *operator new[](size_t size) return rt_malloc(size); } -void operator delete(void *ptr) +void operator delete(void *ptr) noexcept { rt_free(ptr); } -void operator delete[](void *ptr) +void operator delete[](void *ptr) noexcept { return rt_free(ptr); } diff --git a/components/libc/cplusplus/cxx_crt.h b/components/libc/cplusplus/cxx_crt.h index 903d4c86cd9..109d539177b 100644 --- a/components/libc/cplusplus/cxx_crt.h +++ b/components/libc/cplusplus/cxx_crt.h @@ -17,8 +17,8 @@ void *operator new(size_t size); void *operator new[](size_t size); -void operator delete(void * ptr); -void operator delete[](void *ptr); +void operator delete(void * ptr) noexcept; +void operator delete[](void *ptr) noexcept; extern "C" void __cxa_pure_virtual(void); extern "C" int cplusplus_system_init(void); diff --git a/components/libc/posix/io/mman/mman.c b/components/libc/posix/io/mman/mman.c index 68b906903c1..1d8dcda259c 100644 --- a/components/libc/posix/io/mman/mman.c +++ b/components/libc/posix/io/mman/mman.c @@ -44,24 +44,26 @@ void *mmap(void *addr, size_t length, int prot, int flags, if (mem) { - off_t cur; - size_t read_bytes; + if (!(flags & MAP_ANON)) + { + off_t cur; + size_t read_bytes; - cur = lseek(fd, 0, SEEK_SET); + cur = lseek(fd, 0, SEEK_SET); - lseek(fd, offset, SEEK_SET); - read_bytes = read(fd, mem, length); - if (read_bytes != length) - { - if (addr == RT_NULL) + lseek(fd, offset, SEEK_SET); + read_bytes = read(fd, mem, length); + if (read_bytes != length) { - /* read failed */ - free(mem); - mem = RT_NULL; + if (addr == RT_NULL) + { + /* read failed */ + free(mem); + mem = RT_NULL; + } } + lseek(fd, cur, SEEK_SET); } - lseek(fd, cur, SEEK_SET); - return mem; } diff --git a/libcpu/risc-v/virt64/plic.c b/libcpu/risc-v/virt64/plic.c index 051e89c5ced..3569e5c4056 100644 --- a/libcpu/risc-v/virt64/plic.c +++ b/libcpu/risc-v/virt64/plic.c @@ -26,7 +26,11 @@ #define rt_ioremap(addr, ...) (addr) #endif -size_t plic_base = 0x0c000000L; +#ifndef PLIC_BASE +#define PLIC_BASE 0x0c000000L +#endif + +size_t plic_base = PLIC_BASE; /* * Each PLIC interrupt source can be assigned a priority by writing From c2775d6a35df4e394946943c84ebbf120543de02 Mon Sep 17 00:00:00 2001 From: Haojin Tang Date: Thu, 5 Mar 2026 16:05:06 +0800 Subject: [PATCH 02/18] feat(bsp): add initial verilator bsp for xiangshan --- bsp/xiangshan-verilator/.config | 1581 +++++++++++++++++ bsp/xiangshan-verilator/.gitignore | 3 + bsp/xiangshan-verilator/Kconfig | 61 + bsp/xiangshan-verilator/README.md | 407 +++++ bsp/xiangshan-verilator/README_cn.md | 411 +++++ bsp/xiangshan-verilator/SConscript | 14 + bsp/xiangshan-verilator/SConstruct | 62 + .../applications/SConscript | 17 + bsp/xiangshan-verilator/applications/main.c | 19 + .../applications/test/SConscript | 17 + .../applications/test/test_vector/SConscript | 9 + .../test/test_vector/test_vector.c | 178 ++ bsp/xiangshan-verilator/driver/Kconfig | 43 + bsp/xiangshan-verilator/driver/SConscript | 19 + bsp/xiangshan-verilator/driver/asm/sbiasm.h | 10 + bsp/xiangshan-verilator/driver/asm/sbidef.h | 27 + bsp/xiangshan-verilator/driver/board.c | 118 ++ bsp/xiangshan-verilator/driver/board.h | 34 + bsp/xiangshan-verilator/driver/drv_uart.c | 165 ++ bsp/xiangshan-verilator/driver/drv_uart.h | 62 + bsp/xiangshan-verilator/driver/drv_virtio.c | 100 ++ bsp/xiangshan-verilator/driver/drv_virtio.h | 16 + bsp/xiangshan-verilator/driver/virt.h | 30 + bsp/xiangshan-verilator/link.lds | 201 +++ bsp/xiangshan-verilator/link_cpus.lds | 1 + bsp/xiangshan-verilator/link_smart.lds | 202 +++ bsp/xiangshan-verilator/link_stacksize.lds | 1 + bsp/xiangshan-verilator/qemu-dbg.sh | 16 + bsp/xiangshan-verilator/qemu-dumpdtb.sh | 1 + bsp/xiangshan-verilator/qemu-nographic.bat | 9 + .../qemu-rv64ilp32-nographic.sh | 1 + bsp/xiangshan-verilator/qemu-v-dbg.sh | 4 + bsp/xiangshan-verilator/qemu-v-nographic.sh | 9 + bsp/xiangshan-verilator/rtconfig.h | 554 ++++++ bsp/xiangshan-verilator/rtconfig.py | 51 + bsp/xiangshan-verilator/run.sh | 43 + bsp/xiangshan-verilator/smart-env.bat | 30 + 37 files changed, 4526 insertions(+) create mode 100644 bsp/xiangshan-verilator/.config create mode 100644 bsp/xiangshan-verilator/.gitignore create mode 100644 bsp/xiangshan-verilator/Kconfig create mode 100644 bsp/xiangshan-verilator/README.md create mode 100644 bsp/xiangshan-verilator/README_cn.md create mode 100644 bsp/xiangshan-verilator/SConscript create mode 100644 bsp/xiangshan-verilator/SConstruct create mode 100644 bsp/xiangshan-verilator/applications/SConscript create mode 100644 bsp/xiangshan-verilator/applications/main.c create mode 100644 bsp/xiangshan-verilator/applications/test/SConscript create mode 100644 bsp/xiangshan-verilator/applications/test/test_vector/SConscript create mode 100644 bsp/xiangshan-verilator/applications/test/test_vector/test_vector.c create mode 100644 bsp/xiangshan-verilator/driver/Kconfig create mode 100644 bsp/xiangshan-verilator/driver/SConscript create mode 100644 bsp/xiangshan-verilator/driver/asm/sbiasm.h create mode 100644 bsp/xiangshan-verilator/driver/asm/sbidef.h create mode 100644 bsp/xiangshan-verilator/driver/board.c create mode 100644 bsp/xiangshan-verilator/driver/board.h create mode 100644 bsp/xiangshan-verilator/driver/drv_uart.c create mode 100644 bsp/xiangshan-verilator/driver/drv_uart.h create mode 100644 bsp/xiangshan-verilator/driver/drv_virtio.c create mode 100644 bsp/xiangshan-verilator/driver/drv_virtio.h create mode 100644 bsp/xiangshan-verilator/driver/virt.h create mode 100644 bsp/xiangshan-verilator/link.lds create mode 100644 bsp/xiangshan-verilator/link_cpus.lds create mode 100644 bsp/xiangshan-verilator/link_smart.lds create mode 100644 bsp/xiangshan-verilator/link_stacksize.lds create mode 100755 bsp/xiangshan-verilator/qemu-dbg.sh create mode 100755 bsp/xiangshan-verilator/qemu-dumpdtb.sh create mode 100644 bsp/xiangshan-verilator/qemu-nographic.bat create mode 100755 bsp/xiangshan-verilator/qemu-rv64ilp32-nographic.sh create mode 100644 bsp/xiangshan-verilator/qemu-v-dbg.sh create mode 100644 bsp/xiangshan-verilator/qemu-v-nographic.sh create mode 100644 bsp/xiangshan-verilator/rtconfig.h create mode 100644 bsp/xiangshan-verilator/rtconfig.py create mode 100755 bsp/xiangshan-verilator/run.sh create mode 100644 bsp/xiangshan-verilator/smart-env.bat diff --git a/bsp/xiangshan-verilator/.config b/bsp/xiangshan-verilator/.config new file mode 100644 index 00000000000..8ed52cd1c3e --- /dev/null +++ b/bsp/xiangshan-verilator/.config @@ -0,0 +1,1581 @@ + +# +# RT-Thread Kernel +# + +# +# klibc options +# + +# +# rt_vsnprintf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSNPRINTF is not set +CONFIG_RT_KLIBC_USING_VSNPRINTF_LONGLONG=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER=y +CONFIG_RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER=y +# CONFIG_RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS is not set +CONFIG_RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE=32 +CONFIG_RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE=32 +CONFIG_RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION=6 +CONFIG_RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9 +CONFIG_RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS=4 +# end of rt_vsnprintf options + +# +# rt_vsscanf options +# +# CONFIG_RT_KLIBC_USING_LIBC_VSSCANF is not set +# end of rt_vsscanf options + +# +# rt_memset options +# +# CONFIG_RT_KLIBC_USING_USER_MEMSET is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMSET is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMSET is not set +# end of rt_memset options + +# +# rt_memcpy options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCPY is not set +# CONFIG_RT_KLIBC_USING_TINY_MEMCPY is not set +# end of rt_memcpy options + +# +# rt_memmove options +# +# CONFIG_RT_KLIBC_USING_USER_MEMMOVE is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMMOVE is not set +# end of rt_memmove options + +# +# rt_memcmp options +# +# CONFIG_RT_KLIBC_USING_USER_MEMCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_MEMCMP is not set +# end of rt_memcmp options + +# +# rt_strstr options +# +# CONFIG_RT_KLIBC_USING_USER_STRSTR is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRSTR is not set +# end of rt_strstr options + +# +# rt_strcasecmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCASECMP is not set +# end of rt_strcasecmp options + +# +# rt_strncpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCPY is not set +# end of rt_strncpy options + +# +# rt_strcpy options +# +# CONFIG_RT_KLIBC_USING_USER_STRCPY is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCPY is not set +# end of rt_strcpy options + +# +# rt_strncmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRNCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRNCMP is not set +# end of rt_strncmp options + +# +# rt_strcmp options +# +# CONFIG_RT_KLIBC_USING_USER_STRCMP is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRCMP is not set +# end of rt_strcmp options + +# +# rt_strlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRLEN is not set +# CONFIG_RT_KLIBC_USING_LIBC_STRLEN is not set +# end of rt_strlen options + +# +# rt_strnlen options +# +# CONFIG_RT_KLIBC_USING_USER_STRNLEN is not set +# end of rt_strnlen options +# end of klibc options + +CONFIG_RT_NAME_MAX=24 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +# CONFIG_RT_USING_NANO is not set +# CONFIG_RT_USING_SMART is not set +# CONFIG_RT_USING_AMP is not set +# CONFIG_RT_USING_SMP is not set +CONFIG_RT_CPUS_NR=1 +CONFIG_RT_ALIGN_SIZE=8 +# CONFIG_RT_THREAD_PRIORITY_8 is not set +CONFIG_RT_THREAD_PRIORITY_32=y +# CONFIG_RT_THREAD_PRIORITY_256 is not set +CONFIG_RT_THREAD_PRIORITY_MAX=32 +CONFIG_RT_TICK_PER_SECOND=100 +CONFIG_RT_USING_OVERFLOW_CHECK=y +CONFIG_RT_USING_HOOK=y +CONFIG_RT_HOOK_USING_FUNC_PTR=y +# CONFIG_RT_USING_HOOKLIST is not set +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=16384 +CONFIG_RT_USING_TIMER_SOFT=y +CONFIG_RT_TIMER_THREAD_PRIO=4 +CONFIG_RT_TIMER_THREAD_STACK_SIZE=16384 +# CONFIG_RT_USING_TIMER_ALL_SOFT is not set +CONFIG_RT_USING_CPU_USAGE_TRACER=y + +# +# kservice options +# +# CONFIG_RT_USING_TINY_FFS is not set +# end of kservice options + +CONFIG_RT_USING_DEBUG=y +CONFIG_RT_DEBUGING_ASSERT=y +CONFIG_RT_DEBUGING_COLOR=y +CONFIG_RT_DEBUGING_CONTEXT=y +# CONFIG_RT_DEBUGING_AUTO_INIT is not set +# CONFIG_RT_USING_CI_ACTION is not set + +# +# Inter-Thread communication +# +CONFIG_RT_USING_SEMAPHORE=y +CONFIG_RT_USING_MUTEX=y +CONFIG_RT_USING_EVENT=y +CONFIG_RT_USING_MAILBOX=y +CONFIG_RT_USING_MESSAGEQUEUE=y +# CONFIG_RT_USING_MESSAGEQUEUE_PRIORITY is not set +CONFIG_RT_USING_SIGNALS=y +# end of Inter-Thread communication + +# +# Memory Management +# +CONFIG_RT_USING_MEMPOOL=y +# CONFIG_RT_USING_SMALL_MEM is not set +CONFIG_RT_USING_SLAB=y +# CONFIG_RT_USING_MEMHEAP is not set +# CONFIG_RT_USING_SMALL_MEM_AS_HEAP is not set +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set +CONFIG_RT_USING_SLAB_AS_HEAP=y +# CONFIG_RT_USING_USERHEAP is not set +# CONFIG_RT_USING_NOHEAP is not set +CONFIG_RT_USING_MEMTRACE=y +# CONFIG_RT_USING_HEAP_ISR is not set +CONFIG_RT_USING_HEAP=y +# end of Memory Management + +CONFIG_RT_USING_DEVICE=y +CONFIG_RT_USING_DEVICE_OPS=y +# CONFIG_RT_USING_INTERRUPT_INFO is not set +# CONFIG_RT_USING_THREADSAFE_PRINTF is not set +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=256 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart0" +CONFIG_RT_VER_NUM=0x50300 +# CONFIG_RT_USING_STDC_ATOMIC is not set +CONFIG_RT_BACKTRACE_LEVEL_MAX_NR=32 +# end of RT-Thread Kernel + +CONFIG_ARCH_CPU_64BIT=y +CONFIG_RT_USING_CACHE=y +CONFIG_ARCH_MM_MMU=y +CONFIG_ARCH_RISCV=y +CONFIG_ARCH_RISCV64=y +CONFIG_ARCH_USING_NEW_CTX_SWITCH=y +CONFIG_ARCH_USING_RISCV_COMMON64=y +CONFIG_ARCH_REMAP_KERNEL=y + +# +# RT-Thread Components +# +CONFIG_RT_USING_COMPONENTS_INIT=y +CONFIG_RT_USING_USER_MAIN=y +CONFIG_RT_MAIN_THREAD_STACK_SIZE=8388608 +CONFIG_RT_MAIN_THREAD_PRIORITY=10 +# CONFIG_RT_USING_LEGACY is not set +CONFIG_RT_USING_MSH=y +CONFIG_RT_USING_FINSH=y +CONFIG_FINSH_USING_MSH=y +CONFIG_FINSH_THREAD_NAME="tshell" +CONFIG_FINSH_THREAD_PRIORITY=20 +CONFIG_FINSH_THREAD_STACK_SIZE=16384 +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=10 +# CONFIG_FINSH_USING_WORD_OPERATION is not set +# CONFIG_FINSH_USING_FUNC_EXT is not set +CONFIG_FINSH_USING_SYMTAB=y +CONFIG_FINSH_CMD_SIZE=80 +CONFIG_MSH_USING_BUILT_IN_COMMANDS=y +CONFIG_FINSH_USING_DESCRIPTION=y +# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set +# CONFIG_FINSH_USING_AUTH is not set +CONFIG_FINSH_ARG_MAX=10 +CONFIG_FINSH_USING_OPTION_COMPLETION=y + +# +# DFS: device virtual file system +# +CONFIG_RT_USING_DFS=y +CONFIG_DFS_USING_POSIX=y +CONFIG_DFS_USING_WORKDIR=y +CONFIG_DFS_FD_MAX=32 +# CONFIG_RT_USING_DFS_V1 is not set +CONFIG_RT_USING_DFS_V2=y +CONFIG_RT_USING_DFS_ELMFAT=y + +# +# elm-chan's FatFs, Generic FAT Filesystem Module +# +CONFIG_RT_DFS_ELM_CODE_PAGE=437 +CONFIG_RT_DFS_ELM_WORD_ACCESS=y +# CONFIG_RT_DFS_ELM_USE_LFN_0 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_1 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_2 is not set +CONFIG_RT_DFS_ELM_USE_LFN_3=y +CONFIG_RT_DFS_ELM_USE_LFN=3 +CONFIG_RT_DFS_ELM_LFN_UNICODE_0=y +# CONFIG_RT_DFS_ELM_LFN_UNICODE_1 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_2 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_3 is not set +CONFIG_RT_DFS_ELM_LFN_UNICODE=0 +CONFIG_RT_DFS_ELM_MAX_LFN=255 +CONFIG_RT_DFS_ELM_DRIVES=2 +CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=512 +# CONFIG_RT_DFS_ELM_USE_ERASE is not set +CONFIG_RT_DFS_ELM_REENTRANT=y +CONFIG_RT_DFS_ELM_MUTEX_TIMEOUT=3000 +# CONFIG_RT_DFS_ELM_USE_EXFAT is not set +# end of elm-chan's FatFs, Generic FAT Filesystem Module + +CONFIG_RT_USING_DFS_DEVFS=y +CONFIG_RT_USING_DFS_ROMFS=y +# CONFIG_RT_USING_DFS_CROMFS is not set +# CONFIG_RT_USING_DFS_TMPFS is not set +# CONFIG_RT_USING_DFS_MQUEUE is not set +# end of DFS: device virtual file system + +# CONFIG_RT_USING_FAL is not set + +# +# Device Drivers +# +# CONFIG_RT_USING_DM is not set +# CONFIG_RT_USING_DEV_BUS is not set +CONFIG_RT_USING_DEVICE_IPC=y +CONFIG_RT_UNAMED_PIPE_NUMBER=64 +CONFIG_RT_USING_SYSTEM_WORKQUEUE=y +CONFIG_RT_SYSTEM_WORKQUEUE_STACKSIZE=8192 +CONFIG_RT_SYSTEM_WORKQUEUE_PRIORITY=23 +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_USING_SERIAL_V1=y +# CONFIG_RT_USING_SERIAL_V2 is not set +CONFIG_RT_SERIAL_USING_DMA=y +CONFIG_RT_SERIAL_RB_BUFSZ=64 +# CONFIG_RT_USING_SERIAL_BYPASS is not set +# CONFIG_RT_USING_CAN is not set +CONFIG_RT_USING_CPUTIME=y +CONFIG_RT_USING_CPUTIME_RISCV=y +CONFIG_CPUTIME_TIMER_FREQ=10000000 +# CONFIG_RT_USING_I2C is not set +# CONFIG_RT_USING_PHY is not set +# CONFIG_RT_USING_PHY_V2 is not set +# CONFIG_RT_USING_ADC is not set +# CONFIG_RT_USING_DAC is not set +CONFIG_RT_USING_NULL=y +CONFIG_RT_USING_ZERO=y +CONFIG_RT_USING_RANDOM=y +# CONFIG_RT_USING_PWM is not set +# CONFIG_RT_USING_PULSE_ENCODER is not set +# CONFIG_RT_USING_INPUT_CAPTURE is not set +# CONFIG_RT_USING_MTD_NOR is not set +# CONFIG_RT_USING_MTD_NAND is not set +# CONFIG_RT_USING_PM is not set +CONFIG_RT_USING_RTC=y +# CONFIG_RT_USING_ALARM is not set +CONFIG_RT_USING_SOFT_RTC=y +# CONFIG_RT_USING_SDIO is not set +# CONFIG_RT_USING_SPI is not set +# CONFIG_RT_USING_WDT is not set +# CONFIG_RT_USING_AUDIO is not set +# CONFIG_RT_USING_SENSOR is not set +# CONFIG_RT_USING_TOUCH is not set +# CONFIG_RT_USING_LCD is not set +# CONFIG_RT_USING_HWCRYPTO is not set +# CONFIG_RT_USING_WIFI is not set +# CONFIG_RT_USING_BLK is not set +# CONFIG_RT_USING_VIRTIO is not set +# CONFIG_RT_USING_VIRTIO_MMIO_ALIGN is not set +# CONFIG_RT_USING_PIN is not set +CONFIG_RT_USING_KTIME=y +# CONFIG_RT_USING_HWTIMER is not set +# CONFIG_RT_USING_CHERRYUSB is not set +# end of Device Drivers + +# +# C/C++ and POSIX layer +# + +# +# ISO-ANSI C layer +# + +# +# Timezone and Daylight Saving Time +# +# CONFIG_RT_LIBC_USING_FULL_TZ_DST is not set +CONFIG_RT_LIBC_USING_LIGHT_TZ_DST=y +CONFIG_RT_LIBC_TZ_DEFAULT_HOUR=8 +CONFIG_RT_LIBC_TZ_DEFAULT_MIN=0 +CONFIG_RT_LIBC_TZ_DEFAULT_SEC=0 +# end of Timezone and Daylight Saving Time +# end of ISO-ANSI C layer + +# +# POSIX (Portable Operating System Interface) layer +# +CONFIG_RT_USING_POSIX_FS=y +CONFIG_RT_USING_POSIX_DEVIO=y +CONFIG_RT_USING_POSIX_STDIO=y +CONFIG_RT_USING_POSIX_POLL=y +CONFIG_RT_USING_POSIX_SELECT=y +# CONFIG_RT_USING_POSIX_EVENTFD is not set +# CONFIG_RT_USING_POSIX_TIMERFD is not set +# CONFIG_RT_USING_POSIX_SOCKET is not set +CONFIG_RT_USING_POSIX_TERMIOS=y +CONFIG_RT_USING_POSIX_AIO=y +CONFIG_RT_USING_POSIX_MMAN=y +CONFIG_RT_USING_POSIX_DELAY=y +CONFIG_RT_USING_POSIX_CLOCK=y +CONFIG_RT_USING_POSIX_TIMER=y +# CONFIG_RT_USING_PTHREADS is not set +# CONFIG_RT_USING_MODULE is not set + +# +# Interprocess Communication (IPC) +# +CONFIG_RT_USING_POSIX_PIPE=y +CONFIG_RT_USING_POSIX_PIPE_SIZE=512 +# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set +# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set + +# +# Socket is in the 'Network' category +# +# end of Interprocess Communication (IPC) +# end of POSIX (Portable Operating System Interface) layer + +CONFIG_RT_USING_CPLUSPLUS=y +# CONFIG_RT_USING_CPLUSPLUS11 is not set +CONFIG_RT_USING_CPP_WRAPPER=y +# CONFIG_RT_USING_CPP_EXCEPTIONS is not set +# end of C/C++ and POSIX layer + +# +# Network +# +CONFIG_RT_USING_SAL=y +CONFIG_SAL_INTERNET_CHECK=y +CONFIG_SOCKET_TABLE_STEP_LEN=4 + +# +# Docking with protocol stacks +# +CONFIG_SAL_USING_LWIP=y +# CONFIG_SAL_USING_AT is not set +# CONFIG_SAL_USING_TLS is not set +# end of Docking with protocol stacks + +CONFIG_SAL_USING_POSIX=y +CONFIG_RT_USING_NETDEV=y +CONFIG_NETDEV_USING_IFCONFIG=y +CONFIG_NETDEV_USING_PING=y +CONFIG_NETDEV_USING_NETSTAT=y +CONFIG_NETDEV_USING_AUTO_DEFAULT=y +# CONFIG_NETDEV_USING_LINK_STATUS_CALLBACK is not set +# CONFIG_NETDEV_USING_IPV6 is not set +CONFIG_NETDEV_IPV4=1 +CONFIG_NETDEV_IPV6=0 +CONFIG_RT_USING_LWIP=y +# CONFIG_RT_USING_LWIP_LOCAL_VERSION is not set +# CONFIG_RT_USING_LWIP141 is not set +CONFIG_RT_USING_LWIP203=y +# CONFIG_RT_USING_LWIP212 is not set +# CONFIG_RT_USING_LWIP_LATEST is not set +CONFIG_RT_USING_LWIP_VER_NUM=0x20003 +# CONFIG_RT_USING_LWIP_IPV6 is not set +CONFIG_RT_LWIP_MEM_ALIGNMENT=4 +CONFIG_RT_LWIP_IGMP=y +CONFIG_RT_LWIP_ICMP=y +# CONFIG_RT_LWIP_SNMP is not set +CONFIG_RT_LWIP_DNS=y +CONFIG_RT_LWIP_DHCP=y +CONFIG_IP_SOF_BROADCAST=1 +CONFIG_IP_SOF_BROADCAST_RECV=1 + +# +# Static IPv4 Address +# +CONFIG_RT_LWIP_IPADDR="192.168.1.30" +CONFIG_RT_LWIP_GWADDR="192.168.1.1" +CONFIG_RT_LWIP_MSKADDR="255.255.255.0" +# end of Static IPv4 Address + +CONFIG_RT_LWIP_UDP=y +CONFIG_RT_LWIP_TCP=y +CONFIG_RT_LWIP_RAW=y +# CONFIG_RT_LWIP_PPP is not set +CONFIG_RT_MEMP_NUM_NETCONN=8 +CONFIG_RT_LWIP_PBUF_NUM=16 +CONFIG_RT_LWIP_RAW_PCB_NUM=4 +CONFIG_RT_LWIP_UDP_PCB_NUM=4 +CONFIG_RT_LWIP_TCP_PCB_NUM=4 +CONFIG_RT_LWIP_TCP_SEG_NUM=40 +CONFIG_RT_LWIP_TCP_SND_BUF=8196 +CONFIG_RT_LWIP_TCP_WND=8196 +CONFIG_RT_LWIP_TCPTHREAD_PRIORITY=10 +CONFIG_RT_LWIP_TCPTHREAD_MBOX_SIZE=8 +CONFIG_RT_LWIP_TCPTHREAD_STACKSIZE=8192 +# CONFIG_LWIP_NO_RX_THREAD is not set +# CONFIG_LWIP_NO_TX_THREAD is not set +CONFIG_RT_LWIP_ETHTHREAD_PRIORITY=12 +CONFIG_RT_LWIP_ETHTHREAD_STACKSIZE=8192 +CONFIG_RT_LWIP_ETHTHREAD_MBOX_SIZE=8 +# CONFIG_RT_LWIP_REASSEMBLY_FRAG is not set +CONFIG_LWIP_NETIF_STATUS_CALLBACK=1 +CONFIG_LWIP_NETIF_LINK_CALLBACK=1 +CONFIG_RT_LWIP_NETIF_NAMESIZE=6 +CONFIG_SO_REUSE=1 +CONFIG_LWIP_SO_RCVTIMEO=1 +CONFIG_LWIP_SO_SNDTIMEO=1 +CONFIG_LWIP_SO_RCVBUF=1 +CONFIG_LWIP_SO_LINGER=0 +# CONFIG_RT_LWIP_NETIF_LOOPBACK is not set +CONFIG_LWIP_NETIF_LOOPBACK=0 +# CONFIG_RT_LWIP_STATS is not set +# CONFIG_RT_LWIP_USING_HW_CHECKSUM is not set +CONFIG_RT_LWIP_USING_PING=y +# CONFIG_LWIP_USING_DHCPD is not set +# CONFIG_RT_LWIP_ENABLE_USER_HOOKS is not set +# CONFIG_RT_LWIP_DEBUG is not set +# CONFIG_RT_USING_AT is not set +# end of Network + +# +# Memory protection +# +# CONFIG_RT_USING_MEM_PROTECTION is not set +# CONFIG_RT_USING_HW_STACK_GUARD is not set +# end of Memory protection + +# +# Utilities +# +# CONFIG_RT_USING_RYM is not set +# CONFIG_RT_USING_ULOG is not set +CONFIG_RT_USING_UTEST=y +CONFIG_UTEST_THR_STACK_SIZE=4096 +CONFIG_UTEST_THR_PRIORITY=20 +# CONFIG_RT_UTEST_USING_AUTO_RUN is not set +CONFIG_RT_UTEST_MAX_OPTIONS=64 +# CONFIG_RT_USING_VAR_EXPORT is not set +CONFIG_RT_USING_RESOURCE_ID=y +CONFIG_RT_USING_ADT=y +CONFIG_RT_USING_ADT_AVL=y +CONFIG_RT_USING_ADT_BITMAP=y +CONFIG_RT_USING_ADT_HASHMAP=y +CONFIG_RT_USING_ADT_REF=y +# CONFIG_RT_USING_RT_LINK is not set +# end of Utilities + +# +# Memory management +# +# CONFIG_RT_PAGE_MPR_SIZE_DYNAMIC is not set +CONFIG_RT_PAGE_AFFINITY_BLOCK_SIZE=0x1000 +CONFIG_RT_PAGE_MAX_ORDER=11 +# CONFIG_RT_USING_MEMBLOCK is not set + +# +# Debugging +# +# CONFIG_RT_DEBUGGING_ALIASING is not set +# CONFIG_RT_DEBUGING_PAGE_LEAK is not set +# CONFIG_RT_DEBUGGING_PAGE_POISON is not set +# end of Debugging +# end of Memory management + +# +# Using USB legacy version +# +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set +# end of Using USB legacy version + +# CONFIG_RT_USING_FDT is not set +# CONFIG_RT_USING_RUST is not set +# end of RT-Thread Components + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set +# end of RT-Thread Utestcases + +# +# RT-Thread online packages +# + +# +# IoT - internet of things +# +# CONFIG_PKG_USING_LORAWAN_DRIVER is not set +# CONFIG_PKG_USING_PAHOMQTT is not set +# CONFIG_PKG_USING_UMQTT is not set +# CONFIG_PKG_USING_WEBCLIENT is not set +# CONFIG_PKG_USING_WEBNET is not set +# CONFIG_PKG_USING_MONGOOSE is not set +# CONFIG_PKG_USING_MYMQTT is not set +# CONFIG_PKG_USING_KAWAII_MQTT is not set +# CONFIG_PKG_USING_BC28_MQTT is not set +# CONFIG_PKG_USING_WEBTERMINAL is not set +# CONFIG_PKG_USING_FREEMODBUS is not set +# CONFIG_PKG_USING_NANOPB is not set +# CONFIG_PKG_USING_WIFI_HOST_DRIVER is not set +# CONFIG_PKG_USING_ESP_HOSTED is not set + +# +# Wi-Fi +# + +# +# Marvell WiFi +# +# CONFIG_PKG_USING_WLANMARVELL is not set +# end of Marvell WiFi + +# +# Wiced WiFi +# +# CONFIG_PKG_USING_WLAN_WICED is not set +# end of Wiced WiFi + +# CONFIG_PKG_USING_RW007 is not set + +# +# CYW43012 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43012 is not set +# end of CYW43012 WiFi + +# +# BL808 WiFi +# +# CONFIG_PKG_USING_WLAN_BL808 is not set +# end of BL808 WiFi + +# +# CYW43439 WiFi +# +# CONFIG_PKG_USING_WLAN_CYW43439 is not set +# end of CYW43439 WiFi +# end of Wi-Fi + +# CONFIG_PKG_USING_COAP is not set +# CONFIG_PKG_USING_NOPOLL is not set +# CONFIG_PKG_USING_NETUTILS is not set +# CONFIG_PKG_USING_CMUX is not set +# CONFIG_PKG_USING_PPP_DEVICE is not set +# CONFIG_PKG_USING_AT_DEVICE is not set +# CONFIG_PKG_USING_ATSRV_SOCKET is not set +# CONFIG_PKG_USING_WIZNET is not set +# CONFIG_PKG_USING_ZB_COORDINATOR is not set + +# +# IoT Cloud +# +# CONFIG_PKG_USING_ONENET is not set +# CONFIG_PKG_USING_GAGENT_CLOUD is not set +# CONFIG_PKG_USING_ALI_IOTKIT is not set +# CONFIG_PKG_USING_AZURE is not set +# CONFIG_PKG_USING_TENCENT_IOT_EXPLORER is not set +# CONFIG_PKG_USING_JIOT-C-SDK is not set +# CONFIG_PKG_USING_UCLOUD_IOT_SDK is not set +# CONFIG_PKG_USING_JOYLINK is not set +# CONFIG_PKG_USING_IOTSHARP_SDK is not set +# end of IoT Cloud + +# CONFIG_PKG_USING_NIMBLE is not set +# CONFIG_PKG_USING_LLSYNC_SDK_ADAPTER is not set +# CONFIG_PKG_USING_OTA_DOWNLOADER is not set +# CONFIG_PKG_USING_IPMSG is not set +# CONFIG_PKG_USING_LSSDP is not set +# CONFIG_PKG_USING_AIRKISS_OPEN is not set +# CONFIG_PKG_USING_LIBRWS is not set +# CONFIG_PKG_USING_TCPSERVER is not set +# CONFIG_PKG_USING_PROTOBUF_C is not set +# CONFIG_PKG_USING_DLT645 is not set +# CONFIG_PKG_USING_QXWZ is not set +# CONFIG_PKG_USING_SMTP_CLIENT is not set +# CONFIG_PKG_USING_ABUP_FOTA is not set +# CONFIG_PKG_USING_LIBCURL2RTT is not set +# CONFIG_PKG_USING_CAPNP is not set +# CONFIG_PKG_USING_AGILE_TELNET is not set +# CONFIG_PKG_USING_NMEALIB is not set +# CONFIG_PKG_USING_PDULIB is not set +# CONFIG_PKG_USING_BTSTACK is not set +# CONFIG_PKG_USING_BT_CYW43012 is not set +# CONFIG_PKG_USING_CYW43XX is not set +# CONFIG_PKG_USING_LORAWAN_ED_STACK is not set +# CONFIG_PKG_USING_WAYZ_IOTKIT is not set +# CONFIG_PKG_USING_MAVLINK is not set +# CONFIG_PKG_USING_BSAL is not set +# CONFIG_PKG_USING_AGILE_MODBUS is not set +# CONFIG_PKG_USING_AGILE_FTP is not set +# CONFIG_PKG_USING_EMBEDDEDPROTO is not set +# CONFIG_PKG_USING_RT_LINK_HW is not set +# CONFIG_PKG_USING_RYANMQTT is not set +# CONFIG_PKG_USING_RYANW5500 is not set +# CONFIG_PKG_USING_LORA_PKT_FWD is not set +# CONFIG_PKG_USING_LORA_GW_DRIVER_LIB is not set +# CONFIG_PKG_USING_LORA_PKT_SNIFFER is not set +# CONFIG_PKG_USING_HM is not set +# CONFIG_PKG_USING_SMALL_MODBUS is not set +# CONFIG_PKG_USING_NET_SERVER is not set +# CONFIG_PKG_USING_ZFTP is not set +# CONFIG_PKG_USING_WOL is not set +# CONFIG_PKG_USING_ZEPHYR_POLLING is not set +# CONFIG_PKG_USING_MATTER_ADAPTATION_LAYER is not set +# CONFIG_PKG_USING_LHC_MODBUS is not set +# CONFIG_PKG_USING_QMODBUS is not set +# CONFIG_PKG_USING_PNET is not set +# CONFIG_PKG_USING_OPENER is not set +# CONFIG_PKG_USING_FREEMQTT is not set +# end of IoT - internet of things + +# +# security packages +# +# CONFIG_PKG_USING_MBEDTLS is not set +# CONFIG_PKG_USING_LIBSODIUM is not set +# CONFIG_PKG_USING_LIBHYDROGEN is not set +# CONFIG_PKG_USING_TINYCRYPT is not set +# CONFIG_PKG_USING_TFM is not set +# CONFIG_PKG_USING_YD_CRYPTO is not set +# end of security packages + +# +# language packages +# + +# +# JSON: JavaScript Object Notation, a lightweight data-interchange format +# +# CONFIG_PKG_USING_CJSON is not set +# CONFIG_PKG_USING_LJSON is not set +# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set +# CONFIG_PKG_USING_RAPIDJSON is not set +# CONFIG_PKG_USING_JSMN is not set +# CONFIG_PKG_USING_AGILE_JSMN is not set +# CONFIG_PKG_USING_PARSON is not set +# CONFIG_PKG_USING_RYAN_JSON is not set +# end of JSON: JavaScript Object Notation, a lightweight data-interchange format + +# +# XML: Extensible Markup Language +# +# CONFIG_PKG_USING_SIMPLE_XML is not set +# CONFIG_PKG_USING_EZXML is not set +# end of XML: Extensible Markup Language + +# CONFIG_PKG_USING_LUATOS_SOC is not set +# CONFIG_PKG_USING_LUA is not set +# CONFIG_PKG_USING_JERRYSCRIPT is not set +# CONFIG_PKG_USING_MICROPYTHON is not set +# CONFIG_PKG_USING_PIKASCRIPT is not set +# CONFIG_PKG_USING_RTT_RUST is not set +# end of language packages + +# +# multimedia packages +# + +# +# LVGL: powerful and easy-to-use embedded GUI library +# +# CONFIG_PKG_USING_LVGL is not set +# CONFIG_PKG_USING_LV_MUSIC_DEMO is not set +# CONFIG_PKG_USING_GUI_GUIDER_DEMO is not set +# end of LVGL: powerful and easy-to-use embedded GUI library + +# +# u8g2: a monochrome graphic library +# +# CONFIG_PKG_USING_U8G2_OFFICIAL is not set +# CONFIG_PKG_USING_U8G2 is not set +# end of u8g2: a monochrome graphic library + +# CONFIG_PKG_USING_OPENMV is not set +# CONFIG_PKG_USING_MUPDF is not set +# CONFIG_PKG_USING_STEMWIN is not set +# CONFIG_PKG_USING_WAVPLAYER is not set +# CONFIG_PKG_USING_TJPGD is not set +# CONFIG_PKG_USING_PDFGEN is not set +# CONFIG_PKG_USING_HELIX is not set +# CONFIG_PKG_USING_AZUREGUIX is not set +# CONFIG_PKG_USING_TOUCHGFX2RTT is not set +# CONFIG_PKG_USING_NUEMWIN is not set +# CONFIG_PKG_USING_MP3PLAYER is not set +# CONFIG_PKG_USING_TINYJPEG is not set +# CONFIG_PKG_USING_UGUI is not set +# CONFIG_PKG_USING_MCURSES is not set +# CONFIG_PKG_USING_TERMBOX is not set +# CONFIG_PKG_USING_VT100 is not set +# CONFIG_PKG_USING_QRCODE is not set +# CONFIG_PKG_USING_GUIENGINE is not set +# CONFIG_PKG_USING_3GPP_AMRNB is not set +# end of multimedia packages + +# +# tools packages +# +# CONFIG_PKG_USING_CMBACKTRACE is not set +# CONFIG_PKG_USING_MCOREDUMP is not set +# CONFIG_PKG_USING_EASYFLASH is not set +# CONFIG_PKG_USING_EASYLOGGER is not set +# CONFIG_PKG_USING_SYSTEMVIEW is not set +# CONFIG_PKG_USING_SEGGER_RTT is not set +# CONFIG_PKG_USING_RTT_AUTO_EXE_CMD is not set +# CONFIG_PKG_USING_RDB is not set +# CONFIG_PKG_USING_ULOG_EASYFLASH is not set +# CONFIG_PKG_USING_LOGMGR is not set +# CONFIG_PKG_USING_ADBD is not set +# CONFIG_PKG_USING_COREMARK is not set +# CONFIG_PKG_USING_DHRYSTONE is not set +# CONFIG_PKG_USING_MEMORYPERF is not set +# CONFIG_PKG_USING_NR_MICRO_SHELL is not set +# CONFIG_PKG_USING_CHINESE_FONT_LIBRARY is not set +# CONFIG_PKG_USING_LUNAR_CALENDAR is not set +# CONFIG_PKG_USING_BS8116A is not set +# CONFIG_PKG_USING_GPS_RMC is not set +# CONFIG_PKG_USING_URLENCODE is not set +# CONFIG_PKG_USING_UMCN is not set +# CONFIG_PKG_USING_LWRB2RTT is not set +# CONFIG_PKG_USING_CPU_USAGE is not set +# CONFIG_PKG_USING_GBK2UTF8 is not set +# CONFIG_PKG_USING_VCONSOLE is not set +# CONFIG_PKG_USING_KDB is not set +# CONFIG_PKG_USING_WAMR is not set +# CONFIG_PKG_USING_MICRO_XRCE_DDS_CLIENT is not set +# CONFIG_PKG_USING_LWLOG is not set +# CONFIG_PKG_USING_ANV_TRACE is not set +# CONFIG_PKG_USING_ANV_MEMLEAK is not set +# CONFIG_PKG_USING_ANV_TESTSUIT is not set +# CONFIG_PKG_USING_ANV_BENCH is not set +# CONFIG_PKG_USING_DEVMEM is not set +# CONFIG_PKG_USING_REGEX is not set +# CONFIG_PKG_USING_MEM_SANDBOX is not set +# CONFIG_PKG_USING_SOLAR_TERMS is not set +# CONFIG_PKG_USING_GAN_ZHI is not set +# CONFIG_PKG_USING_FDT is not set +# CONFIG_PKG_USING_CBOX is not set +# CONFIG_PKG_USING_SNOWFLAKE is not set +# CONFIG_PKG_USING_HASH_MATCH is not set +# CONFIG_PKG_USING_ARMV7M_DWT_TOOL is not set +# CONFIG_PKG_USING_VOFA_PLUS is not set +# CONFIG_PKG_USING_ZDEBUG is not set +# CONFIG_PKG_USING_RVBACKTRACE is not set +# CONFIG_PKG_USING_HPATCHLITE is not set +# CONFIG_PKG_USING_THREAD_METRIC is not set +# end of tools packages + +# +# system packages +# + +# +# enhanced kernel services +# +# CONFIG_PKG_USING_RT_MEMCPY_CM is not set +# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set +# end of enhanced kernel services + +# CONFIG_PKG_USING_AUNITY is not set + +# +# acceleration: Assembly language or algorithmic acceleration packages +# +# CONFIG_PKG_USING_QFPLIB_M0_FULL is not set +# CONFIG_PKG_USING_QFPLIB_M0_TINY is not set +# CONFIG_PKG_USING_QFPLIB_M3 is not set +# end of acceleration: Assembly language or algorithmic acceleration packages + +# +# CMSIS: ARM Cortex-M Microcontroller Software Interface Standard +# +# CONFIG_PKG_USING_CMSIS_5 is not set +# CONFIG_PKG_USING_CMSIS_CORE is not set +# CONFIG_PKG_USING_CMSIS_NN is not set +# CONFIG_PKG_USING_CMSIS_RTOS1 is not set +# CONFIG_PKG_USING_CMSIS_RTOS2 is not set +# end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard + +# +# Micrium: Micrium software products porting for RT-Thread +# +# CONFIG_PKG_USING_UCOSIII_WRAPPER is not set +# CONFIG_PKG_USING_UCOSII_WRAPPER is not set +# CONFIG_PKG_USING_UC_CRC is not set +# CONFIG_PKG_USING_UC_CLK is not set +# CONFIG_PKG_USING_UC_COMMON is not set +# CONFIG_PKG_USING_UC_MODBUS is not set +# end of Micrium: Micrium software products porting for RT-Thread + +# CONFIG_PKG_USING_FREERTOS_WRAPPER is not set +# CONFIG_PKG_USING_LITEOS_SDK is not set +# CONFIG_PKG_USING_TZ_DATABASE is not set +# CONFIG_PKG_USING_CAIRO is not set +# CONFIG_PKG_USING_PIXMAN is not set +# CONFIG_PKG_USING_PARTITION is not set +# CONFIG_PKG_USING_PERF_COUNTER is not set +# CONFIG_PKG_USING_FILEX is not set +# CONFIG_PKG_USING_LEVELX is not set +# CONFIG_PKG_USING_FLASHDB is not set +# CONFIG_PKG_USING_SQLITE is not set +# CONFIG_PKG_USING_RTI is not set +# CONFIG_PKG_USING_DFS_YAFFS is not set +# CONFIG_PKG_USING_LITTLEFS is not set +# CONFIG_PKG_USING_DFS_JFFS2 is not set +# CONFIG_PKG_USING_DFS_UFFS is not set +# CONFIG_PKG_USING_LWEXT4 is not set +# CONFIG_PKG_USING_THREAD_POOL is not set +# CONFIG_PKG_USING_ROBOTS is not set +# CONFIG_PKG_USING_EV is not set +# CONFIG_PKG_USING_SYSWATCH is not set +# CONFIG_PKG_USING_SYS_LOAD_MONITOR is not set +# CONFIG_PKG_USING_PLCCORE is not set +# CONFIG_PKG_USING_RAMDISK is not set +# CONFIG_PKG_USING_MININI is not set +# CONFIG_PKG_USING_QBOOT is not set +# CONFIG_PKG_USING_PPOOL is not set +# CONFIG_PKG_USING_OPENAMP is not set +# CONFIG_PKG_USING_RPMSG_LITE is not set +# CONFIG_PKG_USING_LPM is not set +# CONFIG_PKG_USING_TLSF is not set +# CONFIG_PKG_USING_EVENT_RECORDER is not set +# CONFIG_PKG_USING_ARM_2D is not set +# CONFIG_PKG_USING_MCUBOOT is not set +# CONFIG_PKG_USING_TINYUSB is not set +# CONFIG_PKG_USING_KMULTI_RTIMER is not set +# CONFIG_PKG_USING_TFDB is not set +# CONFIG_PKG_USING_QPC is not set +# CONFIG_PKG_USING_AGILE_UPGRADE is not set +# CONFIG_PKG_USING_FLASH_BLOB is not set +# CONFIG_PKG_USING_MLIBC is not set +# CONFIG_PKG_USING_TASK_MSG_BUS is not set +# CONFIG_PKG_USING_UART_FRAMEWORK is not set +# CONFIG_PKG_USING_SFDB is not set +# CONFIG_PKG_USING_RTP is not set +# CONFIG_PKG_USING_REB is not set +# CONFIG_PKG_USING_RMP is not set +# CONFIG_PKG_USING_R_RHEALSTONE is not set +# CONFIG_PKG_USING_HEARTBEAT is not set +# CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set +# end of system packages + +# +# peripheral libraries and drivers +# + +# +# HAL & SDK Drivers +# + +# +# STM32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_STM32F0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F1_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F2_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32F7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32G4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32H7RS_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L0_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32L5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32U5_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB55_SDK is not set +# CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_STM32WL_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WL_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32WB_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_HAL_DRIVER is not set +# CONFIG_PKG_USING_STM32MP1_M4_CMSIS_DRIVER is not set +# end of STM32 HAL & SDK Drivers + +# +# Infineon HAL Packages +# +# CONFIG_PKG_USING_INFINEON_CAT1CM0P is not set +# CONFIG_PKG_USING_INFINEON_CMSIS is not set +# CONFIG_PKG_USING_INFINEON_CORE_LIB is not set +# CONFIG_PKG_USING_INFINEON_MTB_HAL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_MTB_PDL_CAT1 is not set +# CONFIG_PKG_USING_INFINEON_RETARGET_IO is not set +# CONFIG_PKG_USING_INFINEON_CAPSENSE is not set +# CONFIG_PKG_USING_INFINEON_CSDIDAC is not set +# CONFIG_PKG_USING_INFINEON_SERIAL_FLASH is not set +# CONFIG_PKG_USING_INFINEON_USBDEV is not set +# end of Infineon HAL Packages + +# CONFIG_PKG_USING_BLUETRUM_SDK is not set +# CONFIG_PKG_USING_EMBARC_BSP is not set +# CONFIG_PKG_USING_ESP_IDF is not set + +# +# Kendryte SDK +# +# CONFIG_PKG_USING_K210_SDK is not set +# CONFIG_PKG_USING_KENDRYTE_SDK is not set +# end of Kendryte SDK + +# CONFIG_PKG_USING_NRF5X_SDK is not set +# CONFIG_PKG_USING_NRFX is not set +# CONFIG_PKG_USING_NUCLEI_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_RP2350_SDK is not set +# CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set +# CONFIG_PKG_USING_MM32 is not set + +# +# WCH HAL & SDK Drivers +# +# CONFIG_PKG_USING_CH32V20x_SDK is not set +# CONFIG_PKG_USING_CH32V307_SDK is not set +# end of WCH HAL & SDK Drivers + +# +# AT32 HAL & SDK Drivers +# +# CONFIG_PKG_USING_AT32A403A_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A403A_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32A423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F45x_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F402_405_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F403A_407_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F413_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F415_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F421_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F423_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F425_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32F435_437_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_HAL_DRIVER is not set +# CONFIG_PKG_USING_AT32M412_416_CMSIS_DRIVER is not set +# end of AT32 HAL & SDK Drivers + +# +# HC32 DDL Drivers +# +# CONFIG_PKG_USING_HC32F3_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F3_SERIES_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_HC32F4_SERIES_DRIVER is not set +# end of HC32 DDL Drivers + +# +# NXP HAL & SDK Drivers +# +# CONFIG_PKG_USING_NXP_MCX_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_NXP_MCX_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC_DRIVER is not set +# CONFIG_PKG_USING_NXP_LPC55S_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6SX_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMX6UL_DRIVER is not set +# CONFIG_PKG_USING_NXP_IMXRT_DRIVER is not set +# end of NXP HAL & SDK Drivers + +# +# NUVOTON Drivers +# +# CONFIG_PKG_USING_NUVOTON_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_NUVOTON_SERIES_DRIVER is not set +# CONFIG_PKG_USING_NUVOTON_ARM926_LIB is not set +# end of NUVOTON Drivers + +# +# GD32 Drivers +# +# CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER is not set +# CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER is not set +# end of GD32 Drivers +# end of HAL & SDK Drivers + +# +# sensors drivers +# +# CONFIG_PKG_USING_LSM6DSM is not set +# CONFIG_PKG_USING_LSM6DSL is not set +# CONFIG_PKG_USING_LPS22HB is not set +# CONFIG_PKG_USING_HTS221 is not set +# CONFIG_PKG_USING_LSM303AGR is not set +# CONFIG_PKG_USING_BME280 is not set +# CONFIG_PKG_USING_BME680 is not set +# CONFIG_PKG_USING_BMA400 is not set +# CONFIG_PKG_USING_BMI160_BMX160 is not set +# CONFIG_PKG_USING_SPL0601 is not set +# CONFIG_PKG_USING_MS5805 is not set +# CONFIG_PKG_USING_DA270 is not set +# CONFIG_PKG_USING_DF220 is not set +# CONFIG_PKG_USING_HSHCAL001 is not set +# CONFIG_PKG_USING_BH1750 is not set +# CONFIG_PKG_USING_MPU6XXX is not set +# CONFIG_PKG_USING_AHT10 is not set +# CONFIG_PKG_USING_AP3216C is not set +# CONFIG_PKG_USING_TSL4531 is not set +# CONFIG_PKG_USING_DS18B20 is not set +# CONFIG_PKG_USING_DHT11 is not set +# CONFIG_PKG_USING_DHTXX is not set +# CONFIG_PKG_USING_GY271 is not set +# CONFIG_PKG_USING_GP2Y10 is not set +# CONFIG_PKG_USING_SGP30 is not set +# CONFIG_PKG_USING_HDC1000 is not set +# CONFIG_PKG_USING_BMP180 is not set +# CONFIG_PKG_USING_BMP280 is not set +# CONFIG_PKG_USING_SHTC1 is not set +# CONFIG_PKG_USING_BMI088 is not set +# CONFIG_PKG_USING_HMC5883 is not set +# CONFIG_PKG_USING_MAX6675 is not set +# CONFIG_PKG_USING_MAX31855 is not set +# CONFIG_PKG_USING_TMP1075 is not set +# CONFIG_PKG_USING_SR04 is not set +# CONFIG_PKG_USING_CCS811 is not set +# CONFIG_PKG_USING_PMSXX is not set +# CONFIG_PKG_USING_RT3020 is not set +# CONFIG_PKG_USING_MLX90632 is not set +# CONFIG_PKG_USING_MLX90382 is not set +# CONFIG_PKG_USING_MLX90393 is not set +# CONFIG_PKG_USING_MLX90392 is not set +# CONFIG_PKG_USING_MLX90394 is not set +# CONFIG_PKG_USING_MLX90397 is not set +# CONFIG_PKG_USING_MS5611 is not set +# CONFIG_PKG_USING_MAX31865 is not set +# CONFIG_PKG_USING_VL53L0X is not set +# CONFIG_PKG_USING_INA260 is not set +# CONFIG_PKG_USING_MAX30102 is not set +# CONFIG_PKG_USING_INA226 is not set +# CONFIG_PKG_USING_LIS2DH12 is not set +# CONFIG_PKG_USING_HS300X is not set +# CONFIG_PKG_USING_ZMOD4410 is not set +# CONFIG_PKG_USING_ISL29035 is not set +# CONFIG_PKG_USING_MMC3680KJ is not set +# CONFIG_PKG_USING_QMP6989 is not set +# CONFIG_PKG_USING_BALANCE is not set +# CONFIG_PKG_USING_SHT2X is not set +# CONFIG_PKG_USING_SHT3X is not set +# CONFIG_PKG_USING_SHT4X is not set +# CONFIG_PKG_USING_AD7746 is not set +# CONFIG_PKG_USING_ADT74XX is not set +# CONFIG_PKG_USING_MAX17048 is not set +# CONFIG_PKG_USING_AS7341 is not set +# CONFIG_PKG_USING_CW2015 is not set +# CONFIG_PKG_USING_ICM20608 is not set +# CONFIG_PKG_USING_PAJ7620 is not set +# CONFIG_PKG_USING_STHS34PF80 is not set +# CONFIG_PKG_USING_P3T1755 is not set +# CONFIG_PKG_USING_QMI8658 is not set +# CONFIG_PKG_USING_ICM20948 is not set +# end of sensors drivers + +# +# touch drivers +# +# CONFIG_PKG_USING_GT9147 is not set +# CONFIG_PKG_USING_GT1151 is not set +# CONFIG_PKG_USING_GT917S is not set +# CONFIG_PKG_USING_GT911 is not set +# CONFIG_PKG_USING_FT6206 is not set +# CONFIG_PKG_USING_FT5426 is not set +# CONFIG_PKG_USING_FT6236 is not set +# CONFIG_PKG_USING_XPT2046_TOUCH is not set +# CONFIG_PKG_USING_CST816X is not set +# CONFIG_PKG_USING_CST812T is not set +# end of touch drivers + +# CONFIG_PKG_USING_REALTEK_AMEBA is not set +# CONFIG_PKG_USING_BUTTON is not set +# CONFIG_PKG_USING_PCF8574 is not set +# CONFIG_PKG_USING_SX12XX is not set +# CONFIG_PKG_USING_SIGNAL_LED is not set +# CONFIG_PKG_USING_LEDBLINK is not set +# CONFIG_PKG_USING_LITTLED is not set +# CONFIG_PKG_USING_LKDGUI is not set +# CONFIG_PKG_USING_INFRARED is not set +# CONFIG_PKG_USING_MULTI_INFRARED is not set +# CONFIG_PKG_USING_AGILE_BUTTON is not set +# CONFIG_PKG_USING_AGILE_LED is not set +# CONFIG_PKG_USING_AT24CXX is not set +# CONFIG_PKG_USING_MOTIONDRIVER2RTT is not set +# CONFIG_PKG_USING_PCA9685 is not set +# CONFIG_PKG_USING_ILI9341 is not set +# CONFIG_PKG_USING_I2C_TOOLS is not set +# CONFIG_PKG_USING_NRF24L01 is not set +# CONFIG_PKG_USING_RPLIDAR is not set +# CONFIG_PKG_USING_AS608 is not set +# CONFIG_PKG_USING_RC522 is not set +# CONFIG_PKG_USING_WS2812B is not set +# CONFIG_PKG_USING_EXTERN_RTC_DRIVERS is not set +# CONFIG_PKG_USING_MULTI_RTIMER is not set +# CONFIG_PKG_USING_MAX7219 is not set +# CONFIG_PKG_USING_BEEP is not set +# CONFIG_PKG_USING_EASYBLINK is not set +# CONFIG_PKG_USING_PMS_SERIES is not set +# CONFIG_PKG_USING_CAN_YMODEM is not set +# CONFIG_PKG_USING_LORA_RADIO_DRIVER is not set +# CONFIG_PKG_USING_QLED is not set +# CONFIG_PKG_USING_AGILE_CONSOLE is not set +# CONFIG_PKG_USING_LD3320 is not set +# CONFIG_PKG_USING_WK2124 is not set +# CONFIG_PKG_USING_LY68L6400 is not set +# CONFIG_PKG_USING_DM9051 is not set +# CONFIG_PKG_USING_SSD1306 is not set +# CONFIG_PKG_USING_QKEY is not set +# CONFIG_PKG_USING_RS485 is not set +# CONFIG_PKG_USING_RS232 is not set +# CONFIG_PKG_USING_NES is not set +# CONFIG_PKG_USING_VIRTUAL_SENSOR is not set +# CONFIG_PKG_USING_VDEVICE is not set +# CONFIG_PKG_USING_SGM706 is not set +# CONFIG_PKG_USING_RDA58XX is not set +# CONFIG_PKG_USING_LIBNFC is not set +# CONFIG_PKG_USING_MFOC is not set +# CONFIG_PKG_USING_TMC51XX is not set +# CONFIG_PKG_USING_TCA9534 is not set +# CONFIG_PKG_USING_KOBUKI is not set +# CONFIG_PKG_USING_ROSSERIAL is not set +# CONFIG_PKG_USING_MICRO_ROS is not set +# CONFIG_PKG_USING_MCP23008 is not set +# CONFIG_PKG_USING_MISAKA_AT24CXX is not set +# CONFIG_PKG_USING_MISAKA_RGB_BLING is not set +# CONFIG_PKG_USING_LORA_MODEM_DRIVER is not set +# CONFIG_PKG_USING_SOFT_SERIAL is not set +# CONFIG_PKG_USING_MB85RS16 is not set +# CONFIG_PKG_USING_RFM300 is not set +# CONFIG_PKG_USING_IO_INPUT_FILTER is not set +# CONFIG_PKG_USING_LRF_NV7LIDAR is not set +# CONFIG_PKG_USING_AIP650 is not set +# CONFIG_PKG_USING_FINGERPRINT is not set +# CONFIG_PKG_USING_BT_ECB02C is not set +# CONFIG_PKG_USING_UAT is not set +# CONFIG_PKG_USING_ST7789 is not set +# CONFIG_PKG_USING_VS1003 is not set +# CONFIG_PKG_USING_X9555 is not set +# CONFIG_PKG_USING_SYSTEM_RUN_LED is not set +# CONFIG_PKG_USING_BT_MX01 is not set +# CONFIG_PKG_USING_RGPOWER is not set +# CONFIG_PKG_USING_BT_MX02 is not set +# CONFIG_PKG_USING_GC9A01 is not set +# CONFIG_PKG_USING_IK485 is not set +# CONFIG_PKG_USING_SERVO is not set +# CONFIG_PKG_USING_SEAN_WS2812B is not set +# CONFIG_PKG_USING_IC74HC165 is not set +# CONFIG_PKG_USING_IST8310 is not set +# CONFIG_PKG_USING_ST7789_SPI is not set +# CONFIG_PKG_USING_SPI_TOOLS is not set +# end of peripheral libraries and drivers + +# +# AI packages +# +# CONFIG_PKG_USING_LIBANN is not set +# CONFIG_PKG_USING_NNOM is not set +# CONFIG_PKG_USING_ONNX_BACKEND is not set +# CONFIG_PKG_USING_ONNX_PARSER is not set +# CONFIG_PKG_USING_TENSORFLOWLITEMICRO is not set +# CONFIG_PKG_USING_ELAPACK is not set +# CONFIG_PKG_USING_ULAPACK is not set +# CONFIG_PKG_USING_QUEST is not set +# CONFIG_PKG_USING_NAXOS is not set +# CONFIG_PKG_USING_R_TINYMAIX is not set +# CONFIG_PKG_USING_LLMCHAT is not set +# end of AI packages + +# +# Signal Processing and Control Algorithm Packages +# +# CONFIG_PKG_USING_APID is not set +# CONFIG_PKG_USING_FIRE_PID_CURVE is not set +# CONFIG_PKG_USING_QPID is not set +# CONFIG_PKG_USING_UKAL is not set +# CONFIG_PKG_USING_DIGITALCTRL is not set +# CONFIG_PKG_USING_KISSFFT is not set +# end of Signal Processing and Control Algorithm Packages + +# +# miscellaneous packages +# + +# +# project laboratory +# +# end of project laboratory + +# +# samples: kernel and components samples +# +# CONFIG_PKG_USING_KERNEL_SAMPLES is not set +# CONFIG_PKG_USING_FILESYSTEM_SAMPLES is not set +# CONFIG_PKG_USING_NETWORK_SAMPLES is not set +# CONFIG_PKG_USING_PERIPHERAL_SAMPLES is not set +# end of samples: kernel and components samples + +# +# entertainment: terminal games and other interesting software packages +# +# CONFIG_PKG_USING_CMATRIX is not set +# CONFIG_PKG_USING_SL is not set +# CONFIG_PKG_USING_CAL is not set +# CONFIG_PKG_USING_ACLOCK is not set +# CONFIG_PKG_USING_THREES is not set +# CONFIG_PKG_USING_2048 is not set +# CONFIG_PKG_USING_SNAKE is not set +# CONFIG_PKG_USING_TETRIS is not set +# CONFIG_PKG_USING_DONUT is not set +# CONFIG_PKG_USING_COWSAY is not set +# CONFIG_PKG_USING_MORSE is not set +# end of entertainment: terminal games and other interesting software packages + +# CONFIG_PKG_USING_LIBCSV is not set +# CONFIG_PKG_USING_OPTPARSE is not set +# CONFIG_PKG_USING_FASTLZ is not set +# CONFIG_PKG_USING_MINILZO is not set +# CONFIG_PKG_USING_QUICKLZ is not set +# CONFIG_PKG_USING_LZMA is not set +# CONFIG_PKG_USING_RALARAM is not set +# CONFIG_PKG_USING_MULTIBUTTON is not set +# CONFIG_PKG_USING_FLEXIBLE_BUTTON is not set +# CONFIG_PKG_USING_CANFESTIVAL is not set +# CONFIG_PKG_USING_ZLIB is not set +# CONFIG_PKG_USING_MINIZIP is not set +# CONFIG_PKG_USING_HEATSHRINK is not set +# CONFIG_PKG_USING_DSTR is not set +# CONFIG_PKG_USING_TINYFRAME is not set +# CONFIG_PKG_USING_KENDRYTE_DEMO is not set +# CONFIG_PKG_USING_UPACKER is not set +# CONFIG_PKG_USING_UPARAM is not set +# CONFIG_PKG_USING_HELLO is not set +# CONFIG_PKG_USING_VI is not set +# CONFIG_PKG_USING_KI is not set +# CONFIG_PKG_USING_ARMv7M_DWT is not set +# CONFIG_PKG_USING_CRCLIB is not set +# CONFIG_PKG_USING_LIBCRC is not set +# CONFIG_PKG_USING_LWGPS is not set +# CONFIG_PKG_USING_STATE_MACHINE is not set +# CONFIG_PKG_USING_DESIGN_PATTERN is not set +# CONFIG_PKG_USING_CONTROLLER is not set +# CONFIG_PKG_USING_PHASE_LOCKED_LOOP is not set +# CONFIG_PKG_USING_MFBD is not set +# CONFIG_PKG_USING_SLCAN2RTT is not set +# CONFIG_PKG_USING_SOEM is not set +# CONFIG_PKG_USING_QPARAM is not set +# CONFIG_PKG_USING_CorevMCU_CLI is not set +# CONFIG_PKG_USING_DRMP is not set +# end of miscellaneous packages + +# +# Arduino libraries +# +# CONFIG_PKG_USING_RTDUINO is not set + +# +# Projects and Demos +# +# CONFIG_PKG_USING_ARDUINO_MSGQ_C_CPP_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_SKETCH_LOADER_DEMO is not set +# CONFIG_PKG_USING_ARDUINO_ULTRASOUND_RADAR is not set +# CONFIG_PKG_USING_ARDUINO_RTDUINO_SENSORFUSION_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_NINEINONE_SENSOR_SHIELD is not set +# CONFIG_PKG_USING_ARDUINO_SENSOR_KIT is not set +# CONFIG_PKG_USING_ARDUINO_MATLAB_SUPPORT is not set +# end of Projects and Demos + +# +# Sensors +# +# CONFIG_PKG_USING_ARDUINO_SENSOR_DEVICE_DRIVERS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSOR is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSORLAB is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL375 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L1X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL6180X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31855 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31865 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31856 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX6675 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90614 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS1 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AHTX0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS0 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADT7410 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME680 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9808 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4728 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA219 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR390 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DHT is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM6DS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO055 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX1704X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMC56X3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90393 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90395 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ICM20X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DPS310 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTS221 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT4X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL343 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS726X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AMG88XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2320 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2315 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR329_LTR303 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP3XX is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MS8607 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90640 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMA8451 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MSA301 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X_RVC is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS2MDL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303DLH_MAG is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LC709203F is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CAP1188 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CCS811 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_NAU7802 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS331 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS2X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS35HW is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303_ACCEL is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3DH is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8591 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL3115A2 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPR121 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPRLS is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPU6050 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCT2075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PM25AQI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_EMC2101 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXAS21002C is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SCD30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXOS8700 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HMC5883_UNIFIED is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP30 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP006 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TLA202X is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCS34725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI7021 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP40 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHTC3 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU21DF is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS7341 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU31D is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA260 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP007_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_L3GD20 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP117 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSC2007 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2591_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VCNL4040 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6075 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML7700 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LIS3DHTR is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DHT is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL335 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL345 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BME280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP280 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_H3LIS331DL is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MMA7660 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TSL2561 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PAJ7620 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VL53L0X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_ITG3200 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT31 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HP20X is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_DRV2605L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BBM150 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HMC5883L is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM303DLH is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_TCS3414CS is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MP503 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_BMP085 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HIGHTEMP is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_VEML6070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SI1145 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_SHT35 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_AT42QT1070 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LSM6DS3 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HDC1000 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_HM3301 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_MCP9600 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LTC2941 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_LDC1612 is not set +# CONFIG_PKG_USING_ARDUINO_CAPACITIVESENSOR is not set +# CONFIG_PKG_USING_ARDUINO_JARZEBSKI_MPU6050 is not set +# end of Sensors + +# +# Display +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_GFX_LIBRARY is not set +# CONFIG_PKG_USING_ARDUINO_U8G2 is not set +# CONFIG_PKG_USING_ARDUINO_TFT_ESPI is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ST7735 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SSD1306 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ILI9341 is not set +# CONFIG_PKG_USING_SEEED_TM1637 is not set +# end of Display + +# +# Timing +# +# CONFIG_PKG_USING_ARDUINO_RTCLIB is not set +# CONFIG_PKG_USING_ARDUINO_MSTIMER2 is not set +# CONFIG_PKG_USING_ARDUINO_TICKER is not set +# CONFIG_PKG_USING_ARDUINO_TASKSCHEDULER is not set +# end of Timing + +# +# Data Processing +# +# CONFIG_PKG_USING_ARDUINO_KALMANFILTER is not set +# CONFIG_PKG_USING_ARDUINO_ARDUINOJSON is not set +# CONFIG_PKG_USING_ARDUINO_TENSORFLOW_LITE_MICRO is not set +# CONFIG_PKG_USING_ARDUINO_RUNNINGMEDIAN is not set +# end of Data Processing + +# +# Data Storage +# + +# +# Communication +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PN532 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI4713 is not set +# end of Communication + +# +# Device Control +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8574 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCA9685 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TPA2016 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DRV2605 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS1841 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS3502 is not set +# CONFIG_PKG_USING_ARDUINO_SEEED_PCF85063TP is not set +# end of Device Control + +# +# Other +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MFRC630 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI5351 is not set +# end of Other + +# +# Signal IO +# +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BUSIO is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCA8418 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP23017 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADS1X15 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AW9523 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP3008 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4725 is not set +# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BD3491FS is not set +# end of Signal IO + +# +# Uncategorized +# +# end of Arduino libraries +# end of RT-Thread online packages + +# +# XiangShan configs +# +# CONFIG_BSP_USING_VIRTIO is not set +# end of XiangShan configs + +CONFIG_BOARD_XIANGSHAN=y +# CONFIG_ENABLE_FPU is not set +# CONFIG_ENABLE_VECTOR is not set +# CONFIG_RT_USING_USERSPACE_32BIT_LIMIT is not set +CONFIG_PLIC_BASE=0x3c000000 +CONFIG___STACKSIZE__=16384 diff --git a/bsp/xiangshan-verilator/.gitignore b/bsp/xiangshan-verilator/.gitignore new file mode 100644 index 00000000000..341f703a73f --- /dev/null +++ b/bsp/xiangshan-verilator/.gitignore @@ -0,0 +1,3 @@ +mnt.c +romfs_data.c +opensbi diff --git a/bsp/xiangshan-verilator/Kconfig b/bsp/xiangshan-verilator/Kconfig new file mode 100644 index 00000000000..b68f17f9c83 --- /dev/null +++ b/bsp/xiangshan-verilator/Kconfig @@ -0,0 +1,61 @@ +mainmenu "RT-Thread Project Configuration" + +BSP_DIR := . + +RTT_DIR := ../../ + +PKGS_DIR := packages + +source "$(RTT_DIR)/Kconfig" +osource "$PKGS_DIR/Kconfig" +rsource "driver/Kconfig" + +config BOARD_XIANGSHAN + bool + select ARCH_RISCV64 + select ARCH_USING_RISCV_COMMON64 + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + select RT_USING_CACHE + select ARCH_MM_MMU + select ARCH_REMAP_KERNEL + default y + +config ENABLE_FPU + bool "Enable FPU" + select ARCH_RISCV_FPU + default n + +config ENABLE_VECTOR + bool "Using RISC-V Vector Extension" + select ARCH_RISCV_VECTOR + default n + +if ENABLE_VECTOR + choice + prompt "Vector Registers Length in Bits" + default ARCH_VECTOR_VLEN_128 + + config ARCH_VECTOR_VLEN_128 + bool "128" + + config ARCH_VECTOR_VLEN_256 + bool "256" + endchoice +endif + +config RT_USING_USERSPACE_32BIT_LIMIT + bool "Enable userspace 32bit limit" + default n + +config RT_USING_VIRTIO_MMIO_ALIGN + bool "Open packed attribution, this may caused an error on virtio" + default n + +config PLIC_BASE + hex "PLIC base address" + default 0x3c000000 + +config __STACKSIZE__ + int "stack size for interrupt" + default 4096 diff --git a/bsp/xiangshan-verilator/README.md b/bsp/xiangshan-verilator/README.md new file mode 100644 index 00000000000..5a41a1f6080 --- /dev/null +++ b/bsp/xiangshan-verilator/README.md @@ -0,0 +1,407 @@ +**XiangShan/RISCV64 Board Support Package User Guide** + +English | [中文](./README_cn.md) + + + +- [1. Introduction](#1-introduction) +- [2. Building](#2-building) + - [2.1. Installing the toolchain](#21-installing-the-toolchain) + - [2.2. Setting RT-Thread toolchain environment variables](#22-setting-rt-thread-toolchain-environment-variables) + - [2.3. Downloading the kernel](#23-downloading-the-kernel) + - [2.4. Configuring the kernel](#24-configuring-the-kernel) + - [2.5. Compiling the kernel](#25-compiling-the-kernel) +- [3. Running](#3-running) + - [3.1. Installing QEMU](#31-installing-qemu) + - [3.2. Running QEMU](#32-running-qemu) + - [3.2.1. Running RT-Thread Standard Edition](#321-running-rt-thread-standard-edition) + - [3.2.2. Running RT-Thread Smart version](#322-running-rt-thread-smart-version) + - [3.2.3. Running RT-Thread Smart version + Root file-system](#323-running-rt-thread-smart-version--root-file-system) +- [4. How to use rv64ilp32 toolchain](#4-how-to-use-rv64ilp32-toolchain) +- [5. Contact information](#5-contact-information) + + + +# 1. Introduction + +RISC-V is an open and free instruction set architecture (ISA). This BSP targets the XiangShan environment and is derived from the RISCV64 VIRT BSP. + +> Note: The QEMU-specific build/run steps below are inherited from the original BSP and may not apply to XiangShan. Please adapt them to your XiangShan runtime environment. + +This project supports the world's first rv64ilp32 product-level open source toolchain jointly launched by the Xuantie team and the Institute of Software of the Chinese Academy of Sciences. + +# 2. Building + +Working system: take Ubuntu 22.04 as an example: + +```shell +$ lsb_release -a +No LSB modules are available. +Distributor ID: Ubuntu +Description: Ubuntu 22.04.2 LTS +Release: 22.04 +Codename: jammy +``` + +## 2.1. Installing the toolchain + +The specific toolchain used is consistent with the official RT-Thread. For the specific toolchain version, please refer to the file in the RT-Thread repository. + +```yaml + - name: Install RISC-V ToolChains + if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST != 'rtsmart/riscv64' && success() }} + run: | + wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.4/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz + sudo tar zxvf riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz -C /opt + /opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gcc --version + echo "RTT_EXEC_PATH=/opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin" >> $GITHUB_ENV + + - name: Install RISC-V Musl ToolChains + if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST == 'rtsmart/riscv64' && success() }} + shell: bash + run: | + wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.7/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 + sudo tar xjf riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 -C /opt + /opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin/riscv64-unknown-linux-musl-gcc --version + echo "RTT_EXEC_PATH=/opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin" >> $GITHUB_ENV + echo "RTT_CC_PREFIX=riscv64-unknown-linux-musl-" >> $GITHUB_ENV +``` + +Among them, `riscv64-unknown-elf-gcc` is used to build the RT-Thread Standard version, and `riscv64-unknown-linux-musl-gcc` is used to build the RT-Thread Smart version. Download them to your local computer according to the links shown above and decompress them. + +## 2.2. Setting RT-Thread toolchain environment variables + +There are three environment variables related to the RT-Thread toolchain + +- `RTT_CC` is the toolchain name, which is `"gcc"` here +- `RTT_CC_PREFIX`: is the toolchain prefix, which is `"riscv64-unknown-elf-"` for the Standard version and `"riscv64-unknown-linux-musl-"` for the Smart version. +- `RTT_EXEC_PATH`: the path where the bin folder of the toolchain is located, such as `"$HOME/tools/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin"`. This is set according to the actual path after personal download and decompression. Note that the toolchains of the RT-Thread standard version and the Smart version are two different versions, and the path name of `RTT_EXEC_PATH` must be set to `bin`. + +If you use them all the time, it is recommended to export these three environment variables in the `.bashrc` file. + +## 2.3. Downloading the kernel + +Assume that the working path is `$WORKSPACE`. + +```shell +$ cd $WORKSPACE +$ git clone git@github.com:RT-Thread/rt-thread.git +``` + +Enter the BSP directory where xiangshan is located. The following operations will not be introduced separately. By default, it is in this directory. + +```shell +$ cd $WORKSPACE/rt-thread/bsp/xiangshan +``` + +## 2.4. Configuring the kernel + +Refresh the configuration file before compiling for the first time. + +```shell +$ scons --menuconfig +``` + +The default configuration is the RT-Thread standard version, so if you don't have any special requirements, don't change anything, just save and exit. + +If you want to use the RT-Thread Smart version, at least turn on the `RT_USING_SMART` option after entering the configuration menu (see the figure below), and the rest depends on your needs. + +``` +(Top) → RT-Thread Kernel +RT-Thread Project Configuration +(24) The maximal size of kernel object name +[ ] Use the data types defined in ARCH_CPU +[*] Enable RT-Thread Smart (microkernel on kernel/userland) +[ ] Enable RT-Thread Nano +... +``` + +Save and exit after modification. + +## 2.5. Compiling the kernel + +If you have compiled before, you can clean it up: + +```shell +$ scons --clean +``` + +Or compile directly: + +```shell +$ scons -j$(nproc) +``` + +The kernel binary file `rtthread.bin` will be generated in the `$WORKSPACE/rt-thread/bsp/xiangshan`. + +# 3. Running + +## 3.1. Installing QEMU + +```shell +$ sudo apt update +$ sudo apt install qemu-system-misc +``` + +After the installation is complete, you can check the version. + +```shell +$ qemu-system-riscv64 --version +QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.24) +Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers +``` + +## 3.2. Running QEMU + +The repository has provided a ready-made execution script, which can be executed directly: + +```shell +$ ./run.sh +``` + +### 3.2.1. Running RT-Thread Standard Edition + +The following is an example: + +```shell +$ ./run.sh + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x8028d8a8 - 0x8428d8a8] + + \ | / +- RT - Thread Operating System + / | \ 5.2.0 build Nov 14 2024 15:41:57 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +file system initialization done! +Hello RISC-V +msh /> +``` +### 3.2.2. Running RT-Thread Smart version + +The following is an example: + +```shell +$ ./run.sh + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x002ef030 - 0x042ef030] + + \ | / +- RT - Thread Smart Operating System + / | \ 5.2.0 build Nov 14 2024 15:48:43 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +[I/drivers.serial] Using /dev/ttyS0 as default console +file system initialization done! +Hello RISC-V +msh /> +``` + +### 3.2.3. Running RT-Thread Smart version + Root file-system + +For the Smart version of the kernel, you can also specify the path of the root file-system image file when executing the `run.sh` script to mount the root file-system during the startup process. + +It should be noted that the kernel supports fat by default. If you want to mount the ext4 file-system, you need to install the lwext4 package additionally, i.e. to enable the `PKG_USING_LWEXT4` option (the specific menuconfig path is (Top) -> RT-Thread online packages -> system packages -> lwext4: an excellent choice of ext2/3/4 filesystem for microcontrollers.). If you can't find the item in the menu, you can exit menuconfig and execute `pkgs --upgrade` to update the package index and then try to enable the package. + +After checking this option, you also need to perform the following operations to update the software and install the source code to the packages directory of bsp (this operation only needs to be performed once): + +```shell +$ source ~/.env/env.sh +$ pkgs --update +``` + +Save and recompile the kernel. + +For how to make a root file-system, please refer to , which will not be repeated here. + +The example is as follows: + +```shell +$ ./run.sh /home/u/ws/duo/userapps/apps/build/ext4.img + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x00326438 - 0x04326438] + + \ | / +- RT - Thread Smart Operating System + / | \ 5.2.0 build Dec 17 2024 11:49:39 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +[I/drivers.serial] Using /dev/ttyS0 as default console +[W/DFS.fs] mount / failed with file system type: elm +file system initialization done! +Hello RISC-V +msh />[E/sal.skt] not find network interface device by protocol family(1). +[E/sal.skt] SAL socket protocol family input failed, return error -3. +/ # ls +bin lib proc sbin tmp +dev lost+found root services usr +etc mnt run tc var +/ # +``` + +# 4. How to use rv64ilp32 toolchain + +- Toolchain address: + +- Usage: + + - Configure toolchain path + + - Modify ABI parameter to: `-mabi=ilp32d` + + - Then perform regular compilation + + - Use [script](./qemu-rv64ilp32-nographic.sh) to start QEMU (INFO: QEMU binary is also in the toolchain directory) + +- Compare the firmware size of the same project compiled using the traditional 64-bit toolchain and the new 32-bit toolchain: + + Traditional 64-bit toolchain firmware size: + + ```bash + Memory region Used Size Region Size %age Used + SRAM: 225856 B 16 MB 1.35% + riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin + riscv64-unknown-elf-size rtthread.elf + text data bss dec hex filename + 150907 3664 71268 225839 3722f rtthread.elf + ``` + + New 32-bit toolchain firmware size: + + ```bash + Memory region Used Size Region Size %age Used + SRAM: 209376 B 16 MB 1.25% + riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin + riscv64-unknown-elf-size rtthread.elf + text data bss dec hex filename + 138739 1356 69276 209371 331db rtthread.elf + ``` + +# 5. Contact information + +Maintainer: [bernard][1] + +[1]: https://github.com/BernardXiong \ No newline at end of file diff --git a/bsp/xiangshan-verilator/README_cn.md b/bsp/xiangshan-verilator/README_cn.md new file mode 100644 index 00000000000..5990ab316dc --- /dev/null +++ b/bsp/xiangshan-verilator/README_cn.md @@ -0,0 +1,411 @@ +**XiangShan/RISCV64 板级支持包使用说明** + +中文页 | [English](./README.md) + + + +- [1. 简介](#1-简介) +- [2. 构建](#2-构建) + - [2.1. 安装工具链](#21-安装工具链) + - [2.2. 设置 RT-Thread 工具链环境变量](#22-设置-rt-thread-工具链环境变量) + - [2.3. 下载内核](#23-下载内核) + - [2.4. 配置内核](#24-配置内核) + - [2.5. 编译内核](#25-编译内核) +- [3. 运行](#3-运行) + - [3.1. 安装 QEMU](#31-安装-qemu) + - [3.2. 运行 QEMU](#32-运行-qemu) + - [3.2.1. 运行 RT-Thread 标准版](#321-运行-rt-thread-标准版) + - [3.2.2. 运行 RT-Thread Smart 版](#322-运行-rt-thread-smart-版) + - [3.2.3. 运行 RT-Thread Smart 版 + 根文件系统](#323-运行-rt-thread-smart-版--根文件系统) +- [4. 如何使用 rv64ilp32 工具链](#4-如何使用-rv64ilp32-工具链) +- [5. 联系人信息](#5-联系人信息) + + + +# 1. 简介 + +RISC-V 是一种开放和免费的指令集体系结构 (ISA)。本 BSP 面向 XiangShan 环境,派生自 RISCV64 VIRT BSP。 + +> 说明:下方的 QEMU 构建/运行步骤继承自原 BSP,可能不适用于 XiangShan,请根据 XiangShan 运行环境自行调整。 + +本工程支持玄铁团队联合中科院软件所共同推出的全球首款 rv64ilp32 产品级开源工具链。 + +# 2. 构建 + +工作系统:以 Ubuntu 22.04 为例: + +```shell +$ lsb_release -a +No LSB modules are available. +Distributor ID: Ubuntu +Description: Ubuntu 22.04.2 LTS +Release: 22.04 +Codename: jammy +``` + +## 2.1. 安装工具链 + +具体使用的工具链,和 RT-Thread 官方保持一致,具体的工具链版本可以参考 RT-Thread 仓库的 这个文件。 + +```yaml + - name: Install RISC-V ToolChains + if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST != 'rtsmart/riscv64' && success() }} + run: | + wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.4/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz + sudo tar zxvf riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz -C /opt + /opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gcc --version + echo "RTT_EXEC_PATH=/opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin" >> $GITHUB_ENV + + - name: Install RISC-V Musl ToolChains + if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST == 'rtsmart/riscv64' && success() }} + shell: bash + run: | + wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.7/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 + sudo tar xjf riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 -C /opt + /opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin/riscv64-unknown-linux-musl-gcc --version + echo "RTT_EXEC_PATH=/opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin" >> $GITHUB_ENV + echo "RTT_CC_PREFIX=riscv64-unknown-linux-musl-" >> $GITHUB_ENV +``` + +其中 `riscv64-unknown-elf-gcc` 用于构建 RT-Thread 标准版,`riscv64-unknown-linux-musl-gcc` 用于构建 RT-Thread Smart 版。根据上面所示链接分别下载到本地后解压缩。 + +## 2.2. 设置 RT-Thread 工具链环境变量 + +和 RT-Thread 工具链相关的环境变量有三个 + +- `RTT_CC` 为工具链名称, 这里统一为 `"gcc"` +- `RTT_CC_PREFIX`: 为工具链前缀, 这里对于标准版是 `"riscv64-unknown-elf-"`,对于 Smart 版是 `"riscv64-unknown-linux-musl-"`。 +- `RTT_EXEC_PATH`: 工具链的 bin 文件夹所在路径, 如 `"$HOME/tools/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin"`, 这个根据个人下载解压后的实际路径进行设置,注意 RT-Thread 标准版和 Smart 版本的工具链是两套不同的版本,而且设置 `RTT_EXEC_PATH` 的路径名时要一直到 `bin`。 + +如果一直使用的话,建议将这三个环境变量在 `.bashrc` 文件中 export。 + +## 2.3. 下载内核 + +假设工作路径是 `$WORKSPACE`。 + +```shell +$ cd $WORKSPACE +$ git clone git@github.com:RT-Thread/rt-thread.git +``` + +进入 xiangshan 所在 BSP 目录,后面的操作不做另外介绍,默认就在这个目录下。 + +```shell +$ cd $WORKSPACE/rt-thread/bsp/xiangshan +``` + +## 2.4. 配置内核 + +第一次编译前先刷新一下配置文件。 + +```shell +$ scons --menuconfig +``` + +默认配置就是 RT-Thread 标准版,所以如果没有什么特别需求,什么都不要改动,直接保存退出即可。 + +如果要使用 RT-Thread Smart 版,进入配置菜单后至少要打开 `RT_USING_SMART` 这个选项(见下图),其他的看自己的需求。 + +``` +(Top) → RT-Thread Kernel + RT-Thread Project Configuration +(24) The maximal size of kernel object name +[ ] Use the data types defined in ARCH_CPU +[*] Enable RT-Thread Smart (microkernel on kernel/userland) +[ ] Enable RT-Thread Nano +... +``` + +修改后保存退出。 + +## 2.5. 编译内核 + +如果以前编译后,可以清理一下: + +```shell +$ scons --clean +``` + +或者直接编译: + +```shell +$ scons -j$(nproc) +``` + +在 `$WORKSPACE/rt-thread/bsp/xiangshan` 路径下会生成内核的二进制文件 `rtthread.bin`。 + +# 3. 运行 + +## 3.1. 安装 QEMU + +```shell +$ sudo apt update +$ sudo apt install qemu-system-misc +``` + +安装完毕后可以看一下版本。 + +```shell +$ qemu-system-riscv64 --version +QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.24) +Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers +``` + +## 3.2. 运行 QEMU + +仓库里已经提供了现成的执行脚本,可以直接执行: + +```shell +$ ./run.sh +``` + +### 3.2.1. 运行 RT-Thread 标准版 + +示例如下: + +```shell +$ ./run.sh + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x8028d8a8 - 0x8428d8a8] + + \ | / +- RT - Thread Operating System + / | \ 5.2.0 build Nov 14 2024 15:41:57 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +file system initialization done! +Hello RISC-V +msh /> +``` + +### 3.2.2. 运行 RT-Thread Smart 版 + +示例如下: + +```shell +$ ./run.sh + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x002ef030 - 0x042ef030] + + \ | / +- RT - Thread Smart Operating System + / | \ 5.2.0 build Nov 14 2024 15:48:43 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +[I/drivers.serial] Using /dev/ttyS0 as default console +file system initialization done! +Hello RISC-V +msh /> +``` + +### 3.2.3. 运行 RT-Thread Smart 版 + 根文件系统 + +对于 Smart 版本的内核,也可以在执行 `run.sh` 脚本时指定根文件系统镜像文件的路径在启动过程中挂载根文件系统。 + +需要注意的是,内核默认支持 fat, 如果要挂载 ext4 的文件系统,则还需要额外安装 lwext4 软件包,即使能 `PKG_USING_LWEXT4`(具体 menuconfig 路径是 (Top) -> RT-Thread online packages -> system packages -> lwext4: an excellent choice of ext2/3/4 filesystem for microcontrollers.)。如果在菜单中找不到该软件包,可以退出 menuconfig 并执行 `pkgs --upgrade` 更新软件包索引后再尝试使能软件包。 + +勾选该选项后还需要执行如下操作更新软件并安装源码到 bsp 的 packages 目录下(该操作只要执行一次即可): + +```shell +$ source ~/.env/env.sh +$ pkgs --update +``` + +保存后重新编译内核。 + +有关如何制作根文件系统,请参考 ,这里不再赘述。 + +示例如下: + +```shell +$ ./run.sh /home/u/ws/duo/userapps/apps/build/ext4.img + +OpenSBI v0.9 + ____ _____ ____ _____ + / __ \ / ____| _ \_ _| + | | | |_ __ ___ _ __ | (___ | |_) || | + | | | | '_ \ / _ \ '_ \ \___ \| _ < | | + | |__| | |_) | __/ | | |____) | |_) || |_ + \____/| .__/ \___|_| |_|_____/|____/_____| + | | + |_| + +Platform Name : riscv-virtio,qemu +Platform Features : timer,mfdeleg +Platform HART Count : 1 +Firmware Base : 0x80000000 +Firmware Size : 100 KB +Runtime SBI Version : 0.2 + +Domain0 Name : root +Domain0 Boot HART : 0 +Domain0 HARTs : 0* +Domain0 Region00 : 0x0000000080000000-0x000000008001ffff () +Domain0 Region01 : 0x0000000000000000-0xffffffffffffffff (R,W,X) +Domain0 Next Address : 0x0000000080200000 +Domain0 Next Arg1 : 0x000000008f000000 +Domain0 Next Mode : S-mode +Domain0 SysReset : yes + +Boot HART ID : 0 +Boot HART Domain : root +Boot HART ISA : rv64imafdcsu +Boot HART Features : scounteren,mcounteren,time +Boot HART PMP Count : 16 +Boot HART PMP Granularity : 4 +Boot HART PMP Address Bits: 54 +Boot HART MHPM Count : 0 +Boot HART MHPM Count : 0 +Boot HART MIDELEG : 0x0000000000000222 +Boot HART MEDELEG : 0x000000000000b109 +heap: [0x00326438 - 0x04326438] + + \ | / +- RT - Thread Smart Operating System + / | \ 5.2.0 build Dec 17 2024 11:49:39 + 2006 - 2024 Copyright by RT-Thread team +lwIP-2.0.3 initialized! +[I/sal.skt] Socket Abstraction Layer initialize success. +[I/utest] utest is initialize success. +[I/utest] total utest testcase num: (0) +[I/drivers.serial] Using /dev/ttyS0 as default console +[W/DFS.fs] mount / failed with file system type: elm +file system initialization done! +Hello RISC-V +msh />[E/sal.skt] not find network interface device by protocol family(1). +[E/sal.skt] SAL socket protocol family input failed, return error -3. +/ # ls +bin lib proc sbin tmp +dev lost+found root services usr +etc mnt run tc var +/ # +``` + +# 4. 如何使用 rv64ilp32 工具链 + +- 工具链地址:https://github.com/ruyisdk/riscv-gnu-toolchain-rv64ilp32/tags + +- 使用方法: + + - 配置工具链路径 + + - 修改ABI参数为:`-mabi=ilp32d` + + - 然后执行常规编译 + + - 使用 [脚本](./qemu-rv64ilp32-nographic.sh) 启动 QEMU (INFO: QEMU 二进制同样在工具链目录) + +- 使用传统 64 位工具链与使用新 32 位工具链编译相同工程的固件大小对比: + + 传统 64 位工具链固件大小: + + ```bash + Memory region Used Size Region Size %age Used + SRAM: 225856 B 16 MB 1.35% + riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin + riscv64-unknown-elf-size rtthread.elf + text data bss dec hex filename + 150907 3664 71268 225839 3722f rtthread.elf + ``` + + 新 32 位工具链固件大小: + + ```bash + Memory region Used Size Region Size %age Used + SRAM: 209376 B 16 MB 1.25% + riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin + riscv64-unknown-elf-size rtthread.elf + text data bss dec hex filename + 138739 1356 69276 209371 331db rtthread.elf + ``` + +# 5. 联系人信息 + +维护人:[bernard][1] + +[1]: https://github.com/BernardXiong + + + diff --git a/bsp/xiangshan-verilator/SConscript b/bsp/xiangshan-verilator/SConscript new file mode 100644 index 00000000000..c7ef7659ece --- /dev/null +++ b/bsp/xiangshan-verilator/SConscript @@ -0,0 +1,14 @@ +# for module compiling +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/bsp/xiangshan-verilator/SConstruct b/bsp/xiangshan-verilator/SConstruct new file mode 100644 index 00000000000..ae0e3375e11 --- /dev/null +++ b/bsp/xiangshan-verilator/SConstruct @@ -0,0 +1,62 @@ +import os +import sys +import rtconfig + +from rtconfig import RTT_ROOT + +sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')] +from building import * + +TARGET = 'rtthread.' + rtconfig.TARGET_EXT + +DefaultEnvironment(tools=[]) +env = Environment(tools = ['mingw'], + AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, + CC = rtconfig.CC, CCFLAGS = rtconfig.CFLAGS, + CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS, + AR = rtconfig.AR, ARFLAGS = '-rc', + LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS) +env.PrependENVPath('PATH', rtconfig.EXEC_PATH) +env['ASCOM'] = env['ASPPCOM'] + +Export('RTT_ROOT') +Export('rtconfig') +rtconfig.CPU='virt64' +rtconfig.ARCH='risc-v' + +# prepare building environment +objs = PrepareBuilding(env, RTT_ROOT, has_libcpu = False) + +stack_size = 4096 + +if GetDepend('RT_USING_SMART'): + # use smart link.lds + env['LINKFLAGS'] = env['LINKFLAGS'].replace('link.lds', 'link_smart.lds') + +stack_lds = open('link_stacksize.lds', 'w') +if GetDepend('__STACKSIZE__'): stack_size = GetDepend('__STACKSIZE__') +stack_lds.write('__STACKSIZE__ = %d;\n' % stack_size) +stack_lds.close() + +# Obtain the number of harts from rtconfig.h and write +# it into link_cpus.lds for the linker script +try: + with open('rtconfig.h', 'r') as f: + rtconfig_content = f.readlines() +except FileNotFoundError: + cpus_nr = 1 +else: + cpus_nr = 1 # default value + for line in rtconfig_content: + line = line.strip() + if line.startswith('#define') and 'RT_CPUS_NR' in line: + parts = line.split() + if len(parts) >= 3 and parts[2].isdigit(): + cpus_nr = int(parts[2]) + break + +with open('link_cpus.lds', 'w') as cpus_lds: + cpus_lds.write(f'RT_CPUS_NR = {cpus_nr};\n') + +# make a building +DoBuilding(TARGET, objs) diff --git a/bsp/xiangshan-verilator/applications/SConscript b/bsp/xiangshan-verilator/applications/SConscript new file mode 100644 index 00000000000..7e4f7d3ac37 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/SConscript @@ -0,0 +1,17 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH) + +objs = [group] + +list = os.listdir(cwd) + +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/bsp/xiangshan-verilator/applications/main.c b/bsp/xiangshan-verilator/applications/main.c new file mode 100644 index 00000000000..b55df4da2b1 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/main.c @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include + +int main(void) +{ + rt_kprintf("Hello RISC-V\n"); + + return 0; +} diff --git a/bsp/xiangshan-verilator/applications/test/SConscript b/bsp/xiangshan-verilator/applications/test/SConscript new file mode 100644 index 00000000000..2597249cbd9 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/test/SConscript @@ -0,0 +1,17 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('Testcase', src, depend = [''], CPPPATH = CPPPATH) + +list = os.listdir(cwd) + +objs = [group] + +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/bsp/xiangshan-verilator/applications/test/test_vector/SConscript b/bsp/xiangshan-verilator/applications/test/test_vector/SConscript new file mode 100644 index 00000000000..0827b048b28 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/test/test_vector/SConscript @@ -0,0 +1,9 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.cpp') +CPPPATH = [cwd] + +group = DefineGroup('Vector', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/xiangshan-verilator/applications/test/test_vector/test_vector.c b/bsp/xiangshan-verilator/applications/test/test_vector/test_vector.c new file mode 100644 index 00000000000..50aae802480 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/test/test_vector/test_vector.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#include +#include +#include +#include + +#if defined(RT_USING_UTEST) && defined(ENABLE_VECTOR) +#include +#include + +void rt_hw_vector_ctx_restore(void *buf); +void rt_hw_vector_ctx_save(void *buf); + +/** + * ============================================================== + * TEST FEATURE + * Use libc `memcpy` which employing V extension codes + * to test V extension features + * ============================================================== + */ +static char *constant = "hello,it's a nice day and i'm happy to see you\n"; +#define ARR_SIZE 4096 +static char array[ARR_SIZE]; + +static void test_feature(void) +{ + memcpy(array, constant, sizeof array); + char *src = constant; + char *dst = array; + int error = 0; + + for (size_t i = 0; i < ARR_SIZE; i++) + { + if (src[i] != dst[i]) + { + error = 1; + break; + } + } + + uassert_false(error); +} + +/** + * ============================================================== + * TEST CONTEXT SAVING + * Create 2 threads employing V extension, verify V states are + * not modified by each other + * ============================================================== + */ +#define TEST_THREAD 2 +#define VECTOR_CTX_BYTES (CTX_VECTOR_REG_NR * REGBYTES) +void *ctx_vector[TEST_THREAD * 2]; + +static rt_sem_t sem; + +void dump_frame(void *frame) +{ + uint64_t *content = frame; + for (size_t i = 0; i < VECTOR_CTX_BYTES / 8; i++) + { + rt_kprintf("%x ", content[i]); + } + rt_kprintf("\n"); +} + +static void vector_child(void *param) +{ + void **ctx = param; + uint64_t *reg = ctx[0]; + uint64_t vtype; + uint64_t vl; + + rt_sem_release(sem); + + rt_hw_vector_ctx_restore(ctx[0]); + + /* STAGE 2, save t2 context */ + test_feature(); + + /** + * @brief vtype & vl will be modified after context saving, + * it's ok because it will be recover after context restoring + * We restore these states manually here. + */ + asm volatile("csrr %0, vtype":"=r"(vtype)); + asm volatile("csrr %0, vl":"=r"(vl)); + rt_hw_vector_ctx_save(ctx[0]); + + rt_memcpy(ctx[1], ctx[0], VECTOR_CTX_BYTES); + + rt_thread_yield(); + + asm volatile("vsetvl x0, %0, %1"::"r"(vl), "r"(vtype)); + rt_hw_vector_ctx_save(ctx[0]); + + uassert_false(rt_memcmp(ctx[1], ctx[0], VECTOR_CTX_BYTES)); +} + +/** + * @brief Test if context save/restore codes work properly + */ +static void test_context() +{ + rt_thread_t child; + uint64_t vtype; + uint64_t vl; + + for (size_t i = 0; i < TEST_THREAD; i++) + { + ctx_vector[i * 2] = calloc(VECTOR_CTX_BYTES, 1); + ctx_vector[i * 2 + 1] = calloc(VECTOR_CTX_BYTES, 1); + } + rt_hw_vector_ctx_restore(ctx_vector[0]); + + child = rt_thread_create("test_vector_child", vector_child, &ctx_vector[2], 4096, 10, 20); + + /* STAGE 1, save t1 context */ + /* assuming that rt libc memcpy do not use vector instruction */ + asm volatile("csrr %0, vtype":"=r"(vtype)); + asm volatile("csrr %0, vl":"=r"(vl)); + rt_hw_vector_ctx_save(ctx_vector[0]); + + rt_memcpy(ctx_vector[1], ctx_vector[0], VECTOR_CTX_BYTES); + + rt_thread_startup(child); + rt_sem_take(sem, 0); + + /* STAGE 3, verify t1 context */ + asm volatile("vsetvl x0, %0, %1"::"r"(vl), "r"(vtype)); + rt_hw_vector_ctx_save(ctx_vector[0]); + uassert_false(rt_memcmp(ctx_vector[1], ctx_vector[0], VECTOR_CTX_BYTES)); + + rt_thread_yield(); +} + +/** + * ============================================================== + * TEST NO VECTOR raise error and recover + * ============================================================== + */ + +static void test_no_vector() +{ + asm volatile ("li t0, 0x600\n" + "csrc sstatus, t0"); + test_feature(); + uassert_true(1); +} + +static rt_err_t utest_tc_init(void) +{ + sem = rt_sem_create("test_ctx", 0, RT_IPC_FLAG_FIFO); + return RT_EOK; +} + +static rt_err_t utest_tc_cleanup(void) +{ + rt_sem_delete(sem); + return RT_EOK; +} + +static void testcase(void) +{ + UTEST_UNIT_RUN(test_feature); + UTEST_UNIT_RUN(test_context); + UTEST_UNIT_RUN(test_no_vector); +} + +UTEST_TC_EXPORT(testcase, "testcases.libcpu.vector", utest_tc_init, utest_tc_cleanup, 10); +#endif /* RT_USING_UTEST && ENABLE_VECTOR */ diff --git a/bsp/xiangshan-verilator/driver/Kconfig b/bsp/xiangshan-verilator/driver/Kconfig new file mode 100644 index 00000000000..e0df212a98b --- /dev/null +++ b/bsp/xiangshan-verilator/driver/Kconfig @@ -0,0 +1,43 @@ +menu "XiangShan configs" + +config BSP_USING_VIRTIO + bool "Using VirtIO" + default n + depends on RT_USING_DEVICE_OPS + +config BSP_USING_VIRTIO_BLK + bool "Using VirtIO BLK" + select RT_USING_VIRTIO + select RT_USING_VIRTIO_BLK + default n + depends on BSP_USING_VIRTIO + +config BSP_USING_VIRTIO_NET + bool "Using VirtIO NET" + select RT_USING_VIRTIO + select RT_USING_VIRTIO_NET + default n + depends on BSP_USING_VIRTIO + +config BSP_USING_VIRTIO_CONSOLE + bool "Using VirtIO Console" + select RT_USING_VIRTIO + select RT_USING_VIRTIO_CONSOLE + default n + depends on BSP_USING_VIRTIO + +config BSP_USING_VIRTIO_GPU + bool "Using VirtIO GPU" + select RT_USING_VIRTIO + select RT_USING_VIRTIO_GPU + default n + depends on BSP_USING_VIRTIO + +config BSP_USING_VIRTIO_INPUT + bool "Using VirtIO Input" + select RT_USING_VIRTIO + select RT_USING_VIRTIO_INPUT + default n + depends on BSP_USING_VIRTIO + +endmenu diff --git a/bsp/xiangshan-verilator/driver/SConscript b/bsp/xiangshan-verilator/driver/SConscript new file mode 100644 index 00000000000..faea9c1bd9b --- /dev/null +++ b/bsp/xiangshan-verilator/driver/SConscript @@ -0,0 +1,19 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH) + +objs = [group] + +list = os.listdir(cwd) + +for item in list: + if os.path.isfile(os.path.join(cwd, item, 'SConscript')): + objs = objs + SConscript(os.path.join(item, 'SConscript')) + +Return('objs') diff --git a/bsp/xiangshan-verilator/driver/asm/sbiasm.h b/bsp/xiangshan-verilator/driver/asm/sbiasm.h new file mode 100644 index 00000000000..4639fba68cf --- /dev/null +++ b/bsp/xiangshan-verilator/driver/asm/sbiasm.h @@ -0,0 +1,10 @@ +#ifndef _SBI_ASM_H +#define _SBI_ASM_H + +.macro SBI_CALL which + li a7, \which + ecall + nop +.endm + +#endif /* _SBI_ASM_H */ diff --git a/bsp/xiangshan-verilator/driver/asm/sbidef.h b/bsp/xiangshan-verilator/driver/asm/sbidef.h new file mode 100644 index 00000000000..5bcf58ade7c --- /dev/null +++ b/bsp/xiangshan-verilator/driver/asm/sbidef.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2019-2020, Xim + * + * SPDX-License-Identifier: Apache-2.0 + * + */ +#ifndef _ASM_SBI_DEF_H +#define _ASM_SBI_DEF_H + +#define SBI_SET_TIMER 0 +#define SBI_CONSOLE_PUTCHAR 1 +#define SBI_CONSOLE_GETCHAR 2 +#define SBI_CLEAR_IPI 3 +#define SBI_SEND_IPI 4 +#define SBI_REMOTE_FENCE_I 5 +#define SBI_REMOTE_SFENCE_VMA 6 +#define SBI_REMOTE_SFENCE_VMA_ASID 7 +#define SBI_SHUTDOWN 8 + +#define SBI_CONSOLE_PUTSTR 9 + +#define SBI_SD_WRITE 10 +#define SBI_SD_READ 11 +#define SBI_NET_WRITE 12 +#define SBI_NET_READ 13 + +#endif /* _ASM_SBI_DEF_H */ diff --git a/bsp/xiangshan-verilator/driver/board.c b/bsp/xiangshan-verilator/driver/board.c new file mode 100644 index 00000000000..092244278ed --- /dev/null +++ b/bsp/xiangshan-verilator/driver/board.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-01-30 lizhirui first version + */ + +#include +#include +#include + +#include "board.h" +#include "mm_aspace.h" +#include "tick.h" + +#include "drv_uart.h" +#include "encoding.h" +#include "stack.h" +#include "sbi.h" +#include "riscv.h" +#include "plic.h" +#include "stack.h" + +#ifdef RT_USING_SMP +#include "interrupt.h" +#endif /* RT_USING_SMP */ + +#ifdef RT_USING_SMART +#include "riscv_mmu.h" +#include "mmu.h" +#include "page.h" +#include "lwp_arch.h" + +rt_region_t init_page_region = {(rt_size_t)RT_HW_PAGE_START, (rt_size_t)RT_HW_PAGE_END}; + +extern size_t MMUTable[]; + +struct mem_desc platform_mem_desc[] = { + {KERNEL_VADDR_START, (rt_size_t)RT_HW_PAGE_END - 1, (rt_size_t)ARCH_MAP_FAILED, NORMAL_MEM}, +}; + +#define NUM_MEM_DESC (sizeof(platform_mem_desc) / sizeof(platform_mem_desc[0])) + +#endif + +void primary_cpu_entry(void) +{ + /* disable global interrupt */ + rt_hw_interrupt_disable(); + + entry(); +} + +#define IOREMAP_SIZE (1ul << 30) + +#ifndef ARCH_REMAP_KERNEL +#define IOREMAP_VEND USER_VADDR_START +#else +#define IOREMAP_VEND 0ul +#endif /* ARCH_REMAP_KERNEL */ + +void rt_hw_board_init(void) +{ +#ifdef RT_USING_SMART + /* init data structure */ + rt_hw_mmu_map_init(&rt_kernel_space, (void *)(IOREMAP_VEND - IOREMAP_SIZE), IOREMAP_SIZE, (rt_size_t *)MMUTable, PV_OFFSET); + + /* init page allocator */ + rt_page_init(init_page_region); + + /* setup region, and enable MMU */ + rt_hw_mmu_setup(&rt_kernel_space, platform_mem_desc, NUM_MEM_DESC); +#endif + +#ifdef RT_USING_HEAP + /* initialize memory system */ + rt_system_heap_init(RT_HW_HEAP_BEGIN, RT_HW_HEAP_END); +#endif + + plic_init(); + + rt_hw_interrupt_init(); + + rt_hw_uart_init(); + +#ifdef RT_USING_CONSOLE + /* set console device */ + rt_console_set_device(RT_CONSOLE_DEVICE_NAME); +#endif /* RT_USING_CONSOLE */ + + rt_hw_tick_init(); + +#ifdef RT_USING_SMP + /* ipi init */ + rt_hw_ipi_init(); +#endif /* RT_USING_SMP */ + +#ifdef RT_USING_COMPONENTS_INIT + rt_components_board_init(); +#endif + +#ifdef RT_USING_HEAP + rt_kprintf("heap: [0x%08x - 0x%08x]\n", (rt_ubase_t)RT_HW_HEAP_BEGIN, (rt_ubase_t)RT_HW_HEAP_END); +#endif /* RT_USING_HEAP */ +} + +void rt_hw_cpu_reset(void) +{ + sbi_shutdown(); + + while (1) + ; +} +MSH_CMD_EXPORT_ALIAS(rt_hw_cpu_reset, reboot, reset machine); + diff --git a/bsp/xiangshan-verilator/driver/board.h b/bsp/xiangshan-verilator/driver/board.h new file mode 100644 index 00000000000..9c74ae6b267 --- /dev/null +++ b/bsp/xiangshan-verilator/driver/board.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-01-30 lizhirui first version + */ + +#ifndef BOARD_H__ +#define BOARD_H__ + +#include + +extern unsigned int __bss_start; +extern unsigned int __bss_end; + +#ifndef RT_USING_SMART +#define KERNEL_VADDR_START 0x0 +#endif + +#define VIRT64_SBI_MEMSZ (0x200000) + +#define RT_HW_HEAP_BEGIN ((void *)&__bss_end) +#define RT_HW_HEAP_END ((void *)(RT_HW_HEAP_BEGIN + 64 * 1024 * 1024)) +#define RT_HW_PAGE_START RT_HW_HEAP_END +#define RT_HW_PAGE_END ((void *)(KERNEL_VADDR_START + (128 * 1024 * 1024 - VIRT64_SBI_MEMSZ))) + +void rt_hw_board_init(void); +void rt_init_user_mem(struct rt_thread *thread, const char *name, + unsigned long *entry); + +#endif diff --git a/bsp/xiangshan-verilator/driver/drv_uart.c b/bsp/xiangshan-verilator/driver/drv_uart.c new file mode 100644 index 00000000000..11755761e75 --- /dev/null +++ b/bsp/xiangshan-verilator/driver/drv_uart.c @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include +#include +#include + +#include "board.h" +#include "drv_uart.h" + +#include +#ifdef RT_USING_SMART +#include +#endif +#include "sbi.h" + +struct device_uart +{ + rt_ubase_t hw_base; + rt_uint32_t irqno; +}; + +void *uart0_base = (void*)0x310b0000; +struct rt_serial_device serial0; +struct device_uart uart0; + +void uart_init(void) +{ + rt_uint32_t div = UART_REFERENCE_CLOCK / (UART_DEFAULT_BAUDRATE * 16); + + write8_uart0(UART_IER, 0x00); + write8_uart0(UART_LCR, UART_LCR_BAUD_LATCH); + + // LSB + write8_uart0(0, div & 0xff); + // MSB + write8_uart0((1 << 2), (div >> 8) & 0xff); + + // set word length to 8 bits, no parity + write8_uart0(UART_LCR, UART_LCR_EIGHT_BITS); + + write8_uart0(UART_FCR, UART_FCR_FIFO_ENABLE | UART_FCR_FIFO_CLEAR); + + return; +} + +static rt_err_t _uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + return (RT_EOK); +} + +static rt_err_t _uart_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + struct device_uart *uart = (struct device_uart*)serial->parent.user_data; + + switch (cmd) + { + case RT_DEVICE_CTRL_CLR_INT: + if ((size_t)arg == RT_DEVICE_FLAG_INT_RX) + { + rt_uint8_t value = read8_uart0(UART_IER); + write8_uart0(UART_IER, value & ~UART_IER_RX_ENABLE); + } + break; + + case RT_DEVICE_CTRL_SET_INT: + if ((size_t)arg == RT_DEVICE_FLAG_INT_RX) + { + rt_uint8_t value = read8_uart0(UART_IER); + write8_uart0(UART_IER, value | UART_IER_RX_ENABLE); + } + break; + } + + return (RT_EOK); +} + +static int _uart_putc(struct rt_serial_device *serial, char c) +{ + struct device_uart *uart; + uart = (struct device_uart*)serial->parent.user_data; + + // wait for Transmit Holding Empty to be set in LSR. + while((read8_uart0(UART_LSR) & UART_LSR_TX_IDLE) == 0) + ; + write8_uart0(UART_THR, c); + + return (1); +} + +static int _uart_getc(struct rt_serial_device *serial) +{ + struct device_uart *uart; + volatile rt_uint32_t lsr; + int ch = -1; + + uart = (struct device_uart*)serial->parent.user_data; + lsr = read8_uart0(UART_LSR); + + if (lsr & UART_LSR_RX_READY) + { + ch = read8_uart0(UART_RHR); + } + return ch; +} + +const struct rt_uart_ops _uart_ops = { + _uart_configure, + _uart_control, + _uart_putc, + _uart_getc, + // TODO: add DMA support + RT_NULL}; + +static void rt_hw_uart_isr(int irqno, void *param) +{ + rt_ubase_t level = rt_hw_interrupt_disable(); + + struct rt_serial_device *serial = (struct rt_serial_device *)param; + + rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND); + + rt_hw_interrupt_enable(level); +} + +/* + * UART Initiation + */ +int rt_hw_uart_init(void) +{ + struct rt_serial_device *serial; + struct device_uart *uart; + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; +#ifdef RT_USING_SMART + uart0_base = rt_ioremap(uart0_base, 4096); +#endif + // register device + serial = &serial0; + uart = &uart0; + + serial->ops = &_uart_ops; + serial->config = config; + serial->config.baud_rate = UART_DEFAULT_BAUDRATE; + uart->hw_base = (rt_ubase_t)uart0_base; + uart->irqno = 0x0a; + + rt_hw_serial_register(serial, + RT_CONSOLE_DEVICE_NAME, + RT_DEVICE_FLAG_STREAM | RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + uart); + rt_hw_interrupt_install(uart->irqno, rt_hw_uart_isr, serial, RT_CONSOLE_DEVICE_NAME); + rt_hw_interrupt_umask(uart->irqno); + return 0; +} + +/* WEAK for SDK 0.5.6 */ +rt_weak void uart_debug_init(int uart_channel) +{ +} diff --git a/bsp/xiangshan-verilator/driver/drv_uart.h b/bsp/xiangshan-verilator/driver/drv_uart.h new file mode 100644 index 00000000000..d40295d8188 --- /dev/null +++ b/bsp/xiangshan-verilator/driver/drv_uart.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __DRV_UART_H__ +#define __DRV_UART_H__ + +#include "riscv_io.h" + +/** + * uart ns16550a + * http://byterunner.com/16550.html + */ + +/* TRANSMIT AND RECEIVE HOLDING REGISTER */ +#define UART_RHR 0 +#define UART_THR 0 + +/* INTERRUPT ENABLE REGISTER */ +#define UART_IER (1 << 2) +#define UART_IER_RX_ENABLE (1 << 0) +#define UART_IER_TX_ENABLE (1 << 1) + +/* FIFO CONTROL REGISTER */ +#define UART_FCR (2 << 2) +#define UART_FCR_FIFO_ENABLE (1 << 0) +#define UART_FCR_FIFO_CLEAR (3 << 1) + +/* INTERRUPT STATUS REGISTER */ +#define UART_ISR (2 << 2) + +/* LINE CONTROL REGISTER */ +#define UART_LCR (3 << 2) +#define UART_LCR_EIGHT_BITS (3 << 0) +// special mode to set baud rate +#define UART_LCR_BAUD_LATCH (1 << 7) + +/* LINE STATUS REGISTER */ +#define UART_LSR (5 << 2) +// input is waiting to be read from RHR +#define UART_LSR_RX_READY (1 << 0) +// THR can accept another character to send +#define UART_LSR_TX_IDLE (1 << 5) + +#define UART_REFERENCE_CLOCK 1843200 +#define UART_DEFAULT_BAUDRATE 115200 + +extern void *uart0_base; + +#define write8_uart0(idx, value) __raw_writeb(((rt_uint8_t)value), (void*)((size_t)uart0_base + (idx))) +#define read8_uart0(idx) __raw_readb((void*)((size_t)uart0_base + (idx))) + +void rt_hw_uart_start_rx_thread(); +int rt_hw_uart_init(void); +void drv_uart_puts(char *str); // for syscall + +#endif /* __DRV_UART_H__ */ diff --git a/bsp/xiangshan-verilator/driver/drv_virtio.c b/bsp/xiangshan-verilator/driver/drv_virtio.c new file mode 100644 index 00000000000..e1289863b7e --- /dev/null +++ b/bsp/xiangshan-verilator/driver/drv_virtio.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#include +#include + +#ifdef BSP_USING_VIRTIO + +#include +#ifdef BSP_USING_VIRTIO_BLK +#include +#endif +#ifdef BSP_USING_VIRTIO_NET +#include +#endif +#ifdef BSP_USING_VIRTIO_CONSOLE +#include +#endif +#ifdef BSP_USING_VIRTIO_GPU +#include +#endif +#ifdef BSP_USING_VIRTIO_INPUT +#include +#endif + +#include + +static virtio_device_init_handler virtio_device_init_handlers[] = +{ +#ifdef BSP_USING_VIRTIO_BLK + [VIRTIO_DEVICE_ID_BLOCK] = rt_virtio_blk_init, +#endif +#ifdef BSP_USING_VIRTIO_NET + [VIRTIO_DEVICE_ID_NET] = rt_virtio_net_init, +#endif +#ifdef BSP_USING_VIRTIO_CONSOLE + [VIRTIO_DEVICE_ID_CONSOLE] = rt_virtio_console_init, +#endif +#ifdef BSP_USING_VIRTIO_GPU + [VIRTIO_DEVICE_ID_GPU] = rt_virtio_gpu_init, +#endif +#ifdef BSP_USING_VIRTIO_INPUT + [VIRTIO_DEVICE_ID_INPUT] = rt_virtio_input_init, +#endif + [VIRTIO_DEVICE_TYPE_SIZE] = RT_NULL +}; + +int rt_virtio_devices_init(void) +{ + int i; + rt_uint32_t irq = VIRTIO_IRQ_BASE; + rt_ubase_t mmio_base = VIRTIO_MMIO_BASE; + struct virtio_mmio_config *mmio_config; + virtio_device_init_handler init_handler; + + if (sizeof(virtio_device_init_handlers) == 0) + { + /* The compiler will optimize the codes after here. */ + return 0; + } + +#ifdef RT_USING_SMART + mmio_base = (rt_ubase_t)rt_ioremap((void *)mmio_base, VIRTIO_MMIO_SIZE * VIRTIO_MAX_NR); + + if (mmio_base == RT_NULL) + { + return -RT_ERROR; + } +#endif + + for (i = 0; i < VIRTIO_MAX_NR; ++i, ++irq, mmio_base += VIRTIO_MMIO_SIZE) + { + mmio_config = (struct virtio_mmio_config *)mmio_base; + + if (mmio_config->magic != VIRTIO_MAGIC_VALUE || + mmio_config->version != RT_USING_VIRTIO_VERSION || + mmio_config->vendor_id != VIRTIO_VENDOR_ID) + { + continue; + } + + init_handler = virtio_device_init_handlers[mmio_config->device_id]; + + if (init_handler != RT_NULL) + { + init_handler((rt_ubase_t *)mmio_base, irq); + } + } + + return 0; +} +INIT_DEVICE_EXPORT(rt_virtio_devices_init); +#endif /* BSP_USING_VIRTIO */ diff --git a/bsp/xiangshan-verilator/driver/drv_virtio.h b/bsp/xiangshan-verilator/driver/drv_virtio.h new file mode 100644 index 00000000000..954338a3864 --- /dev/null +++ b/bsp/xiangshan-verilator/driver/drv_virtio.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#ifndef __DRV_VIRTIO_H__ +#define __DRV_VIRTIO_H__ + +int rt_virtio_devices_init(void); + +#endif /* __DRV_VIRTIO_H__ */ diff --git a/bsp/xiangshan-verilator/driver/virt.h b/bsp/xiangshan-verilator/driver/virt.h new file mode 100644 index 00000000000..f059feb50e6 --- /dev/null +++ b/bsp/xiangshan-verilator/driver/virt.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-02-17 GuEe-GUI the first version + */ + +#ifndef VIRT_H__ +#define VIRT_H__ + +#include + +#ifdef RT_USING_SMART +#include +#include + +#endif + +/* VirtIO */ +#define VIRTIO_MMIO_BASE 0x10001000 +#define VIRTIO_MMIO_SIZE 0x00001000 +#define VIRTIO_MAX_NR 8 +#define VIRTIO_IRQ_BASE 1 +#define VIRTIO_VENDOR_ID 0x554d4551 /* "QEMU" */ + +#define MAX_HANDLERS 128 +#endif diff --git a/bsp/xiangshan-verilator/link.lds b/bsp/xiangshan-verilator/link.lds new file mode 100644 index 00000000000..da750aca9bb --- /dev/null +++ b/bsp/xiangshan-verilator/link.lds @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020/12/12 bernard The first version + */ + +INCLUDE "link_stacksize.lds" +INCLUDE "link_cpus.lds" + +OUTPUT_ARCH( "riscv" ) + +/* + * Memory layout: + * 0x80000000 - 0x80200000: SBI + * 0x80200000 - 0x81200000: Kernel + */ + +MEMORY +{ + SRAM : ORIGIN = 0x80200000, LENGTH = 0x1000000 +} + +ENTRY(_start) +SECTIONS +{ + . = 0x80200000 ; + + /* __STACKSIZE__ = 4096; */ + __text_start = .; + .start : + { + *(.start); + } > SRAM + + . = ALIGN(8); + + .text : + { + *(.text) /* remaining code */ + *(.text.*) /* remaining code */ + *(.rodata) /* read-only data (constants) */ + *(.rodata*) + *(.glue_7) + *(.glue_7t) + *(.gnu.linkonce.t*) + + /* section information for finsh shell */ + . = ALIGN(8); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + . = ALIGN(8); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + . = ALIGN(8); + + /* section information for initial. */ + . = ALIGN(8); + __rt_init_start = .; + KEEP(*(SORT(.rti_fn*))) + __rt_init_end = .; + . = ALIGN(8); + + __rt_utest_tc_tab_start = .; + KEEP(*(UtestTcTab)) + __rt_utest_tc_tab_end = .; + + . = ALIGN(8); + _etext = .; + } > SRAM + + .eh_frame_hdr : + { + *(.eh_frame_hdr) + *(.eh_frame_entry) + } > SRAM + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } > SRAM + + . = ALIGN(8); + __text_end = .; + __text_size = __text_end - __text_start; + + .data : + { + *(.data) + *(.data.*) + + *(.data1) + *(.data1.*) + + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + + *(.sdata) + *(.sdata.*) + } > SRAM + + . = ALIGN(8); + .ctors : + { + PROVIDE(__ctors_start__ = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE(__ctors_end__ = .); + } > SRAM + + .dtors : + { + PROVIDE(__dtors_start__ = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE(__dtors_end__ = .); + } > SRAM + + /* stack for dual core */ + .stack : + { + . = ALIGN(64); + __stack_start__ = .; + /* Dynamically allocate stack areas according to RT_CPUS_NR */ + . += (__STACKSIZE__ * RT_CPUS_NR); + __stack_end__ = .; + } > SRAM + + .sbss : + { + __bss_start = .; + *(.sbss) + *(.sbss.*) + *(.dynsbss) + *(.scommon) + } > SRAM + + .percpu (NOLOAD) : + { + /* 2MB Align for MMU early map */ + . = ALIGN(0x200000); + PROVIDE(__percpu_start = .); + + *(.percpu) + + /* 2MB Align for MMU early map */ + . = ALIGN(0x200000); + + PROVIDE(__percpu_end = .); + + /* Clone the area */ + . = __percpu_end + (__percpu_end - __percpu_start) * (RT_CPUS_NR - 1); + PROVIDE(__percpu_real_end = .); + } > SRAM + + .bss : + { + *(.bss) + *(.bss.*) + *(.dynbss) + *(COMMON) + __bss_end = .; + } > SRAM + + _end = .; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + * Symbols in the DWARF debugging sections are relative to the beginning + * of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} diff --git a/bsp/xiangshan-verilator/link_cpus.lds b/bsp/xiangshan-verilator/link_cpus.lds new file mode 100644 index 00000000000..e4cd5b88712 --- /dev/null +++ b/bsp/xiangshan-verilator/link_cpus.lds @@ -0,0 +1 @@ +RT_CPUS_NR = 1; diff --git a/bsp/xiangshan-verilator/link_smart.lds b/bsp/xiangshan-verilator/link_smart.lds new file mode 100644 index 00000000000..29d33fdbb1c --- /dev/null +++ b/bsp/xiangshan-verilator/link_smart.lds @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020/12/12 bernard The first version + */ + +INCLUDE "link_stacksize.lds" +INCLUDE "link_cpus.lds" + +OUTPUT_ARCH( "riscv" ) + +/* + * Memory layout: + * 0x80000000 - 0x80200000: SBI + * 0x80200000 - 0x81200000: Kernel + */ + +MEMORY +{ + SRAM : ORIGIN = 0xFFFFFFC000200000, LENGTH = 0x1000000 - 0x200000 +} + +ENTRY(_start) +SECTIONS +{ + /* . = 0x80200000 ; */ + . = 0xFFFFFFC000200000; + + /* __STACKSIZE__ = 4096; */ + __text_start = .; + .start : + { + *(.start); + } > SRAM + + . = ALIGN(8); + + .text : + { + *(.text) /* remaining code */ + *(.text.*) /* remaining code */ + *(.rodata) /* read-only data (constants) */ + *(.rodata*) + *(.glue_7) + *(.glue_7t) + *(.gnu.linkonce.t*) + + /* section information for finsh shell */ + . = ALIGN(8); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + . = ALIGN(8); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + . = ALIGN(8); + + /* section information for initial. */ + . = ALIGN(8); + __rt_init_start = .; + KEEP(*(SORT(.rti_fn*))) + __rt_init_end = .; + . = ALIGN(8); + + __rt_utest_tc_tab_start = .; + KEEP(*(UtestTcTab)) + __rt_utest_tc_tab_end = .; + + . = ALIGN(8); + _etext = .; + } > SRAM + + .eh_frame_hdr : + { + *(.eh_frame_hdr) + *(.eh_frame_entry) + } > SRAM + .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } > SRAM + + . = ALIGN(8); + __text_end = .; + __text_size = __text_end - __text_start; + + .data : + { + *(.data) + *(.data.*) + + *(.data1) + *(.data1.*) + + . = ALIGN(8); + PROVIDE( __global_pointer$ = . + 0x800 ); + + *(.sdata) + *(.sdata.*) + } > SRAM + + . = ALIGN(8); + .ctors : + { + PROVIDE(__ctors_start__ = .); + KEEP(*(SORT(.init_array.*))) + KEEP(*(.init_array)) + PROVIDE(__ctors_end__ = .); + } > SRAM + + .dtors : + { + PROVIDE(__dtors_start__ = .); + KEEP(*(SORT(.fini_array.*))) + KEEP(*(.fini_array)) + PROVIDE(__dtors_end__ = .); + } > SRAM + + /* stack for dual core */ + .stack : + { + . = ALIGN(64); + __stack_start__ = .; + /* Dynamically allocate stack areas according to RT_CPUS_NR */ + . += (__STACKSIZE__ * RT_CPUS_NR); + __stack_end__ = .; + } > SRAM + + .sbss : + { + __bss_start = .; + *(.sbss) + *(.sbss.*) + *(.dynsbss) + *(.scommon) + } > SRAM + + .percpu (NOLOAD) : + { + /* 2MB Align for MMU early map */ + . = ALIGN(0x200000); + PROVIDE(__percpu_start = .); + + *(.percpu) + + /* 2MB Align for MMU early map */ + . = ALIGN(0x200000); + + PROVIDE(__percpu_end = .); + + /* Clone the area */ + . = __percpu_end + (__percpu_end - __percpu_start) * (RT_CPUS_NR - 1); + PROVIDE(__percpu_real_end = .); + } > SRAM + + .bss : + { + *(.bss) + *(.bss.*) + *(.dynbss) + *(COMMON) + __bss_end = .; + } > SRAM + + _end = .; + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + /* DWARF debug sections. + * Symbols in the DWARF debugging sections are relative to the beginning + * of the section so we begin them at 0. */ + /* DWARF 1 */ + .debug 0 : { *(.debug) } + .line 0 : { *(.line) } + /* GNU DWARF 1 extensions */ + .debug_srcinfo 0 : { *(.debug_srcinfo) } + .debug_sfnames 0 : { *(.debug_sfnames) } + /* DWARF 1.1 and DWARF 2 */ + .debug_aranges 0 : { *(.debug_aranges) } + .debug_pubnames 0 : { *(.debug_pubnames) } + /* DWARF 2 */ + .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) } + .debug_abbrev 0 : { *(.debug_abbrev) } + .debug_line 0 : { *(.debug_line) } + .debug_frame 0 : { *(.debug_frame) } + .debug_str 0 : { *(.debug_str) } + .debug_loc 0 : { *(.debug_loc) } + .debug_macinfo 0 : { *(.debug_macinfo) } + /* SGI/MIPS DWARF 2 extensions */ + .debug_weaknames 0 : { *(.debug_weaknames) } + .debug_funcnames 0 : { *(.debug_funcnames) } + .debug_typenames 0 : { *(.debug_typenames) } + .debug_varnames 0 : { *(.debug_varnames) } +} diff --git a/bsp/xiangshan-verilator/link_stacksize.lds b/bsp/xiangshan-verilator/link_stacksize.lds new file mode 100644 index 00000000000..14c2aad91f8 --- /dev/null +++ b/bsp/xiangshan-verilator/link_stacksize.lds @@ -0,0 +1 @@ +__STACKSIZE__ = 16384; diff --git a/bsp/xiangshan-verilator/qemu-dbg.sh b/bsp/xiangshan-verilator/qemu-dbg.sh new file mode 100755 index 00000000000..69f62e7f6fb --- /dev/null +++ b/bsp/xiangshan-verilator/qemu-dbg.sh @@ -0,0 +1,16 @@ +QEMU_CMD="qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin -s -S" + +if grep -q "#define RT_USING_SMP" ./rtconfig.h 2>/dev/null; then + hart_num=$(grep "RT_CPUS_NR = [0-9]*;" ./link_cpus.lds | awk -F'[=;]' '{gsub(/ /, "", $2); print $2}') + if [ -z "$hart_num" ]; then + hart_num=1 + fi + QEMU_CMD="$QEMU_CMD -smp $hart_num" +fi + +QEMU_CMD="$QEMU_CMD \ +-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \ +-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 \ +-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0" + +eval $QEMU_CMD \ No newline at end of file diff --git a/bsp/xiangshan-verilator/qemu-dumpdtb.sh b/bsp/xiangshan-verilator/qemu-dumpdtb.sh new file mode 100755 index 00000000000..12068b571a9 --- /dev/null +++ b/bsp/xiangshan-verilator/qemu-dumpdtb.sh @@ -0,0 +1 @@ +qemu-system-riscv64 -nographic -machine virt,dumpdtb=virt.dtb -m 256M -kernel rtthread.bin diff --git a/bsp/xiangshan-verilator/qemu-nographic.bat b/bsp/xiangshan-verilator/qemu-nographic.bat new file mode 100644 index 00000000000..df55b35e84f --- /dev/null +++ b/bsp/xiangshan-verilator/qemu-nographic.bat @@ -0,0 +1,9 @@ +@echo off +if exist sd.bin goto run +qemu-img create -f raw sd.bin 64M + +:run +qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin ^ +-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 ^ +-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 ^ +-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0 diff --git a/bsp/xiangshan-verilator/qemu-rv64ilp32-nographic.sh b/bsp/xiangshan-verilator/qemu-rv64ilp32-nographic.sh new file mode 100755 index 00000000000..798cb9d3d70 --- /dev/null +++ b/bsp/xiangshan-verilator/qemu-rv64ilp32-nographic.sh @@ -0,0 +1 @@ +qemu-system-riscv64ilp32 -cpu rv64 -M virt -m 256M -nographic -kernel rtthread.elf diff --git a/bsp/xiangshan-verilator/qemu-v-dbg.sh b/bsp/xiangshan-verilator/qemu-v-dbg.sh new file mode 100644 index 00000000000..d22af856cbf --- /dev/null +++ b/bsp/xiangshan-verilator/qemu-v-dbg.sh @@ -0,0 +1,4 @@ +qemu-system-riscv64 -nographic -machine virt -cpu rv64,v=true,vlen=128,vext_spec=v1.0 -m 256M -kernel rtthread.bin \ +-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \ +-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 -s -S \ +-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0 diff --git a/bsp/xiangshan-verilator/qemu-v-nographic.sh b/bsp/xiangshan-verilator/qemu-v-nographic.sh new file mode 100644 index 00000000000..068ff4fa6c3 --- /dev/null +++ b/bsp/xiangshan-verilator/qemu-v-nographic.sh @@ -0,0 +1,9 @@ +if [ ! -f "sd.bin" ]; then +dd if=/dev/zero of=sd.bin bs=1024 count=65536 +mkfs.fat sd.bin +fi + +qemu-system-riscv64 -nographic -machine virt -cpu rv64,v=true,vlen=128,vext_spec=v1.0 -m 256M -kernel rtthread.bin \ +-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \ +-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 \ +-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0 diff --git a/bsp/xiangshan-verilator/rtconfig.h b/bsp/xiangshan-verilator/rtconfig.h new file mode 100644 index 00000000000..d610a63bd8a --- /dev/null +++ b/bsp/xiangshan-verilator/rtconfig.h @@ -0,0 +1,554 @@ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* RT-Thread Kernel */ + +/* klibc options */ + +/* rt_vsnprintf options */ + +#define RT_KLIBC_USING_VSNPRINTF_LONGLONG +#define RT_KLIBC_USING_VSNPRINTF_STANDARD +#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS +#define RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS +#define RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER +#define RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER +#define RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE 32 +#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE 32 +#define RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION 6 +#define RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9 +#define RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS 4 +/* end of rt_vsnprintf options */ + +/* rt_vsscanf options */ + +/* end of rt_vsscanf options */ + +/* rt_memset options */ + +/* end of rt_memset options */ + +/* rt_memcpy options */ + +/* end of rt_memcpy options */ + +/* rt_memmove options */ + +/* end of rt_memmove options */ + +/* rt_memcmp options */ + +/* end of rt_memcmp options */ + +/* rt_strstr options */ + +/* end of rt_strstr options */ + +/* rt_strcasecmp options */ + +/* end of rt_strcasecmp options */ + +/* rt_strncpy options */ + +/* end of rt_strncpy options */ + +/* rt_strcpy options */ + +/* end of rt_strcpy options */ + +/* rt_strncmp options */ + +/* end of rt_strncmp options */ + +/* rt_strcmp options */ + +/* end of rt_strcmp options */ + +/* rt_strlen options */ + +/* end of rt_strlen options */ + +/* rt_strnlen options */ + +/* end of rt_strnlen options */ +/* end of klibc options */ +#define RT_NAME_MAX 24 +#define RT_CPUS_NR 1 +#define RT_ALIGN_SIZE 8 +#define RT_THREAD_PRIORITY_32 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 100 +#define RT_USING_OVERFLOW_CHECK +#define RT_USING_HOOK +#define RT_HOOK_USING_FUNC_PTR +#define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 4 +#define IDLE_THREAD_STACK_SIZE 16384 +#define RT_USING_TIMER_SOFT +#define RT_TIMER_THREAD_PRIO 4 +#define RT_TIMER_THREAD_STACK_SIZE 16384 +#define RT_USING_CPU_USAGE_TRACER + +/* kservice options */ + +/* end of kservice options */ +#define RT_USING_DEBUG +#define RT_DEBUGING_ASSERT +#define RT_DEBUGING_COLOR +#define RT_DEBUGING_CONTEXT + +/* Inter-Thread communication */ + +#define RT_USING_SEMAPHORE +#define RT_USING_MUTEX +#define RT_USING_EVENT +#define RT_USING_MAILBOX +#define RT_USING_MESSAGEQUEUE +#define RT_USING_SIGNALS +/* end of Inter-Thread communication */ + +/* Memory Management */ + +#define RT_USING_MEMPOOL +#define RT_USING_SLAB +#define RT_USING_SLAB_AS_HEAP +#define RT_USING_MEMTRACE +#define RT_USING_HEAP +/* end of Memory Management */ +#define RT_USING_DEVICE +#define RT_USING_DEVICE_OPS +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 256 +#define RT_CONSOLE_DEVICE_NAME "uart0" +#define RT_VER_NUM 0x50300 +#define RT_BACKTRACE_LEVEL_MAX_NR 32 +/* end of RT-Thread Kernel */ +#define ARCH_CPU_64BIT +#define RT_USING_CACHE +#define ARCH_MM_MMU +#define ARCH_RISCV +#define ARCH_RISCV64 +#define ARCH_USING_NEW_CTX_SWITCH +#define ARCH_USING_RISCV_COMMON64 +#define ARCH_REMAP_KERNEL + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 8388608 +#define RT_MAIN_THREAD_PRIORITY 10 +#define RT_USING_MSH +#define RT_USING_FINSH +#define FINSH_USING_MSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 16384 +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 10 +#define FINSH_USING_SYMTAB +#define FINSH_CMD_SIZE 80 +#define MSH_USING_BUILT_IN_COMMANDS +#define FINSH_USING_DESCRIPTION +#define FINSH_ARG_MAX 10 +#define FINSH_USING_OPTION_COMPLETION + +/* DFS: device virtual file system */ + +#define RT_USING_DFS +#define DFS_USING_POSIX +#define DFS_USING_WORKDIR +#define DFS_FD_MAX 32 +#define RT_USING_DFS_V2 +#define RT_USING_DFS_ELMFAT + +/* elm-chan's FatFs, Generic FAT Filesystem Module */ + +#define RT_DFS_ELM_CODE_PAGE 437 +#define RT_DFS_ELM_WORD_ACCESS +#define RT_DFS_ELM_USE_LFN_3 +#define RT_DFS_ELM_USE_LFN 3 +#define RT_DFS_ELM_LFN_UNICODE_0 +#define RT_DFS_ELM_LFN_UNICODE 0 +#define RT_DFS_ELM_MAX_LFN 255 +#define RT_DFS_ELM_DRIVES 2 +#define RT_DFS_ELM_MAX_SECTOR_SIZE 512 +#define RT_DFS_ELM_REENTRANT +#define RT_DFS_ELM_MUTEX_TIMEOUT 3000 +/* end of elm-chan's FatFs, Generic FAT Filesystem Module */ +#define RT_USING_DFS_DEVFS +#define RT_USING_DFS_ROMFS +/* end of DFS: device virtual file system */ + +/* Device Drivers */ + +#define RT_USING_DEVICE_IPC +#define RT_UNAMED_PIPE_NUMBER 64 +#define RT_USING_SYSTEM_WORKQUEUE +#define RT_SYSTEM_WORKQUEUE_STACKSIZE 8192 +#define RT_SYSTEM_WORKQUEUE_PRIORITY 23 +#define RT_USING_SERIAL +#define RT_USING_SERIAL_V1 +#define RT_SERIAL_USING_DMA +#define RT_SERIAL_RB_BUFSZ 64 +#define RT_USING_CPUTIME +#define RT_USING_CPUTIME_RISCV +#define CPUTIME_TIMER_FREQ 10000000 +#define RT_USING_NULL +#define RT_USING_ZERO +#define RT_USING_RANDOM +#define RT_USING_RTC +#define RT_USING_SOFT_RTC +#define RT_USING_KTIME +/* end of Device Drivers */ + +/* C/C++ and POSIX layer */ + +/* ISO-ANSI C layer */ + +/* Timezone and Daylight Saving Time */ + +#define RT_LIBC_USING_LIGHT_TZ_DST +#define RT_LIBC_TZ_DEFAULT_HOUR 8 +#define RT_LIBC_TZ_DEFAULT_MIN 0 +#define RT_LIBC_TZ_DEFAULT_SEC 0 +/* end of Timezone and Daylight Saving Time */ +/* end of ISO-ANSI C layer */ + +/* POSIX (Portable Operating System Interface) layer */ + +#define RT_USING_POSIX_FS +#define RT_USING_POSIX_DEVIO +#define RT_USING_POSIX_STDIO +#define RT_USING_POSIX_POLL +#define RT_USING_POSIX_SELECT +#define RT_USING_POSIX_TERMIOS +#define RT_USING_POSIX_AIO +#define RT_USING_POSIX_MMAN +#define RT_USING_POSIX_DELAY +#define RT_USING_POSIX_CLOCK +#define RT_USING_POSIX_TIMER + +/* Interprocess Communication (IPC) */ + +#define RT_USING_POSIX_PIPE +#define RT_USING_POSIX_PIPE_SIZE 512 + +/* Socket is in the 'Network' category */ + +/* end of Interprocess Communication (IPC) */ +/* end of POSIX (Portable Operating System Interface) layer */ +#define RT_USING_CPLUSPLUS +#define RT_USING_CPP_WRAPPER +/* end of C/C++ and POSIX layer */ + +/* Network */ + +#define RT_USING_SAL +#define SAL_INTERNET_CHECK +#define SOCKET_TABLE_STEP_LEN 4 + +/* Docking with protocol stacks */ + +#define SAL_USING_LWIP +/* end of Docking with protocol stacks */ +#define SAL_USING_POSIX +#define RT_USING_NETDEV +#define NETDEV_USING_IFCONFIG +#define NETDEV_USING_PING +#define NETDEV_USING_NETSTAT +#define NETDEV_USING_AUTO_DEFAULT +#define NETDEV_IPV4 1 +#define NETDEV_IPV6 0 +#define RT_USING_LWIP +#define RT_USING_LWIP203 +#define RT_USING_LWIP_VER_NUM 0x20003 +#define RT_LWIP_MEM_ALIGNMENT 4 +#define RT_LWIP_IGMP +#define RT_LWIP_ICMP +#define RT_LWIP_DNS +#define RT_LWIP_DHCP +#define IP_SOF_BROADCAST 1 +#define IP_SOF_BROADCAST_RECV 1 + +/* Static IPv4 Address */ + +#define RT_LWIP_IPADDR "192.168.1.30" +#define RT_LWIP_GWADDR "192.168.1.1" +#define RT_LWIP_MSKADDR "255.255.255.0" +/* end of Static IPv4 Address */ +#define RT_LWIP_UDP +#define RT_LWIP_TCP +#define RT_LWIP_RAW +#define RT_MEMP_NUM_NETCONN 8 +#define RT_LWIP_PBUF_NUM 16 +#define RT_LWIP_RAW_PCB_NUM 4 +#define RT_LWIP_UDP_PCB_NUM 4 +#define RT_LWIP_TCP_PCB_NUM 4 +#define RT_LWIP_TCP_SEG_NUM 40 +#define RT_LWIP_TCP_SND_BUF 8196 +#define RT_LWIP_TCP_WND 8196 +#define RT_LWIP_TCPTHREAD_PRIORITY 10 +#define RT_LWIP_TCPTHREAD_MBOX_SIZE 8 +#define RT_LWIP_TCPTHREAD_STACKSIZE 8192 +#define RT_LWIP_ETHTHREAD_PRIORITY 12 +#define RT_LWIP_ETHTHREAD_STACKSIZE 8192 +#define RT_LWIP_ETHTHREAD_MBOX_SIZE 8 +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_LINK_CALLBACK 1 +#define RT_LWIP_NETIF_NAMESIZE 6 +#define SO_REUSE 1 +#define LWIP_SO_RCVTIMEO 1 +#define LWIP_SO_SNDTIMEO 1 +#define LWIP_SO_RCVBUF 1 +#define LWIP_SO_LINGER 0 +#define LWIP_NETIF_LOOPBACK 0 +#define RT_LWIP_USING_PING +/* end of Network */ + +/* Memory protection */ + +/* end of Memory protection */ + +/* Utilities */ + +#define RT_USING_UTEST +#define UTEST_THR_STACK_SIZE 4096 +#define UTEST_THR_PRIORITY 20 +#define RT_UTEST_MAX_OPTIONS 64 +#define RT_USING_RESOURCE_ID +#define RT_USING_ADT +#define RT_USING_ADT_AVL +#define RT_USING_ADT_BITMAP +#define RT_USING_ADT_HASHMAP +#define RT_USING_ADT_REF +/* end of Utilities */ + +/* Memory management */ + +#define RT_PAGE_AFFINITY_BLOCK_SIZE 0x1000 +#define RT_PAGE_MAX_ORDER 11 + +/* Debugging */ + +/* end of Debugging */ +/* end of Memory management */ + +/* Using USB legacy version */ + +/* end of Using USB legacy version */ +/* end of RT-Thread Components */ + +/* RT-Thread Utestcases */ + +/* end of RT-Thread Utestcases */ + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + + +/* Wi-Fi */ + +/* Marvell WiFi */ + +/* end of Marvell WiFi */ + +/* Wiced WiFi */ + +/* end of Wiced WiFi */ + +/* CYW43012 WiFi */ + +/* end of CYW43012 WiFi */ + +/* BL808 WiFi */ + +/* end of BL808 WiFi */ + +/* CYW43439 WiFi */ + +/* end of CYW43439 WiFi */ +/* end of Wi-Fi */ + +/* IoT Cloud */ + +/* end of IoT Cloud */ +/* end of IoT - internet of things */ + +/* security packages */ + +/* end of security packages */ + +/* language packages */ + +/* JSON: JavaScript Object Notation, a lightweight data-interchange format */ + +/* end of JSON: JavaScript Object Notation, a lightweight data-interchange format */ + +/* XML: Extensible Markup Language */ + +/* end of XML: Extensible Markup Language */ +/* end of language packages */ + +/* multimedia packages */ + +/* LVGL: powerful and easy-to-use embedded GUI library */ + +/* end of LVGL: powerful and easy-to-use embedded GUI library */ + +/* u8g2: a monochrome graphic library */ + +/* end of u8g2: a monochrome graphic library */ +/* end of multimedia packages */ + +/* tools packages */ + +/* end of tools packages */ + +/* system packages */ + +/* enhanced kernel services */ + +/* end of enhanced kernel services */ + +/* acceleration: Assembly language or algorithmic acceleration packages */ + +/* end of acceleration: Assembly language or algorithmic acceleration packages */ + +/* CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + +/* end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */ + +/* Micrium: Micrium software products porting for RT-Thread */ + +/* end of Micrium: Micrium software products porting for RT-Thread */ +/* end of system packages */ + +/* peripheral libraries and drivers */ + +/* HAL & SDK Drivers */ + +/* STM32 HAL & SDK Drivers */ + +/* end of STM32 HAL & SDK Drivers */ + +/* Infineon HAL Packages */ + +/* end of Infineon HAL Packages */ + +/* Kendryte SDK */ + +/* end of Kendryte SDK */ + +/* WCH HAL & SDK Drivers */ + +/* end of WCH HAL & SDK Drivers */ + +/* AT32 HAL & SDK Drivers */ + +/* end of AT32 HAL & SDK Drivers */ + +/* HC32 DDL Drivers */ + +/* end of HC32 DDL Drivers */ + +/* NXP HAL & SDK Drivers */ + +/* end of NXP HAL & SDK Drivers */ + +/* NUVOTON Drivers */ + +/* end of NUVOTON Drivers */ + +/* GD32 Drivers */ + +/* end of GD32 Drivers */ +/* end of HAL & SDK Drivers */ + +/* sensors drivers */ + +/* end of sensors drivers */ + +/* touch drivers */ + +/* end of touch drivers */ +/* end of peripheral libraries and drivers */ + +/* AI packages */ + +/* end of AI packages */ + +/* Signal Processing and Control Algorithm Packages */ + +/* end of Signal Processing and Control Algorithm Packages */ + +/* miscellaneous packages */ + +/* project laboratory */ + +/* end of project laboratory */ + +/* samples: kernel and components samples */ + +/* end of samples: kernel and components samples */ + +/* entertainment: terminal games and other interesting software packages */ + +/* end of entertainment: terminal games and other interesting software packages */ +/* end of miscellaneous packages */ + +/* Arduino libraries */ + + +/* Projects and Demos */ + +/* end of Projects and Demos */ + +/* Sensors */ + +/* end of Sensors */ + +/* Display */ + +/* end of Display */ + +/* Timing */ + +/* end of Timing */ + +/* Data Processing */ + +/* end of Data Processing */ + +/* Data Storage */ + +/* Communication */ + +/* end of Communication */ + +/* Device Control */ + +/* end of Device Control */ + +/* Other */ + +/* end of Other */ + +/* Signal IO */ + +/* end of Signal IO */ + +/* Uncategorized */ + +/* end of Arduino libraries */ +/* end of RT-Thread online packages */ + +/* XiangShan configs */ + +/* end of XiangShan configs */ +#define BOARD_XIANGSHAN +#define PLIC_BASE 0x3c000000 +#define __STACKSIZE__ 16384 + +#endif diff --git a/bsp/xiangshan-verilator/rtconfig.py b/bsp/xiangshan-verilator/rtconfig.py new file mode 100644 index 00000000000..d70d9fc1a02 --- /dev/null +++ b/bsp/xiangshan-verilator/rtconfig.py @@ -0,0 +1,51 @@ +import os + +# toolchains options +ARCH ='risc-v' +CPU ='virt64' +CROSS_TOOL ='llvm-riscv' + +RTT_ROOT = os.getenv('RTT_ROOT') or os.path.join(os.getcwd(), '..', '..') + +if os.getenv('RTT_CC'): + CROSS_TOOL = os.getenv('RTT_CC') + +if CROSS_TOOL == 'llvm-riscv': + PLATFORM = 'llvm-riscv' + EXEC_PATH = os.getenv('RTT_EXEC_PATH') or '/usr/bin' +else: + print('Please make sure your toolchains is LLVM RISC-V!') + exit(0) + +BUILD = 'release' + +if PLATFORM == 'llvm-riscv': + # toolchains + PREFIX = os.getenv('RTT_CC_PREFIX') or 'riscv64-unknown-elf-' + CC = PREFIX + 'clang' + CXX = PREFIX + 'clang++' + AS = PREFIX + 'clang' + AR = PREFIX + 'llvm-ar' + LINK = PREFIX + 'clang++' + TARGET_EXT = 'elf' + SIZE = PREFIX + 'size' + OBJDUMP = PREFIX + 'objdump' + OBJCPY = PREFIX + 'objcopy' + + DEVICE = ' -mcmodel=medany -march=rv64imac -mabi=lp64 ' + CFLAGS = DEVICE + '-fno-omit-frame-pointer -flax-vector-conversions -Wno-cpp -fno-common -ffunction-sections -fdata-sections -fdiagnostics-color=always -Xclang -fexperimental-max-bitint-width=20000 -fbracket-depth=2048 -Wno-parentheses-equality -DGSIM' + AFLAGS = ' -c' + DEVICE + ' -x assembler-with-cpp -D__ASSEMBLY__ ' + LFLAGS = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,_start -T link.lds' + ' -stdlib=libstdc++ -lc -lsupc++ -lgcc -lstdc++ -static' + CPATH = '' + LPATH = '' + + if BUILD == 'debug': + CFLAGS += ' -O0 -ggdb ' + AFLAGS += ' -ggdb' + else: + CFLAGS += ' -O3' + + CXXFLAGS = CFLAGS + +DUMP_ACTION = OBJDUMP + ' -D -S $TARGET > rtthread.asm\n' +POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread.bin\n' + SIZE + ' $TARGET \n' diff --git a/bsp/xiangshan-verilator/run.sh b/bsp/xiangshan-verilator/run.sh new file mode 100755 index 00000000000..c332915098c --- /dev/null +++ b/bsp/xiangshan-verilator/run.sh @@ -0,0 +1,43 @@ +#!/bin/bash + +usage () +{ + echo "Usage:" + echo "./run.sh []" + echo "Note: if is not provided, will create a 'sd.bin'" + echo "in the current directory and load it by default." +} + +path_image=${1} + +if [ -z $path_image ]; then + path_image="./sd.bin" + if [ ! -f $path_image ]; then + dd if=/dev/zero of=$path_image bs=1024 count=65536 + mkfs.fat $path_image + fi +fi + +if [ ! -f $path_image ]; then + echo "ERROR: $path_image does not exist!" + usage + exit +fi + +QEMU_CMD="qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin" + +if grep -q "#define RT_USING_SMP" ./rtconfig.h 2>/dev/null; then + hart_num=$(grep "RT_CPUS_NR = [0-9]*;" ./link_cpus.lds 2>/dev/null | awk -F'[=;]' '{gsub(/ /, "", $2); print $2}') + if [ -z "$hart_num" ] || [ "$hart_num" -lt 1 ]; then + echo "Warning: Invalid or missing RT_CPUS_NR, defaulting to 1" + hart_num=1 + fi + QEMU_CMD="$QEMU_CMD -smp $hart_num" +fi + +QEMU_CMD="$QEMU_CMD \ +-drive if=none,file=$path_image,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \ +-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 \ +-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0" + +eval $QEMU_CMD \ No newline at end of file diff --git a/bsp/xiangshan-verilator/smart-env.bat b/bsp/xiangshan-verilator/smart-env.bat new file mode 100644 index 00000000000..87c310560fc --- /dev/null +++ b/bsp/xiangshan-verilator/smart-env.bat @@ -0,0 +1,30 @@ + +@set def_arch=arm + +@if not "%1"=="" ( + @set def_arch=%1 +) + +@if %def_arch%==arm ( + @set RTT_CC=gcc + @set RTT_EXEC_PATH=%cd%\tools\gnu_gcc\arm-linux-musleabi_for_i686-w64-mingw32\bin + @set RTT_CC_PREFIX=arm-linux-musleabi- + @copy configs\def_config_arm .config +) else if %def_arch%==riscv64 ( + @set RTT_CC=gcc + @set RTT_EXEC_PATH=E:\workspace\rt-smart\userapps\tools\gnu_gcc\riscv64-linux-musleabi_for_i686-w64-mingw32\bin + @set RTT_CC_PREFIX=riscv64-unknown-linux-musl- + @copy configs\def_config_riscv64 .config +) else ( + @echo "ERROR:supported_arch=arm riscv64!" + @goto EXIT +) + +@set PATH=%RTT_EXEC_PATH%;%PATH% + +@echo "Arch => %def_arch%" +@echo "CC => %RTT_CC%" +@echo "PREFIX => %RTT_CC_PREFIX%" +@echo "EXEC_PATH => %RTT_EXEC_PATH%" + +:EXIT \ No newline at end of file From f3535b2fa34134eb77e7cf72267d542756787ae7 Mon Sep 17 00:00:00 2001 From: Haojin Tang Date: Thu, 5 Mar 2026 16:05:53 +0800 Subject: [PATCH 03/18] feat(xiangshan-verilator): copy verilator runtime from v5.046 --- .../verilator_runtime/verilated.cpp | 3726 +++++++++++++++ .../verilator_runtime/verilated.h | 1056 +++++ .../verilator_runtime/verilated_config.h | 30 + .../verilator_runtime/verilated_cov.cpp | 541 +++ .../verilator_runtime/verilated_cov.h | 249 + .../verilator_runtime/verilated_cov_key.h | 85 + .../verilator_runtime/verilated_dpi.cpp | 814 ++++ .../verilator_runtime/verilated_dpi.h | 112 + .../verilator_runtime/verilated_fst_c.cpp | 405 ++ .../verilator_runtime/verilated_fst_c.h | 262 ++ .../verilator_runtime/verilated_fst_sc.cpp | 24 + .../verilator_runtime/verilated_fst_sc.h | 56 + .../verilator_runtime/verilated_funcs.h | 3059 +++++++++++++ .../verilator_runtime/verilated_imp.h | 616 +++ .../verilator_runtime/verilated_intrinsics.h | 45 + .../verilator_runtime/verilated_probdist.cpp | 240 + .../verilator_runtime/verilated_profiler.cpp | 225 + .../verilator_runtime/verilated_profiler.h | 305 ++ .../verilator_runtime/verilated_random.cpp | 966 ++++ .../verilator_runtime/verilated_random.h | 677 +++ .../verilator_runtime/verilated_saif_c.cpp | 669 +++ .../verilator_runtime/verilated_saif_c.h | 292 ++ .../verilator_runtime/verilated_saif_sc.h | 56 + .../verilator_runtime/verilated_save.cpp | 270 ++ .../verilator_runtime/verilated_save.h | 335 ++ .../verilator_runtime/verilated_sc.h | 52 + .../verilator_runtime/verilated_sc_trace.h | 247 + .../verilator_runtime/verilated_sym_props.h | 278 ++ .../verilator_runtime/verilated_syms.h | 75 + .../verilator_runtime/verilated_threads.cpp | 281 ++ .../verilator_runtime/verilated_threads.h | 260 ++ .../verilator_runtime/verilated_timing.cpp | 271 ++ .../verilator_runtime/verilated_timing.h | 482 ++ .../verilator_runtime/verilated_trace.h | 648 +++ .../verilator_runtime/verilated_trace_imp.h | 936 ++++ .../verilator_runtime/verilated_types.h | 2139 +++++++++ .../verilator_runtime/verilated_vcd_c.cpp | 685 +++ .../verilator_runtime/verilated_vcd_c.h | 329 ++ .../verilator_runtime/verilated_vcd_sc.cpp | 24 + .../verilator_runtime/verilated_vcd_sc.h | 57 + .../verilator_runtime/verilated_vpi.cpp | 4029 +++++++++++++++++ .../verilator_runtime/verilated_vpi.h | 72 + .../verilator_runtime/verilatedos.h | 753 +++ .../verilator_runtime/verilatedos_c.h | 219 + 44 files changed, 26952 insertions(+) create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_config.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov_key.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_funcs.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_imp.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_intrinsics.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_probdist.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_sc.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc_trace.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sym_props.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_syms.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace_imp.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_types.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.cpp create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos.h create mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos_c.h diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.cpp new file mode 100644 index 00000000000..86a891df8e5 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.cpp @@ -0,0 +1,3726 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//========================================================================= +/// +/// \file +/// \brief Verilated general routine implementation code +/// +/// This file must be compiled and linked against all Verilated objects +/// (all code created from Verilator). +/// +/// Verilator always adds this file to the Makefile for the linker. +/// +/// Those macro/function/variable starting or ending in _ are internal, +/// however many of the other function/macros here are also internal. +/// +//========================================================================= +// Internal note: +// +// verilated.o may exist both in --lib-create (incrementally linked .a/.so) +// and the main module. Both refer the same instance of static +// variables/thread_local in verilated.o such as Verilated, or +// VerilatedImpData. This is important to share that state, but the +// sharing may cause a double-free error when shutting down because the +// loader will insert a constructor/destructor at each reference to +// verilated.o, resulting in at runtime constructors/destructors being +// called multiple times. +// +// To avoid the trouble: +// * Statics declared inside functions. The compiler will wrap +// the construction in must-be-one-time checks. +// * Or, use only C++20 constinit types. (TODO: Make a VL_CONSTINIT). +// * Or, use types that are multi-constructor safe. +// * Or, the static should be of a union, which will avoid compiler +// construction, and appropriately check for duplicate construction. +// * Or, code is not linked in protected library. e.g. the VPI +// and DPI libraries are not needed there. +//========================================================================= + +#define VERILATOR_VERILATED_CPP_ + +#include "verilated_config.h" +#include "verilatedos.h" + +#include "verilated_imp.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include // mkdir + +// clang-format off +#if defined(_WIN32) || defined(__MINGW32__) +# include // mkdir +#endif +#ifdef __GLIBC__ +# include +# include +# define _VL_HAVE_STACKTRACE +#endif +#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__)) +# include +# include +# define _VL_HAVE_GETRLIMIT +#endif + +#include "verilated_threads.h" +// clang-format on + +#include "verilated_trace.h" + +#ifdef VM_SOLVER_DEFAULT +#define VL_SOLVER_DEFAULT VM_SOLVER_DEFAULT +#else +#define VL_SOLVER_DEFAULT "z3 --in" +#endif + +// Max characters in static char string for VL_VALUE_STRING +constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192; + +//=========================================================================== +// Static sanity checks + +static_assert(sizeof(uint8_t) == 1, "uint8_t is missized"); +static_assert(sizeof(uint16_t) == 2, "uint8_t is missized"); +static_assert(sizeof(uint32_t) == 4, "uint8_t is missized"); +static_assert(sizeof(uint64_t) == 8, "uint8_t is missized"); + +//=========================================================================== +// Global variables +// Internal note: Globals may multi-construct, see verilated.cpp top. + +// Fast path, keep together +int Verilated::s_debug = 0; +VerilatedContext* Verilated::s_lastContextp = nullptr; + +// Keep below together in one cache line +// Internal note: Globals may multi-construct, see verilated.cpp top. +thread_local Verilated::ThreadLocal Verilated::t_s; + +//=========================================================================== +// Warning print helper + +void vl_print_warn_error(const char* prefix, const char* filename, int linenum, + const char* msg) VL_MT_UNSAFE { + // A msg of "ERRORCODE: ..." is a code that changes to a prefix, e.g. "%Error-ERRORCODE: ..." + // This avoids changing public API of the vl_stop and related functions. + const char* msgNoCp = msg; + for (; isupper(*msgNoCp); ++msgNoCp); + if (msgNoCp[0] == ':' && msgNoCp[1] == ' ') { + const int codeWidth = static_cast(msgNoCp - msg); + msgNoCp += 2; + if (filename && filename[0]) { + VL_PRINTF( // Not VL_PRINTF_MT, already on main thread + "%s-%.*s: %s:%d: %s\n", prefix, codeWidth, msg, filename, linenum, msgNoCp); + } else { + VL_PRINTF( // Not VL_PRINTF_MT, already on main thread + "%s-%.*s: %s\n", prefix, codeWidth, msg, msgNoCp); + } + } else { + if (filename && filename[0]) { + VL_PRINTF( // Not VL_PRINTF_MT, already on main thread + "%s: %s:%d: %s\n", prefix, filename, linenum, msg); + } else { + VL_PRINTF( // Not VL_PRINTF_MT, already on main thread + "%s: %s\n", prefix, msg); + } + } +} + +//=========================================================================== +// User definable functions +// Note a TODO is a future version of the API will pass a structure so that +// the calling arguments allow for extension + +#ifndef VL_USER_FINISH ///< Define this to override the vl_finish function +void vl_finish(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE { + (void)hier; // hier is unused in the default implementation. + VL_PRINTF( // Not VL_PRINTF_MT, already on main thread + "- %s:%d: Verilog $finish\n", filename, linenum); + Verilated::threadContextp()->gotFinish(true); +} +#endif + +#ifndef VL_USER_STOP ///< Define this to override the vl_stop function +void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE { + // $stop or $fatal reporting; would break current API to add param as to which + if (Verilated::threadContextp()->gotFinish()) return; + const char* const msg = "Verilog $stop"; + Verilated::threadContextp()->gotError(true); + Verilated::threadContextp()->gotFinish(true); + if (Verilated::threadContextp()->fatalOnError()) { + vl_fatal(filename, linenum, hier, msg); + } else { + vl_print_warn_error("%Error", filename, linenum, msg); + Verilated::runFlushCallbacks(); + } +} +#endif + +#ifndef VL_USER_FATAL ///< Define this to override the vl_fatal function +void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE { + (void)hier; // hier is unused in the default implementation. + Verilated::threadContextp()->gotError(true); + Verilated::threadContextp()->gotFinish(true); + vl_print_warn_error("%Error", filename, linenum, msg); + Verilated::runFlushCallbacks(); + + VL_PRINTF("Aborting...\n"); // Not VL_PRINTF_MT, already on main thread + + // Second flush in case VL_PRINTF does something needing a flush + Verilated::runFlushCallbacks(); + + // Callbacks prior to termination + Verilated::runExitCallbacks(); + + if (Verilated::debug()) { + std::abort(); + } else { + std::exit(1); + } +} +#endif + +#ifndef VL_USER_STOP_MAYBE ///< Define this to override the vl_stop_maybe function +void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_UNSAFE { + // $stop or $fatal + Verilated::threadContextp()->errorCountInc(); + if (maybe + && Verilated::threadContextp()->errorCount() < Verilated::threadContextp()->errorLimit()) { + // Do just once when cross error limit + if (Verilated::threadContextp()->errorCount() == 1) { + vl_print_warn_error("-Info", filename, linenum, + "Verilog $stop, ignored due to +verilator+error+limit"); + } + } else { + vl_stop(filename, linenum, hier); + } +} +#endif + +#ifndef VL_USER_WARN ///< Define this to override the vl_warn function +void vl_warn(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE { + (void)hier; // hier is unused in the default implementation. + vl_print_warn_error("%Warning", filename, linenum, msg); + Verilated::runFlushCallbacks(); +} +#endif + +//=========================================================================== +// Wrapper to call certain functions via messages when multithreaded + +void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE { + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // + vl_finish(filename, linenum, hier); + }}); +} + +void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE { + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // + vl_stop_maybe(filename, linenum, hier, maybe); + }}); +} + +void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE { + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // + vl_fatal(filename, linenum, hier, msg); + }}); +} + +void VL_WARN_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE { + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // + vl_warn(filename, linenum, hier, msg); + }}); +} + +//=========================================================================== +// Debug prints + +// sprintf but return as string (this isn't fast, for print messages only) +std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE { + va_list aq; + va_copy(aq, ap); + const size_t len = VL_VSNPRINTF(nullptr, 0, formatp, aq); + va_end(aq); + if (VL_UNLIKELY(len < 1)) return ""; + + char* const bufp = new char[len + 1]; + VL_VSNPRINTF(bufp, len + 1, formatp, ap); + + std::string result{bufp, len}; // Not const to allow move optimization + delete[] bufp; + return result; +} + +uint64_t _vl_dbg_sequence_number() VL_MT_SAFE { + static std::atomic s_sequence; + return ++s_sequence; +} + +uint32_t VL_THREAD_ID() VL_MT_SAFE { + // Alternative is to use std::this_thread::get_id, but that returns a + // hard-to-read number and is very slow + static std::atomic s_nextId(0); + static thread_local uint32_t t_myId = ++s_nextId; + return t_myId; +} + +void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE { + // We're still using c printf formats instead of operator<< so we can avoid the heavy + // includes that otherwise would be required in every Verilated module + va_list ap; + va_start(ap, formatp); + const std::string result = _vl_string_vprintf(formatp, ap); + va_end(ap); + // printf("-imm-V{t%d,%" PRId64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), + // result.c_str()); + + // Using VL_PRINTF not VL_PRINTF_MT so that we can call VL_DBG_MSGF + // from within the guts of the thread execution machinery (and it goes + // to the screen and not into the queues we're debugging) + VL_PRINTF("-V{t%u,%" PRIu64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), result.c_str()); +} + +void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE { + va_list ap; + va_start(ap, formatp); + const std::string result = _vl_string_vprintf(formatp, ap); + va_end(ap); + VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() { // + VL_PRINTF("%s", result.c_str()); + }}); +} + +//=========================================================================== +// Process -- parts of std::process implementation + +std::string VlProcess::randstate() const VL_MT_UNSAFE { + return VlRNG::vl_thread_rng().get_randstate(); +} +void VlProcess::randstate(const std::string& state) VL_MT_UNSAFE { + VlRNG::vl_thread_rng().set_randstate(state); +} + +//=========================================================================== +// Random -- Mostly called at init time, so not inline. + +static std::pair vl_splitmix64(uint64_t x) VL_PURE { + // SplitMix64 algorithm, copied under public domain from + // https://prng.di.unimi.it/splitmix64.c + // by Sebastiano Vigna + uint64_t z = (x += 0x9e3779b97f4a7c15ULL); + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; + z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; + return {x, z ^ (z >> 31)}; +} + +// Xoroshiro128** algorithm, copied under public domain from +// https://xoshiro.di.unimi.it/xoroshiro128starstar.c +// by David Blackman and Sebastiano Vigna + +static uint64_t vl_rolt(const uint64_t x, int k) VL_PURE { return (x << k) | (x >> (64 - k)); } + +static std::array vl_rng_state_from_seed(uint64_t seed) VL_PURE { + const auto split1 = vl_splitmix64(seed); + const auto split2 = vl_splitmix64(split1.first); + return {split1.second, split2.second}; +} + +static uint64_t vl_rng_result(const std::array& state) VL_PURE { + const uint64_t s0 = state[0]; + return vl_rolt(s0 * 5, 7) * 9; +} + +static std::array +vl_rng_compute_new_state(const std::array& current_state) VL_PURE { + const uint64_t s0 = current_state[0]; + uint64_t s1 = current_state[1]; + + s1 ^= s0; + const uint64_t new_s0 = vl_rolt(s0, 24) ^ s1 ^ (s1 << 16); // a, b + const uint64_t new_s1 = vl_rolt(s1, 37); // c + + return {new_s0, new_s1}; +} + +VlRNG::VlRNG() VL_MT_SAFE { + VlRNG& fromr = vl_thread_rng(); + + const uint64_t s0 = vl_rng_result(fromr.m_state); + fromr.m_state = vl_rng_compute_new_state(fromr.m_state); + + const uint64_t s1 = vl_rng_result(fromr.m_state); + fromr.m_state = vl_rng_compute_new_state(fromr.m_state); + + m_state = {s0, s1}; +} + +VlRNG::VlRNG(uint64_t seed) VL_PURE { m_state = vl_rng_state_from_seed(seed); } +void VlRNG::srandom(uint64_t n) VL_MT_UNSAFE { m_state = vl_rng_state_from_seed(n); } + +uint64_t VlRNG::rand64() VL_MT_UNSAFE { + const uint64_t result = vl_rng_result(m_state); + m_state = vl_rng_compute_new_state(m_state); + return result; +} +uint64_t VlRNG::vl_thread_rng_rand64() VL_MT_SAFE { + VlRNG& fromr = vl_thread_rng(); + const uint64_t result = vl_rng_result(fromr.m_state); + fromr.m_state = vl_rng_compute_new_state(fromr.m_state); + return result; +} + +std::string VlRNG::get_randstate() const VL_MT_UNSAFE { + // Though not stated in IEEE, assumption is the string must be printable + const char* const stateCharsp = reinterpret_cast(&m_state); + static_assert(sizeof(m_state) == 16, ""); + std::string result{"R00112233445566770011223344556677"}; + for (size_t i = 0; i < sizeof(m_state); ++i) { + result[1 + i * 2] = 'a' + ((stateCharsp[i] >> 4) & 15); + result[1 + i * 2 + 1] = 'a' + (stateCharsp[i] & 15); + } + return result; +} +void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE { + if (VL_UNLIKELY((state.length() != 1 + 2 * sizeof(m_state)) || (state[0] != 'R'))) { + VL_PRINTF_MT("%%Warning: set_randstate ignored as state string not from get_randstate\n"); + return; + } + char* const stateCharsp = reinterpret_cast(&m_state); + for (size_t i = 0; i < sizeof(m_state); ++i) { + stateCharsp[i] + = (((state[1 + i * 2] - 'a') & 15) << 4) | ((state[1 + i * 2 + 1] - 'a') & 15); + } +} + +static uint32_t vl_sys_rand32() VL_MT_SAFE { + // Return random 32-bits using system library. + // Used only to construct seed for Verilator's PRNG. + static VerilatedMutex s_mutex; + const VerilatedLockGuard lock{s_mutex}; // Otherwise rand is unsafe +#if defined(_WIN32) && !defined(__CYGWIN__) + // Windows doesn't have lrand48(), although Cygwin does. + return (std::rand() << 16) ^ std::rand(); +#else + return (lrand48() << 16) ^ lrand48(); +#endif +} + +VlRNG& VlRNG::vl_thread_rng() VL_MT_SAFE { + static thread_local VlRNG t_rng{0}; + static thread_local uint32_t t_seedEpoch = 0; + // For speed, we use a thread-local epoch number to know when to reseed + // A thread always belongs to a single context, so this works out ok + if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) { + // Set epoch before state, to avoid race case with new seeding + t_seedEpoch = VerilatedContextImp::randSeedEpoch(); + t_rng.m_state + = vl_rng_state_from_seed(Verilated::threadContextp()->impp()->randSeedDefault64()); + } + return t_rng; +} + +WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(obits); ++i) outwp[i] = vl_rand64(); + // Last word is unclean + return outwp; +} + +double VL_RANDOM_RNG_D(VlRNG& rngr) VL_MT_UNSAFE { return VL_CVT_D_Q(VL_RANDOM_RNG_Q(rngr)); } + +WDataOutP VL_RANDOM_RNG_W(VlRNG& rngr, int obits, WDataOutP outwp) VL_MT_UNSAFE { + for (int i = 0; i < VL_WORDS_I(obits); ++i) outwp[i] = rngr.rand64(); + // Last word is unclean + return outwp; +} + +IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE { + // $random - seed is a new seed to apply, then we return new seed + Verilated::threadContextp()->randSeed(static_cast(seedr)); + seedr = VL_RANDOM_I(); + return VL_RANDOM_I(); +} +IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE { + // $urandom - seed is a new seed to apply + Verilated::threadContextp()->randSeed(static_cast(seed)); + return VL_RANDOM_I(); +} + +IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE { + if (Verilated::threadContextp()->randReset() == 0) return 0; + IData data = ~0; + if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize + VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt}; + data = rng.rand64(); + } + data &= VL_MASK_I(obits); + return data; +} + +QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE { + if (Verilated::threadContextp()->randReset() == 0) return 0; + QData data = ~0ULL; + if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize + VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt}; + data = rng.rand64(); + } + data &= VL_MASK_Q(obits); + return data; +} + +WDataOutP VL_SCOPED_RAND_RESET_W(int obits, WDataOutP outwp, uint64_t scopeHash, + uint64_t salt) VL_MT_UNSAFE { + if (Verilated::threadContextp()->randReset() != 2) { return VL_RAND_RESET_W(obits, outwp); } + VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt}; + for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = rng.rand64(); + outwp[VL_WORDS_I(obits) - 1] = rng.rand64() & VL_MASK_E(obits); + return outwp; +} + +IData VL_SCOPED_RAND_RESET_ASSIGN_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE { + VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt}; + const IData data = rng.rand64() & VL_MASK_I(obits); + return data; +} + +QData VL_SCOPED_RAND_RESET_ASSIGN_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE { + VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt}; + const QData data = rng.rand64() & VL_MASK_Q(obits); + return data; +} + +WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint64_t scopeHash, + uint64_t salt) VL_MT_UNSAFE { + VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt}; + for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = rng.rand64(); + outwp[VL_WORDS_I(obits) - 1] = rng.rand64() & VL_MASK_E(obits); + return outwp; +} + +IData VL_RAND_RESET_I(int obits) VL_MT_SAFE { + if (Verilated::threadContextp()->randReset() == 0) return 0; + IData data = ~0; + if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize + data = VL_RANDOM_I(); + } + data &= VL_MASK_I(obits); + return data; +} + +QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE { + if (Verilated::threadContextp()->randReset() == 0) return 0; + QData data = ~0ULL; + if (Verilated::threadContextp()->randReset() != 1) { // if 2, randomize + data = VL_RANDOM_Q(); + } + data &= VL_MASK_Q(obits); + return data; +} + +WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_I(32); + outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_I(32) & VL_MASK_E(obits); + return outwp; +} +WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE { + // Not inlined to speed up compilation of slowpath code + return VL_ZERO_W(obits, outwp); +} + +//=========================================================================== +// Debug + +void _vl_debug_print_w(int lbits, const WDataInP iwp) VL_MT_SAFE { + VL_PRINTF_MT(" Data: w%d: ", lbits); + for (int i = VL_WORDS_I(lbits) - 1; i >= 0; --i) VL_PRINTF_MT("%08x ", iwp[i]); + VL_PRINTF_MT("\n"); +} + +//=========================================================================== +// Slow expressions + +WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WDataInP rwp, + bool is_modulus) VL_MT_SAFE { + // See Knuth Algorithm D. Computes u/v = q.r + // This isn't massively tuned, as wide division is rare + // for debug see V3Number version + // Requires clean input + const int words = VL_WORDS_I(lbits); + for (int i = 0; i < words; ++i) owp[i] = 0; + // Find MSB and check for zero. + const int umsbp1 = VL_MOSTSETBITP1_W(words, lwp); // dividend + const int vmsbp1 = VL_MOSTSETBITP1_W(words, rwp); // divisor + if (VL_UNLIKELY(vmsbp1 == 0) // rwp==0 so division by zero. Return 0. + || VL_UNLIKELY(umsbp1 == 0)) { // 0/x so short circuit and return 0 + return owp; + } + + const int uw = VL_WORDS_I(umsbp1); // aka "m" in the algorithm + const int vw = VL_WORDS_I(vmsbp1); // aka "n" in the algorithm + VL_DEBUG_IFDEF(assert(uw <= VL_MULS_MAX_WORDS);); + VL_DEBUG_IFDEF(assert(vw <= VL_MULS_MAX_WORDS);); + + if (vw == 1) { // Single divisor word breaks rest of algorithm + uint64_t k = 0; + for (int j = uw - 1; j >= 0; --j) { + const uint64_t unw64 = ((k << 32ULL) + static_cast(lwp[j])); + owp[j] = unw64 / static_cast(rwp[0]); + k = unw64 - static_cast(owp[j]) * static_cast(rwp[0]); + } + if (is_modulus) { + owp[0] = k; + for (int i = 1; i < words; ++i) owp[i] = 0; + } + return owp; + } + + // +1 word as we may shift during normalization + uint32_t un[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here + uint32_t vn[VL_MULS_MAX_WORDS + 1]; // v normalized + + // Zero for ease of debugging and to save having to zero for shifts + // Note +1 as loop will use extra word + for (int i = 0; i < words + 1; ++i) un[i] = vn[i] = 0; + + // Algorithm requires divisor MSB to be set + // Copy and shift to normalize divisor so MSB of vn[vw-1] is set + const int s = 31 - VL_BITBIT_I(vmsbp1 - 1); // shift amount (0...31) + // Copy and shift dividend by same amount; may set new upper word + if (s) { + for (int i = vw - 1; i > 0; --i) vn[i] = (rwp[i] << s) | (rwp[i - 1] >> (32 - s)); + vn[0] = rwp[0] << s; + un[uw] = lwp[uw - 1] >> (32 - s); + for (int i = uw - 1; i > 0; --i) un[i] = (lwp[i] << s) | (lwp[i - 1] >> (32 - s)); + un[0] = lwp[0] << s; + } else { + for (int i = vw - 1; i > 0; --i) vn[i] = rwp[i]; + vn[0] = rwp[0]; + un[uw] = 0; + for (int i = uw - 1; i > 0; --i) un[i] = lwp[i]; + un[0] = lwp[0]; + } + + // Main loop + for (int j = uw - vw; j >= 0; --j) { + // Estimate + const uint64_t unw64 + = (static_cast(un[j + vw]) << 32ULL | static_cast(un[j + vw - 1])); + uint64_t qhat = unw64 / static_cast(vn[vw - 1]); + uint64_t rhat = unw64 - qhat * static_cast(vn[vw - 1]); + + again: + if (qhat >= 0x100000000ULL || ((qhat * vn[vw - 2]) > ((rhat << 32ULL) + un[j + vw - 2]))) { + qhat = qhat - 1; + rhat = rhat + vn[vw - 1]; + if (rhat < 0x100000000ULL) goto again; + } + + int64_t t = 0; // Must be signed + uint64_t k = 0; + for (int i = 0; i < vw; ++i) { + const uint64_t p = qhat * vn[i]; // Multiply by estimate + t = un[i + j] - k - (p & 0xFFFFFFFFULL); // Subtract + un[i + j] = t; + k = (p >> 32ULL) - (t >> 32ULL); + } + t = un[j + vw] - k; + un[j + vw] = t; + owp[j] = qhat; // Save quotient digit + + if (t < 0) { + // Over subtracted; correct by adding back + owp[j]--; + k = 0; + for (int i = 0; i < vw; ++i) { + t = static_cast(un[i + j]) + static_cast(vn[i]) + k; + un[i + j] = t; + k = t >> 32ULL; + } + un[j + vw] = un[j + vw] + k; + } + } + + if (is_modulus) { // modulus + // Need to reverse normalization on copy to output + if (s) { + for (int i = 0; i < vw; ++i) owp[i] = (un[i] >> s) | (un[i + 1] << (32 - s)); + } else { + for (int i = 0; i < vw; ++i) owp[i] = un[i]; + } + for (int i = vw; i < words; ++i) owp[i] = 0; + return owp; + } else { // division + return owp; + } +} + +WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp, + const WDataInP rwp) VL_MT_SAFE { + // obits==lbits, rbits can be different + const int owords = VL_WORDS_I(obits); + VL_DEBUG_IFDEF(assert(owords <= VL_MULS_MAX_WORDS);); + owp[0] = 1; + for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + VlWide powstore; // Fixed size, as MSVC++ doesn't allow [words] here + VlWide lastpowstore; // Fixed size, as MSVC++ doesn't allow [words] here + VlWide lastoutstore; // Fixed size, as MSVC++ doesn't allow [words] here + VL_ASSIGN_W(obits, powstore, lwp); + for (int bit = 0; bit < rbits; ++bit) { + if (bit > 0) { // power = power*power + VL_ASSIGN_W(obits, lastpowstore, powstore); + VL_MUL_W(owords, powstore, lastpowstore, lastpowstore); + } + if (VL_BITISSET_W(rwp, bit)) { // out *= power + VL_ASSIGN_W(obits, lastoutstore, owp); + VL_MUL_W(owords, owp, lastoutstore, powstore); + } + } + return owp; +} +WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits, WDataOutP owp, const WDataInP lwp, + QData rhs) VL_MT_SAFE { + VlWide rhsw; + VL_SET_WQ(rhsw, rhs); + return VL_POW_WWW(obits, lbits, rbits, owp, lwp, rhsw); +} +QData VL_POW_QQW(int, int, int rbits, QData lhs, const WDataInP rwp) VL_MT_SAFE { + const int rwords = VL_WORDS_I(rbits); + EData rnz = rwp[0]; + for (int w = 1; w < rwords; ++w) rnz |= rwp[w]; + if (!rnz) return 1; // rwp == 0 + if (VL_UNLIKELY(lhs == 0)) return 0; + QData power = lhs; + QData result = 1ULL; + for (int bit = 0; bit < rbits; ++bit) { + if (bit > 0) power = power * power; + if (VL_BITISSET_W(rwp, bit)) result *= power; + } + return result; +} + +WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp, + const WDataInP rwp, bool lsign, bool rsign) VL_MT_SAFE { + // obits==lbits, rbits can be different + if (rsign && VL_SIGN_W(rbits, rwp)) { + const int words = VL_WORDS_I(obits); + VL_ZERO_W(obits, owp); + EData lor = 0; // 0=all zeros, ~0=all ones, else mix + for (int i = 1; i < (words - 1); ++i) lor |= lwp[i]; + lor |= ((lwp[words - 1] == VL_MASK_E(rbits)) ? ~VL_EUL(0) : 0); + if (lor == 0 && lwp[0] == 0) { // "X" so return 0 + return owp; + } else if (lor == 0 && lwp[0] == 1) { // 1 + owp[0] = 1; + return owp; + } else if (lsign && lor == ~VL_EUL(0) && lwp[0] == ~VL_EUL(0)) { // -1 + if (rwp[0] & 1) { // -1^odd=-1 + return VL_ALLONES_W(obits, owp); + } else { // -1^even=1 + owp[0] = 1; + return owp; + } + } + return owp; + } + return VL_POW_WWW(obits, rbits, rbits, owp, lwp, rwp); +} +WDataOutP VL_POWSS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, const WDataInP lwp, + QData rhs, bool lsign, bool rsign) VL_MT_SAFE { + VlWide rhsw; + VL_SET_WQ(rhsw, rhs); + return VL_POWSS_WWW(obits, lbits, rbits, owp, lwp, rhsw, lsign, rsign); +} +QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, const WDataInP rwp, bool lsign, + bool rsign) VL_MT_SAFE { + // Skip check for rhs == 0, as short-circuit doesn't save time + if (rsign && VL_SIGN_W(rbits, rwp)) { + if (lhs == 0) { + return 0; // "X" + } else if (lhs == 1) { + return 1; + } else if (lsign && lhs == VL_MASK_Q(obits)) { // -1 + if (rwp[0] & 1) { + return VL_MASK_Q(obits); // -1^odd=-1 + } else { + return 1; // -1^even=1 + } + } + return 0; + } + return VL_POW_QQW(obits, rbits, rbits, lhs, rwp); +} + +double VL_ITOR_D_W(int lbits, const WDataInP lwp) VL_PURE { + int ms_word = VL_WORDS_I(lbits) - 1; + for (; !lwp[ms_word] && ms_word > 0;) --ms_word; + if (ms_word == 0) return static_cast(lwp[0]); + if (ms_word == 1) return static_cast(VL_SET_QW(lwp)); + // We need 53 bits of mantissa, which might mean looking at 3 words + // namely ms_word, ms_word-1 and ms_word-2 + const EData ihi = lwp[ms_word]; + const EData imid = lwp[ms_word - 1]; + const EData ilo = lwp[ms_word - 2]; + const double hi = static_cast(ihi) * std::exp2(2 * VL_EDATASIZE); + const double mid = static_cast(imid) * std::exp2(VL_EDATASIZE); + const double lo = static_cast(ilo); + const double d = (hi + mid + lo) * std::exp2(VL_EDATASIZE * (ms_word - 2)); + return d; +} +double VL_ISTOR_D_W(int lbits, const WDataInP lwp) VL_MT_SAFE { + if (!VL_SIGN_W(lbits, lwp)) return VL_ITOR_D_W(lbits, lwp); + const int words = VL_WORDS_I(lbits); + VL_DEBUG_IFDEF(assert(words <= VL_MULS_MAX_WORDS);); + uint32_t pos[VL_MULS_MAX_WORDS + 1]; // Fixed size, as MSVC++ doesn't allow [words] here + VL_NEGATE_W(words, pos, lwp); + _vl_clean_inplace_w(lbits, pos); + return -VL_ITOR_D_W(lbits, pos); +} + +//=========================================================================== +// Formatting + +// Output a string representation of a wide number +std::string VL_DECIMAL_NW(int width, const WDataInP lwp) VL_MT_SAFE { + const int maxdecwidth = (width + 3) * 4 / 3; + // Or (maxdecwidth+7)/8], but can't have more than 4 BCD bits per word + VlWide bcd; + VL_ZERO_W(maxdecwidth, bcd); + VlWide tmp; + VlWide tmp2; + int from_bit = width - 1; + // Skip all leading zeros + for (; from_bit >= 0 && !(VL_BITRSHIFT_W(lwp, from_bit) & 1); --from_bit) {} + // Double-dabble algorithm + for (; from_bit >= 0; --from_bit) { + // Any digits >= 5 need an add 3 (via tmp) + for (int nibble_bit = 0; nibble_bit < maxdecwidth; nibble_bit += 4) { + if ((VL_BITRSHIFT_W(bcd, nibble_bit) & 0xf) >= 5) { + VL_ZERO_W(maxdecwidth, tmp2); + tmp2[VL_BITWORD_E(nibble_bit)] |= VL_EUL(0x3) << VL_BITBIT_E(nibble_bit); + VL_ASSIGN_W(maxdecwidth, tmp, bcd); + VL_ADD_W(VL_WORDS_I(maxdecwidth), bcd, tmp, tmp2); + } + } + // Shift; bcd = bcd << 1 + VL_ASSIGN_W(maxdecwidth, tmp, bcd); + VL_SHIFTL_WWI(maxdecwidth, maxdecwidth, 32, bcd, tmp, 1); + // bcd[0] = lwp[from_bit] + if (VL_BITISSET_W(lwp, from_bit)) bcd[0] |= 1; + } + std::string output; + int lsb = (maxdecwidth - 1) & ~3; + for (; lsb > 0; lsb -= 4) { // Skip leading zeros + if (VL_BITRSHIFT_W(bcd, lsb) & 0xf) break; + } + for (; lsb >= 0; lsb -= 4) { + output += ('0' + (VL_BITRSHIFT_W(bcd, lsb) & 0xf)); // 0..9 + } + return output; +} + +template +std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t width) VL_MT_SAFE { + const VerilatedContextImp* const ctxImpp = Verilated::threadContextp()->impp(); + const std::string suffix = ctxImpp->timeFormatSuffix(); + const int userUnits = ctxImpp->timeFormatUnits(); // 0..-15 + const int fracDigits = ctxImpp->timeFormatPrecision(); // 0..N + const int shift = -userUnits + fracDigits + timeunit; // 0..-15 + int digits = 0; + if (std::numeric_limits::is_integer) { + constexpr int b = 128; + constexpr int w = VL_WORDS_I(b); + VlWide tmp0; + VlWide tmp1; + VlWide tmp2; + VlWide tmp3; + + WDataInP shifted = VL_EXTEND_WQ(b, 0, tmp0, static_cast(ld)); + if (shift < 0) { + const WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(-shift)); + shifted = VL_DIV_WWW(b, tmp2, shifted, pow10); + } else { + const WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(shift)); + shifted = VL_MUL_W(w, tmp2, shifted, pow10); + } + + const WDataInP fracDigitsPow10 = VL_EXTEND_WQ(b, 0, tmp3, vl_time_pow10(fracDigits)); + const WDataInP integer = VL_DIV_WWW(b, tmp0, shifted, fracDigitsPow10); + const WDataInP frac = VL_MODDIV_WWW(b, tmp1, shifted, fracDigitsPow10); + const WDataInP max64Bit + = VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits::max()); // breaks shifted + if (VL_GT_W(w, integer, max64Bit)) { + WDataOutP v = VL_ASSIGN_W(b, tmp3, integer); // breaks fracDigitsPow10 + VlWide zero; + VlWide ten; + VL_ZERO_W(b, zero); + VL_EXTEND_WI(b, 0, ten, 10); + char buf[128]; // 128B is obviously long enough to represent 128bit integer in decimal + char* ptr = buf + sizeof(buf) - 1; + *ptr = '\0'; + while (VL_GT_W(w, v, zero)) { + --ptr; + const WDataInP mod = VL_MODDIV_WWW(b, tmp2, v, ten); // breaks max64Bit + *ptr = "0123456789"[VL_SET_QW(mod)]; + VlWide divided; + VL_DIV_WWW(b, divided, v, ten); + VL_ASSIGN_W(b, v, divided); + } + if (!fracDigits) { + digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%s%s", ptr, suffix.c_str()); + } else { + digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%s.%0*" PRIu64 "%s", ptr, + fracDigits, VL_SET_QW(frac), suffix.c_str()); + } + } else { + const uint64_t integer64 = VL_SET_QW(integer); + if (!fracDigits) { + digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRIu64 "%s", integer64, + suffix.c_str()); + } else { + digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRIu64 ".%0*" PRIu64 "%s", + integer64, fracDigits, VL_SET_QW(frac), suffix.c_str()); + } + } + } else { + const double shiftd = vl_time_multiplier(shift); + const double scaled = ld * shiftd; + const double fracDiv = vl_time_multiplier(fracDigits); + const double whole = scaled / fracDiv; + if (!fracDigits) { + digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%.0f%s", whole, suffix.c_str()); + } else { + digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%.*f%s", fracDigits, whole, + suffix.c_str()); + } + } + + const int needmore = static_cast(width) - digits; + std::string padding; + if (needmore > 0) padding.append(needmore, ' '); // Pad with spaces + return left ? (tmp + padding) : (padding + tmp); +} + +// Do a va_arg returning a quad, assuming input argument is anything less than wide +#define VL_VA_ARG_Q_(ap, bits) (((bits) <= VL_IDATASIZE) ? va_arg(ap, IData) : va_arg(ap, QData)) + +void _vl_vsformat(std::string& output, const std::string& format, va_list ap) VL_MT_SAFE { + // Format a Verilog $write style format into the output list + // The format must be pre-processed (and lower cased) by Verilator + // Arguments are in "width, arg-value (or WDataIn* if wide)" form + // + // Note uses a single buffer internally; presumes only one usage per printf + // Note also assumes variables < 64 are not wide, this assumption is + // sometimes not true in low-level routines written here in verilated.cpp + static thread_local char t_tmp[VL_VALUE_STRING_MAX_WIDTH]; + std::string::const_iterator pctit = format.end(); // Most recent %##.##g format + bool inPct = false; + bool widthSet = false; + bool left = false; + size_t width = 0; + for (std::string::const_iterator pos = format.cbegin(); pos != format.cend(); ++pos) { + if (!inPct && pos[0] == '%') { + pctit = pos; + inPct = true; + widthSet = false; + width = 0; + } else if (!inPct) { // Normal text + // Fast-forward to next escape and add to output + std::string::const_iterator ep = pos; + while (ep != format.end() && ep[0] != '%') ++ep; + if (ep != pos) { + output.append(pos, ep); + pos = ep - 1; + } + } else { // Format character + inPct = false; + const char fmt = pos[0]; + switch (fmt) { + case '0': // FALLTHRU + case '1': // FALLTHRU + case '2': // FALLTHRU + case '3': // FALLTHRU + case '4': // FALLTHRU + case '5': // FALLTHRU + case '6': // FALLTHRU + case '7': // FALLTHRU + case '8': // FALLTHRU + case '9': + inPct = true; // Get more digits + widthSet = true; + width = width * 10 + (fmt - '0'); + break; + case '-': + left = true; + inPct = true; // Get more digits + break; + case '.': + inPct = true; // Get more digits + break; + case '%': // + output += '%'; + break; + case 'N': { // "C" string with name of module, add . if needed + const char* const cstrp = va_arg(ap, const char*); + if (VL_LIKELY(*cstrp)) { + output += cstrp; + output += '.'; + } + break; + } + case 'S': { // "C" string + const char* const cstrp = va_arg(ap, const char*); + output += cstrp; + break; + } + case '@': { // Verilog/C++ string + va_arg(ap, int); // # bits is ignored + const std::string* const cstrp = va_arg(ap, const std::string*); + std::string padding; + if (width > cstrp->size()) padding.append(width - cstrp->size(), ' '); + output += left ? (*cstrp + padding) : (padding + *cstrp); + break; + } + case 'e': + case 'f': + case 'g': + case '^': { // Realtime + const int lbits = va_arg(ap, int); + const double d = va_arg(ap, double); + (void)lbits; // UNUSED - always 64 + if (fmt == '^') { // Realtime + if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth(); + const int timeunit = va_arg(ap, int); + output += _vl_vsformat_time(t_tmp, d, timeunit, left, width); + } else { + const std::string fmts{pctit, pos + 1}; + VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d); + output += t_tmp; + } + break; + } + case 'p': { // 'x' but parameter is string + const int lbits = va_arg(ap, int); + (void)lbits; + const std::string* const cstr = va_arg(ap, const std::string*); + std::ostringstream oss; + for (unsigned char c : *cstr) oss << std::hex << static_cast(c); + std::string hex_str = oss.str(); + if (width > 0 && widthSet) { + hex_str = hex_str.size() > width + ? hex_str.substr(0, width) + : std::string(width - hex_str.size(), '0') + hex_str; + output += hex_str; + } + break; + } + default: { + // Deal with all read-and-print somethings + const int lbits = va_arg(ap, int); + QData ld = 0; + VlWide qlwp; + WDataInP lwp = nullptr; + if (lbits <= VL_QUADSIZE) { + ld = VL_VA_ARG_Q_(ap, lbits); + VL_SET_WQ(qlwp, ld); + lwp = qlwp; + } else { + lwp = va_arg(ap, WDataInP); + ld = lwp[0]; + } + int lsb = lbits - 1; + if (widthSet && width == 0) { + while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb; + } + switch (fmt) { + case 'c': { + const IData charval = ld & 0xff; + output += static_cast(charval); + break; + } + case 's': { + std::string field; + for (; lsb >= 0; --lsb) { + lsb = (lsb / 8) * 8; // Next digit + const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff; + field += (charval == 0) ? ' ' : charval; + } + std::string padding; + if (width > field.size()) padding.append(width - field.size(), ' '); + output += left ? (field + padding) : (padding + field); + break; + } + case 'd': { // Signed decimal + int digits = 0; + std::string append; + if (lbits <= VL_QUADSIZE) { + digits + = VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRId64, + static_cast(VL_EXTENDS_QQ(lbits, lbits, ld))); + append = t_tmp; + } else { + if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) { + VlWide neg; + VL_NEGATE_W(VL_WORDS_I(lbits), neg, lwp); + append = "-"s + VL_DECIMAL_NW(lbits, neg); + } else { + append = VL_DECIMAL_NW(lbits, lwp); + } + digits = static_cast(append.length()); + } + const int needmore = static_cast(width) - digits; + if (needmore > 0) { + std::string padding; + if (left) { + padding.append(needmore, ' '); // Pre-pad spaces + output += append + padding; + } else { + if (pctit != format.end() && pctit[0] && pctit[1] == '0') { // %0 + padding.append(needmore, '0'); // Pre-pad zero + } else { + padding.append(needmore, ' '); // Pre-pad spaces + } + output += padding + append; + } + } else { + output += append; + } + break; + } + case '#': { // Unsigned decimal + int digits = 0; + std::string append; + if (lbits <= VL_QUADSIZE) { + digits = VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRIu64, ld); + append = t_tmp; + } else { + append = VL_DECIMAL_NW(lbits, lwp); + digits = static_cast(append.length()); + } + const int needmore = static_cast(width) - digits; + if (needmore > 0) { + std::string padding; + if (left) { + padding.append(needmore, ' '); // Pre-pad spaces + output += append + padding; + } else { + if (pctit != format.end() && pctit[0] && pctit[1] == '0') { // %0 + padding.append(needmore, '0'); // Pre-pad zero + } else { + padding.append(needmore, ' '); // Pre-pad spaces + } + output += padding + append; + } + } else { + output += append; + } + break; + } + case 't': { // Time + if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth(); + const int timeunit = va_arg(ap, int); + output += _vl_vsformat_time(t_tmp, ld, timeunit, left, width); + break; + } + case 'b': // FALLTHRU + case 'o': // FALLTHRU + case 'x': { + if (widthSet || left) { + lsb = VL_MOSTSETBITP1_W(VL_WORDS_I(lbits), lwp); + lsb = (lsb < 1) ? 0 : (lsb - 1); + } + + std::string append; + int digits; + switch (fmt) { + case 'b': { + digits = lsb + 1; + for (; lsb >= 0; --lsb) append += (VL_BITRSHIFT_W(lwp, lsb) & 1) + '0'; + break; + } + case 'o': { + digits = (lsb + 1 + 2) / 3; + for (; lsb >= 0; --lsb) { + lsb = (lsb / 3) * 3; // Next digit + // Octal numbers may span more than one wide word, + // so we need to grab each bit separately and check for overrun + // Octal is rare, so we'll do it a slow simple way + append += static_cast( + '0' + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 0)) ? 1 : 0) + + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 1)) ? 2 : 0) + + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 2)) ? 4 : 0)); + } + break; + } + default: { // 'x' + digits = (lsb + 1 + 3) / 4; + for (; lsb >= 0; --lsb) { + lsb = (lsb / 4) * 4; // Next digit + const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xf; + append += "0123456789abcdef"[charval]; + } + break; + } + } // switch + + const int needmore = static_cast(width) - digits; + if (needmore > 0) { + std::string padding; + if (left) { + padding.append(needmore, ' '); // Pre-pad spaces + output += append + padding; + } else { + padding.append(needmore, '0'); // Pre-pad zero + output += padding + append; + } + } else { + output += append; + } + break; + } // b / o / x + case 'u': + case 'z': { // Packed 4-state + const bool is_4_state = (fmt == 'z'); + output.reserve(output.size() + ((is_4_state ? 2 : 1) * VL_WORDS_I(lbits))); + int bytes_to_go = VL_BYTES_I(lbits); + int bit = 0; + while (bytes_to_go > 0) { + const int wr_bytes = std::min(4, bytes_to_go); + for (int byte = 0; byte < wr_bytes; byte++, bit += 8) + output += static_cast(VL_BITRSHIFT_W(lwp, bit) & 0xff); + output.append(4 - wr_bytes, static_cast(0)); + if (is_4_state) output.append(4, static_cast(0)); + bytes_to_go -= wr_bytes; + } + break; + } + case 'v': // Strength; assume always strong + for (lsb = lbits - 1; lsb >= 0; --lsb) { + if (VL_BITRSHIFT_W(lwp, lsb) & 1) { + output += "St1 "; + } else { + output += "St0 "; + } + } + break; + default: { // LCOV_EXCL_START + const std::string msg = "Unknown _vl_vsformat code: "s + pos[0]; + VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); + break; + } // LCOV_EXCL_STOP + } // switch + } + } // switch + } + } +} + +static bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE { + if (VL_LIKELY(fp)) { + return std::feof(fp) ? true : false; // true : false to prevent MSVC++ warning + } else { + return floc < 0; + } +} +static void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE { + if (VL_LIKELY(fp)) { + std::fgetc(fp); + } else { + floc -= 8; + } +} +static int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp, + const std::string& fstr) VL_MT_SAFE { + // Get a character without advancing + if (VL_LIKELY(fp)) { + const int data = std::fgetc(fp); + if (data == EOF) return EOF; + ungetc(data, fp); + return data; + } else { + if (floc < 0) return EOF; + floc = floc & ~7; // Align to closest character + if (fromp == nullptr) { + return fstr[fstr.length() - 1 - (floc >> 3)]; + } else { + return VL_BITRSHIFT_W(fromp, floc) & 0xff; + } + } +} +static void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp, + const std::string& fstr) VL_MT_SAFE { + while (true) { + const int c = _vl_vsss_peek(fp, floc, fromp, fstr); + if (c == EOF || !std::isspace(c)) return; + _vl_vsss_advance(fp, floc); + } +} +static void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr, + char* tmpp, const char* acceptp) VL_MT_SAFE { + // Read into tmp, consisting of characters from acceptp list + char* cp = tmpp; + while (true) { + int c = _vl_vsss_peek(fp, floc, fromp, fstr); + if (c == EOF || std::isspace(c)) break; + if (acceptp && nullptr == std::strchr(acceptp, c)) break; // String - allow anything + if (acceptp) c = std::tolower(c); // Non-strings we'll simplify + *cp++ = c; + _vl_vsss_advance(fp, floc); + } + *cp++ = '\0'; + // VL_DBG_MSGF(" _read got='"< 0) { + const int c = _vl_vsss_peek(fp, floc, fromp, fstr); + if (c == EOF) return nullptr; + if (!inhibit) *beginp++ = c; + _vl_vsss_advance(fp, floc); + } + return beginp; +} +static void _vl_vsss_setbit(WDataOutP iowp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE { + for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) VL_ASSIGNBIT_WI(lsb, iowp, ld & 1); +} +void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp, size_t posstart, + size_t posend) VL_MT_SAFE { + // Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits. + VL_ZERO_W(obits, owp); + int lsb = 0; + for (int i = 0, pos = static_cast(posend) - 1; + i < obits && pos >= static_cast(posstart); --pos) { + // clang-format off + switch (tolower (strp[pos])) { + case 'x': case 'z': case '?': // FALLTHRU + case '0': lsb += baseLog2; break; + case '1': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 1); lsb += baseLog2; break; + case '2': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 2); lsb += baseLog2; break; + case '3': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 3); lsb += baseLog2; break; + case '4': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 4); lsb += baseLog2; break; + case '5': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 5); lsb += baseLog2; break; + case '6': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 6); lsb += baseLog2; break; + case '7': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 7); lsb += baseLog2; break; + case '8': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 8); lsb += baseLog2; break; + case '9': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 9); lsb += baseLog2; break; + case 'a': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 10); lsb += baseLog2; break; + case 'b': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 11); lsb += baseLog2; break; + case 'c': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 12); lsb += baseLog2; break; + case 'd': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 13); lsb += baseLog2; break; + case 'e': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 14); lsb += baseLog2; break; + case 'f': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 15); lsb += baseLog2; break; + case '_': break; + } + // clang-format on + } +} + +IData _vl_vsscanf(FILE* fp, // If a fscanf + int fbits, const WDataInP fromp, // Else if a sscanf + const std::string& fstr, // if a sscanf to string + const std::string& format, va_list ap) VL_MT_SAFE { + // Read a Verilog $sscanf/$fscanf style format into the output list + // The format must be pre-processed (and lower cased) by Verilator + // Arguments are in "width, arg-value (or WDataIn* if wide)" form + static thread_local char t_tmp[VL_VALUE_STRING_MAX_WIDTH]; + int floc = fbits - 1; + IData got = 0; + bool inPct = false; + bool inIgnore = false; + std::string::const_iterator pos = format.cbegin(); + for (; pos != format.cend(); ++pos) { + // VL_DBG_MSGF("_vlscan fmt='%c' floc=%d file='%c'\n", pos[0], floc, + // _vl_vsss_peek(fp, floc, fromp, fstr)); + if (!inPct && pos[0] == '%') { + inPct = true; + inIgnore = false; + } else if (!inPct && std::isspace(pos[0])) { // Format spaces + while (std::isspace(pos[1])) ++pos; + _vl_vsss_skipspace(fp, floc, fromp, fstr); + } else if (!inPct) { // Expected Format + _vl_vsss_skipspace(fp, floc, fromp, fstr); + const int c = _vl_vsss_peek(fp, floc, fromp, fstr); + if (c != pos[0]) goto done; + _vl_vsss_advance(fp, floc); + } else { // Format character + // Skip loading spaces + inPct = false; + const char fmt = pos[0]; + switch (fmt) { + case '%': { + const int c = _vl_vsss_peek(fp, floc, fromp, fstr); + if (c != '%') goto done; + _vl_vsss_advance(fp, floc); + break; + } + case '0': // FALLTHRU + case '1': // FALLTHRU + case '2': // FALLTHRU + case '3': // FALLTHRU + case '4': // FALLTHRU + case '5': // FALLTHRU + case '6': // FALLTHRU + case '7': // FALLTHRU + case '8': // FALLTHRU + case '9': { + inPct = true; + break; + } + case '*': + inPct = true; + inIgnore = true; + break; + default: { + // Deal with all read-and-scan somethings + // Note LSBs are preserved if there's an overflow + int obits = inIgnore ? 0 : va_arg(ap, int); + VlWide qowp; + VL_SET_WQ(qowp, 0ULL); + WDataOutP owp = qowp; + if (obits == -1) { // string + owp = nullptr; + if (VL_UNCOVERABLE(fmt != 's')) { + VL_FATAL_MT( + __FILE__, __LINE__, "", + "Internal: format other than %s is passed to string"); // LCOV_EXCL_LINE + } + } else if (obits > VL_QUADSIZE) { + owp = va_arg(ap, WDataOutP); + } + + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + switch (fmt) { + case 'c': { + const int c = _vl_vsss_peek(fp, floc, fromp, fstr); + if (c == EOF) goto done; + _vl_vsss_advance(fp, floc); + owp[0] = c; + break; + } + case 's': { + _vl_vsss_skipspace(fp, floc, fromp, fstr); + _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, nullptr); + if (!t_tmp[0]) goto done; + if (owp) { + int lpos = (static_cast(std::strlen(t_tmp))) - 1; + int lsb = 0; + for (int i = 0; i < obits && lpos >= 0; --lpos) { + _vl_vsss_setbit(owp, obits, lsb, 8, t_tmp[lpos]); + lsb += 8; + } + } + break; + } + case 'd': { // Signed decimal + _vl_vsss_skipspace(fp, floc, fromp, fstr); + _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "0123456789+-xXzZ?_"); + if (!t_tmp[0]) goto done; + int64_t ld = 0; + std::sscanf(t_tmp, "%30" PRId64, &ld); + VL_SET_WQ(owp, ld); + break; + } + case 'f': + case 'e': + case 'g': { // Real number + _vl_vsss_skipspace(fp, floc, fromp, fstr); + _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "+-.0123456789eE"); + if (!t_tmp[0]) goto done; + union { + double r; + int64_t ld; + } u; + u.r = std::strtod(t_tmp, nullptr); + VL_SET_WQ(owp, u.ld); + break; + } + case 't': { // Time + _vl_vsss_skipspace(fp, floc, fromp, fstr); + _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "+-.0123456789eE"); + if (!t_tmp[0]) goto done; + union { + double r; + int64_t ld; + } u; + // Get pointer argument first, as proceeds the timeunit value + if (obits != 64) goto done; + QData* const realp = va_arg(ap, QData*); + const int timeunit = va_arg(ap, int); + const int userUnits + = Verilated::threadContextp()->impp()->timeFormatUnits(); // 0..-15 + const int shift = -userUnits + timeunit; // 0..-15 + u.r = std::strtod(t_tmp, nullptr) * vl_time_multiplier(-shift); + *realp = VL_CLEAN_QQ(obits, obits, u.ld); + obits = 0; // Already loaded the value, don't read arg + break; + } + case '#': { // Unsigned decimal + _vl_vsss_skipspace(fp, floc, fromp, fstr); + _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "0123456789+-xXzZ?_"); + if (!t_tmp[0]) goto done; + QData ld = 0; + std::sscanf(t_tmp, "%30" PRIu64, &ld); + VL_SET_WQ(owp, ld); + break; + } + case 'b': { + _vl_vsss_skipspace(fp, floc, fromp, fstr); + _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "01xXzZ?_"); + if (!t_tmp[0]) goto done; + _vl_vsss_based(owp, obits, 1, t_tmp, 0, std::strlen(t_tmp)); + break; + } + case 'o': { + _vl_vsss_skipspace(fp, floc, fromp, fstr); + _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "01234567xXzZ?_"); + if (!t_tmp[0]) goto done; + _vl_vsss_based(owp, obits, 3, t_tmp, 0, std::strlen(t_tmp)); + break; + } + case 'x': { + _vl_vsss_skipspace(fp, floc, fromp, fstr); + _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, + "0123456789abcdefABCDEFxXzZ?_"); + if (!t_tmp[0]) goto done; + _vl_vsss_based(owp, obits, 4, t_tmp, 0, std::strlen(t_tmp)); + break; + } + case 'u': { + // Read packed 2-value binary data + const int bytes = VL_BYTES_I(obits); + char* const out = reinterpret_cast(owp); + if (!_vl_vsss_read_bin(fp, floc, fromp, fstr, out, bytes)) goto done; + const int last = bytes % 4; + if (last != 0 + && !_vl_vsss_read_bin(fp, floc, fromp, fstr, out, 4 - last, true)) + goto done; + break; + } + case 'z': { + // Read packed 4-value binary data + char* out = reinterpret_cast(owp); + int bytes = VL_BYTES_I(obits); + while (bytes > 0) { + const int abytes = std::min(4, bytes); + // aval (4B) read {0, 1} state + out = _vl_vsss_read_bin(fp, floc, fromp, fstr, out, abytes); + if (!out) goto done; + // bval (4B) disregard {X, Z} state and align to new 8B boundary. + out = _vl_vsss_read_bin(fp, floc, fromp, fstr, out, 8 - abytes, true); + if (!out) goto done; + bytes -= abytes; + } + break; + } + default: { // LCOV_EXCL_START + const std::string msg = "Unknown _vl_vsscanf code: "s + pos[0]; + VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); + break; + } // LCOV_EXCL_STOP + + } // switch + + if (!inIgnore) ++got; + // Reload data if non-wide (if wide, we put it in the right place directly) + if (obits == 0) { // Due to inIgnore + } else if (obits == -1) { // string + std::string* const p = va_arg(ap, std::string*); + *p = t_tmp; + } else if (obits <= VL_BYTESIZE) { + CData* const p = va_arg(ap, CData*); + *p = VL_CLEAN_II(obits, obits, owp[0]); + } else if (obits <= VL_SHORTSIZE) { + SData* const p = va_arg(ap, SData*); + *p = VL_CLEAN_II(obits, obits, owp[0]); + } else if (obits <= VL_IDATASIZE) { + IData* const p = va_arg(ap, IData*); + *p = VL_CLEAN_II(obits, obits, owp[0]); + } else if (obits <= VL_QUADSIZE) { + QData* const p = va_arg(ap, QData*); + *p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp)); + } else { + _vl_clean_inplace_w(obits, owp); + } + } + } // switch + } + } + // Processed all arguments + return got; + +done: + // Scan stopped early, return parsed or EOF + if (_vl_vsss_eof(fp, floc)) return -1; + return got; +} + +//=========================================================================== +// File I/O + +FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE { + // Expected non-MCD case; returns null on MCD descriptors. + return Verilated::threadContextp()->impp()->fdToFp(lhs); +} + +void _vl_vint_to_string(int obits, char* destoutp, const WDataInP sourcep) VL_MT_SAFE { + // See also VL_DATA_TO_STRING_NW + int lsb = obits - 1; + bool start = true; + char* destp = destoutp; + for (; lsb >= 0; --lsb) { + lsb = (lsb / 8) * 8; // Next digit + const IData charval = VL_BITRSHIFT_W(sourcep, lsb) & 0xff; + if (!start || charval) { + *destp++ = (charval == 0) ? ' ' : charval; + start = false; // Drop leading 0s + } + } + *destp = '\0'; // Terminate + if (!start) { // Drop trailing spaces + while (std::isspace(*(destp - 1)) && destp > destoutp) *--destp = '\0'; + } +} + +void _vl_string_to_vint(int obits, void* destp, size_t srclen, const char* srcp) VL_MT_SAFE { + // Convert C string to Verilog format + const size_t bytes = VL_BYTES_I(obits); + char* op = reinterpret_cast(destp); + if (srclen > bytes) srclen = bytes; // Don't overflow destination + size_t i = 0; + for (i = 0; i < srclen; ++i) *op++ = srcp[srclen - 1 - i]; + for (; i < bytes; ++i) *op++ = 0; +} + +static IData getLine(std::string& str, IData fpi, size_t maxLen) VL_MT_SAFE { + str.clear(); + + // While threadsafe, each thread can only access different file handles + FILE* const fp = VL_CVT_I_FP(fpi); + if (VL_UNLIKELY(!fp)) return 0; + + // We don't use fgets, as we must read \0s. + while (str.size() < maxLen) { + const int c = getc(fp); // getc() is threadsafe + if (c == EOF) break; + str.push_back(c); + if (c == '\n') break; + } + return static_cast(str.size()); +} + +IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE { + std::string str; + const IData bytes = VL_BYTES_I(obits); + const IData got = getLine(str, fpi, bytes); + + if (VL_UNLIKELY(str.empty())) return 0; + + // V3Emit has static check that bytes < VL_VALUE_STRING_MAX_WORDS, but be safe + if (VL_UNCOVERABLE(bytes < str.size())) { + VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun"); // LCOV_EXCL_LINE + } + + _vl_string_to_vint(obits, destp, got, str.data()); + return got; +} + +IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE { + return getLine(dest, fpi, std::numeric_limits::max()); +} + +IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE { + // We ignore lhs/fpi - IEEE says "most recent error" so probably good enough + const IData ret = errno; + outputr = std::string{::std::strerror(ret)}; + return ret; +} +IData VL_FERROR_IW(IData fpi, int obits, WDataOutP outwp) VL_MT_SAFE { + std::string output; + const IData ret = VL_FERROR_IN(fpi, output /*ref*/); + _vl_string_to_vint(obits, outwp, output.length(), output.c_str()); + return ret; +} + +IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) { + return Verilated::threadContextp()->impp()->fdNew(filename.c_str(), mode.c_str()); +} +IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE { + return Verilated::threadContextp()->impp()->fdNewMcd(filename.c_str()); +} + +void VL_FFLUSH_I(IData fdi) VL_MT_SAFE { Verilated::threadContextp()->impp()->fdFlush(fdi); } +IData VL_FSEEK_I(IData fdi, IData offset, IData origin) VL_MT_SAFE { + return Verilated::threadContextp()->impp()->fdSeek(fdi, offset, origin); +} +IData VL_FTELL_I(IData fdi) VL_MT_SAFE { return Verilated::threadContextp()->impp()->fdTell(fdi); } +void VL_FCLOSE_I(IData fdi) VL_MT_SAFE { + // While threadsafe, each thread can only access different file handles + Verilated::threadContextp()->impp()->fdClose(fdi); +} + +void VL_SFORMAT_NX(int obits, CData& destr, const std::string& format, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + t_output = ""; + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, format, ap); + va_end(ap); + + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); +} + +void VL_SFORMAT_NX(int obits, SData& destr, const std::string& format, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + t_output = ""; + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, format, ap); + va_end(ap); + + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); +} + +void VL_SFORMAT_NX(int obits, IData& destr, const std::string& format, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + t_output = ""; + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, format, ap); + va_end(ap); + + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); +} + +void VL_SFORMAT_NX(int obits, QData& destr, const std::string& format, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + t_output = ""; + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, format, ap); + va_end(ap); + + _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str()); +} + +void VL_SFORMAT_NX(int obits, void* destp, const std::string& format, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + t_output = ""; + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, format, ap); + va_end(ap); + + _vl_string_to_vint(obits, destp, t_output.length(), t_output.c_str()); +} + +void VL_SFORMAT_NX(int obits_ignored, std::string& output, const std::string& format, int argc, + ...) VL_MT_SAFE { + (void)obits_ignored; // So VL_SFORMAT_NNX function signatures all match + std::string temp_output; + va_list ap; + va_start(ap, argc); + _vl_vsformat(temp_output, format, ap); + va_end(ap); + output = temp_output; +} + +std::string VL_SFORMATF_N_NX(const std::string& format, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + t_output = ""; + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, format, ap); + va_end(ap); + + return t_output; +} + +void VL_WRITEF_NX(const std::string& format, int argc, ...) VL_MT_SAFE { + static thread_local std::string t_output; // static only for speed + t_output = ""; + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, format, ap); + va_end(ap); + + VL_PRINTF_MT("%s", t_output.c_str()); +} + +void VL_FWRITEF_NX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE { + // While threadsafe, each thread can only access different file handles + static thread_local std::string t_output; // static only for speed + t_output = ""; + + va_list ap; + va_start(ap, argc); + _vl_vsformat(t_output, format, ap); + va_end(ap); + + Verilated::threadContextp()->impp()->fdWrite(fpi, t_output); +} + +IData VL_FSCANF_INX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE { + // While threadsafe, each thread can only access different file handles + FILE* const fp = VL_CVT_I_FP(fpi); + if (VL_UNLIKELY(!fp)) return ~0U; // -1 + + va_list ap; + va_start(ap, argc); + const IData got = _vl_vsscanf(fp, 0, nullptr, "", format, ap); + va_end(ap); + return got; +} + +IData VL_SSCANF_IINX(int lbits, IData ld, const std::string& format, int argc, ...) VL_MT_SAFE { + VlWide fnw; + VL_SET_WI(fnw, ld); + + va_list ap; + va_start(ap, argc); + const IData got = _vl_vsscanf(nullptr, lbits, fnw, "", format, ap); + va_end(ap); + return got; +} +IData VL_SSCANF_IQNX(int lbits, QData ld, const std::string& format, int argc, ...) VL_MT_SAFE { + VlWide fnw; + VL_SET_WQ(fnw, ld); + + va_list ap; + va_start(ap, argc); + const IData got = _vl_vsscanf(nullptr, lbits, fnw, "", format, ap); + va_end(ap); + return got; +} +IData VL_SSCANF_IWNX(int lbits, const WDataInP lwp, const std::string& format, int argc, + ...) VL_MT_SAFE { + va_list ap; + va_start(ap, argc); + const IData got = _vl_vsscanf(nullptr, lbits, lwp, "", format, ap); + va_end(ap); + return got; +} +IData VL_SSCANF_INNX(int, const std::string& ld, const std::string& format, int argc, + ...) VL_MT_SAFE { + va_list ap; + va_start(ap, argc); + const IData got + = _vl_vsscanf(nullptr, static_cast(ld.length() * 8), nullptr, ld, format, ap); + va_end(ap); + return got; +} + +// MurmurHash64A +uint64_t VL_MURMUR64_HASH(const char* key) VL_PURE { + const size_t len = strlen(key); + const uint64_t seed = 0; + const uint64_t m = 0xc6a4a7935bd1e995ULL; + const int r = 47; + + uint64_t h = seed ^ (len * m); + + const uint64_t* data = reinterpret_cast(key); + const uint64_t* end = data + (len / 8); + + while (data != end) { + uint64_t k = *data++; + + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + } + + const unsigned char* data2 = reinterpret_cast(data); + + switch (len & 7) { + case 7: h ^= uint64_t(data2[6]) << 48; /* fallthrough */ + case 6: h ^= uint64_t(data2[5]) << 40; /* fallthrough */ + case 5: h ^= uint64_t(data2[4]) << 32; /* fallthrough */ + case 4: h ^= uint64_t(data2[3]) << 24; /* fallthrough */ + case 3: h ^= uint64_t(data2[2]) << 16; /* fallthrough */ + case 2: h ^= uint64_t(data2[1]) << 8; /* fallthrough */ + case 1: h ^= uint64_t(data2[0]); h *= m; /* fallthrough */ + }; + + h ^= h >> r; + h *= m; + h ^= h >> r; + + return h; +} + +IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi, IData start, + IData count) VL_MT_SAFE { + // While threadsafe, each thread can only access different file handles + FILE* const fp = VL_CVT_I_FP(fpi); + if (VL_UNLIKELY(!fp)) return 0; + if (count > (array_size - (start - array_lsb))) count = array_size - (start - array_lsb); + // Prep for reading + IData read_count = 0; + IData read_elements = 0; + const int start_shift = (width - 1) & ~7; // bit+7:bit gets first character + int shift = start_shift; + // Read the data + // We process a character at a time, as then we don't need to deal + // with changing buffer sizes dynamically, etc. + while (true) { + const int c = std::fgetc(fp); + if (VL_UNLIKELY(c == EOF)) break; + // Shift value in + const IData entry = read_elements + start - array_lsb; + if (width <= 8) { + CData* const datap = &(reinterpret_cast(memp))[entry]; + if (shift == start_shift) *datap = 0; + *datap |= (c << shift) & VL_MASK_I(width); + } else if (width <= 16) { + SData* const datap = &(reinterpret_cast(memp))[entry]; + if (shift == start_shift) *datap = 0; + *datap |= (c << shift) & VL_MASK_I(width); + } else if (width <= VL_IDATASIZE) { + IData* const datap = &(reinterpret_cast(memp))[entry]; + if (shift == start_shift) *datap = 0; + *datap |= (c << shift) & VL_MASK_I(width); + } else if (width <= VL_QUADSIZE) { + QData* const datap = &(reinterpret_cast(memp))[entry]; + if (shift == start_shift) *datap = 0; + *datap |= ((static_cast(c) << static_cast(shift)) & VL_MASK_Q(width)); + } else { + WDataOutP datap = &(reinterpret_cast(memp))[entry * VL_WORDS_I(width)]; + if (shift == start_shift) VL_ZERO_W(width, datap); + datap[VL_BITWORD_E(shift)] |= (static_cast(c) << VL_BITBIT_E(shift)); + } + // Prep for next + ++read_count; + shift -= 8; + if (shift < 0) { + shift = start_shift; + ++read_elements; + if (VL_UNLIKELY(read_elements >= count)) break; + } + } + return read_count; +} + +#ifdef _VL_HAVE_STACKTRACE +static std::string _vl_stacktrace_demangle(const std::string& input) VL_MT_SAFE { + static VerilatedMutex s_demangleMutex; + const VerilatedLockGuard lock{s_demangleMutex}; + + std::string result; + result.reserve(input.size()); + + std::string word; + for (const char c : input) { + if (std::isalpha(c) || c == '_') { + word += c; + } else if (!word.empty() && std::isdigit(c)) { + word += c; + } else { + if (!word.empty()) { + // abi::__cxa_demangle mallocs demangled_name + int status = 0; + char* const demangled_name + = abi::__cxa_demangle(word.c_str(), NULL, NULL, &status); + if (status == 0) { + result += std::string{demangled_name}; + std::free(demangled_name); // Free the allocated memory + } else { + result += word; + } + word.clear(); + } + result += c; + } + } + // input requires final newline, so last word can't be symbol + result += word; + return result; +} +#endif + +std::string VL_STACKTRACE_N() VL_MT_SAFE { + static VerilatedMutex s_stackTraceMutex; + const VerilatedLockGuard lock{s_stackTraceMutex}; + +#ifdef _VL_HAVE_STACKTRACE + int nptrs = 0; + char** strings = nullptr; + + constexpr int BT_BUF_SIZE = 100; + void* buffer[BT_BUF_SIZE]; + nptrs = backtrace(buffer, BT_BUF_SIZE); + strings = backtrace_symbols(buffer, nptrs); + + // cppcheck-suppress knownConditionTrueFalse + if (!strings) return "Unable to backtrace, call failed\n"; + + std::string result = "Backtrace:\n"; + for (int j = 0; j < nptrs; ++j) + result += _vl_stacktrace_demangle(std::string{strings[j]} + "\n"s); + + free(strings); + return result; +#else + return "Unable to backtrace; not supported\n"; +#endif +} + +void VL_STACKTRACE() VL_MT_SAFE { + const std::string result = VL_STACKTRACE_N(); + VL_PRINTF("%s", result.c_str()); +} + +IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE { + VlWide lhsw; + VL_SET_WQ(lhsw, lhs); + return VL_SYSTEM_IW(VL_WQ_WORDS_E, lhsw); +} +IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE { + const std::string lhs = VL_CVT_PACK_STR_NW(lhswords, lhsp); + return VL_SYSTEM_IN(lhs); +} +IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE { + const int code = std::system(lhs.c_str()); // Yes, std::system() is threadsafe + return code >> 8; // Want exit status +} + +IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE { + const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(format.c_str()); + return match.empty() ? 0 : 1; +} + +IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE { + std::string prefix; + bool inPct = false; + bool done = false; + char fmt = ' '; + for (const char* posp = ld.c_str(); !done && *posp; ++posp) { + if (!inPct && posp[0] == '%') { + inPct = true; + } else if (!inPct) { // Normal text + prefix += *posp; + } else if (*posp == '0') { // %0 + } else { // Format character + switch (std::tolower(*posp)) { + case '%': + prefix += *posp; + inPct = false; + break; + default: + fmt = *posp; + done = true; + break; + } + } + } + + const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str()); + const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length(); + if (match.empty()) return 0; + + VL_ZERO_W(rbits, rwp); + switch (std::tolower(fmt)) { + case 'd': { + int64_t lld = 0; + std::sscanf(dp, "%30" PRId64, &lld); + VL_SET_WQ(rwp, lld); + break; + } + case 'b': _vl_vsss_based(rwp, rbits, 1, dp, 0, std::strlen(dp)); break; + case 'o': _vl_vsss_based(rwp, rbits, 3, dp, 0, std::strlen(dp)); break; + case 'h': // FALLTHRU + case 'x': _vl_vsss_based(rwp, rbits, 4, dp, 0, std::strlen(dp)); break; + case 's': { // string/no conversion + for (int i = 0, lsb = 0, posp = static_cast(std::strlen(dp)) - 1; + i < rbits && posp >= 0; --posp) { + _vl_vsss_setbit(rwp, rbits, lsb, 8, dp[posp]); + lsb += 8; + } + break; + } + case 'e': { + double temp = 0.F; + std::sscanf(dp, "%le", &temp); + VL_SET_WQ(rwp, VL_CVT_Q_D(temp)); + break; + } + case 'f': { + double temp = 0.F; + std::sscanf(dp, "%lf", &temp); + VL_SET_WQ(rwp, VL_CVT_Q_D(temp)); + break; + } + case 'g': { + double temp = 0.F; + std::sscanf(dp, "%lg", &temp); + VL_SET_WQ(rwp, VL_CVT_Q_D(temp)); + break; + } + default: // Other simulators return 0 in these cases and don't error out + return 0; + } + _vl_clean_inplace_w(rbits, rwp); + return 1; +} +IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE { + std::string prefix; + bool inPct = false; + bool done = false; + for (const char* posp = ld.c_str(); !done && *posp; ++posp) { + if (!inPct && posp[0] == '%') { + inPct = true; + } else if (!inPct) { // Normal text + prefix += *posp; + } else { // Format character + switch (std::tolower(*posp)) { + case '%': + prefix += *posp; + inPct = false; + break; + default: // + done = true; + break; + } + } + } + const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str()); + const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length(); + if (match.empty()) return 0; + rdr = std::string{dp}; + return 1; +} + +const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE { + const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefixp); + static thread_local char t_outstr[VL_VALUE_STRING_MAX_WIDTH]; + if (match.empty()) return nullptr; + char* dp = t_outstr; + for (const char* sp = match.c_str() + std::strlen(prefixp) + 1; // +1 to skip the "+" + *sp && (dp - t_outstr) < (VL_VALUE_STRING_MAX_WIDTH - 2);) + *dp++ = *sp++; + *dp++ = '\0'; + return t_outstr; +} + +//=========================================================================== +// Heavy string functions + +std::string VL_TO_STRING(CData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 8, lhs); } +std::string VL_TO_STRING(SData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 16, lhs); } +std::string VL_TO_STRING(IData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 32, lhs); } +std::string VL_TO_STRING(QData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 64, lhs); } +std::string VL_TO_STRING(double lhs) { return VL_SFORMATF_N_NX("%g", 0, 64, lhs); } +std::string VL_TO_STRING_W(int words, const WDataInP obj) { + return VL_SFORMATF_N_NX("'h%0x", 0, words * VL_EDATASIZE, obj); +} + +std::string VL_TOLOWER_NN(const std::string& ld) VL_PURE { + std::string result = ld; + for (auto& cr : result) cr = std::tolower(cr); + return result; +} +std::string VL_TOUPPER_NN(const std::string& ld) VL_PURE { + std::string result = ld; + for (auto& cr : result) cr = std::toupper(cr); + return result; +} + +std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE { + // See also _vl_vint_to_string + std::string result; + result.reserve((lwords * VL_EDATASIZE) / 8 + 1); + const int obits = lwords * VL_EDATASIZE; + int lsb = obits - 1; + for (; lsb >= 0; --lsb) { + lsb = (lsb / 8) * 8; // Next digit + const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff; + if (charval) result += static_cast(charval); + } + return result; +} + +std::string VL_CVT_PACK_STR_ND(const VlQueue& q) VL_PURE { + std::string output; + for (const std::string& s : q) output += s; + return output; +} + +std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE { + std::string lstring = lhs; + const int32_t rhs_s = rhs; // To signed value + // 6.16.2:str.putc(i, c) does not change the value when i < 0 || i >= str.len() || c == 0 + if (0 <= rhs_s && rhs < lhs.length() && ths != 0) lstring[rhs] = ths; + return lstring; +} + +CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE { + CData v = 0; + const int32_t rhs_s = rhs; // To signed value + // 6.16.3:str.getc(i) returns 0 if i < 0 || i >= str.len() + if (0 <= rhs_s && rhs < lhs.length()) v = lhs[rhs]; + return v; +} + +std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE { + const int32_t rhs_s = rhs; // To signed value + const int32_t ths_s = ths; // To signed value + // 6.16.8:str.substr(i, j) returns an empty string when i < 0 || j < i || j >= str.len() + if (rhs_s < 0 || ths_s < rhs_s || ths >= lhs.length()) return ""; + // Second parameter of std::string::substr(i, n) is length, not position as in SystemVerilog + return lhs.substr(rhs, ths - rhs + 1); +} + +IData VL_ATOI_N(const std::string& str, int base) VL_PURE { + std::string str_mod = str; + // IEEE 1800-2023 6.16.9 says '_' may exist. + str_mod.erase(std::remove(str_mod.begin(), str_mod.end(), '_'), str_mod.end()); + + errno = 0; + auto v = std::strtol(str_mod.c_str(), nullptr, base); + if (errno != 0) v = 0; + return static_cast(v); +} +IData VL_NTOI_I(int obits, const std::string& str) VL_PURE { return VL_NTOI_Q(obits, str); } +QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE { + QData out = 0; + const char* const datap = str.data(); + int pos = static_cast(str.length()) - 1; + int bit = 0; + while (bit < obits && pos >= 0) { + out |= static_cast(datap[pos]) << VL_BITBIT_Q(bit); + bit += 8; + --pos; + } + return out & VL_MASK_Q(obits); +} +void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str) VL_PURE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words; ++i) owp[i] = 0; + const char* const datap = str.data(); + int pos = static_cast(str.length()) - 1; + int bit = 0; + while (bit < obits && pos >= 0) { + owp[VL_BITWORD_I(bit)] |= static_cast(datap[pos]) << VL_BITBIT_I(bit); + bit += 8; + --pos; + } + owp[words - 1] &= VL_MASK_E(obits); +} + +//=========================================================================== +// Readmem/writemem + +static const char* memhFormat(int nBits) { + assert((nBits >= 1) && (nBits <= 32)); + + static thread_local char t_buf[32]; + switch ((nBits - 1) / 4) { + case 0: VL_SNPRINTF(t_buf, 32, "%%01x"); break; + case 1: VL_SNPRINTF(t_buf, 32, "%%02x"); break; + case 2: VL_SNPRINTF(t_buf, 32, "%%03x"); break; + case 3: VL_SNPRINTF(t_buf, 32, "%%04x"); break; + case 4: VL_SNPRINTF(t_buf, 32, "%%05x"); break; + case 5: VL_SNPRINTF(t_buf, 32, "%%06x"); break; + case 6: VL_SNPRINTF(t_buf, 32, "%%07x"); break; + case 7: VL_SNPRINTF(t_buf, 32, "%%08x"); break; + default: assert(false); break; // LCOV_EXCL_LINE + } + return t_buf; +} + +static const char* formatBinary(int nBits, uint32_t bits) { + assert((nBits >= 1) && (nBits <= 32)); + + static thread_local char t_buf[64]; + for (int i = 0; i < nBits; ++i) { + const bool isOne = bits & (1 << (nBits - 1 - i)); + t_buf[i] = (isOne ? '1' : '0'); + } + t_buf[nBits] = '\0'; + return t_buf; +} + +VlReadMem::VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end) + : m_hex{hex} + , m_bits{bits} + , m_filename(filename) // Need () or GCC 4.8 false warning + , m_end{end} + , m_addr{start} { + m_fp = std::fopen(filename.c_str(), "r"); + if (VL_UNLIKELY(!m_fp)) { + // We don't report the Verilog source filename as it slow to have to pass it down + VL_WARN_MT(filename.c_str(), 0, "", "$readmem file not found"); + return; + } +} +VlReadMem::~VlReadMem() { + if (m_fp) { + std::fclose(m_fp); + m_fp = nullptr; + } +} +bool VlReadMem::get(QData& addrr, std::string& valuer) { + if (VL_UNLIKELY(!m_fp)) return false; + valuer = ""; + // Prep for reading + bool inData = false; + bool ignoreToEol = false; + bool ignoreToComment = false; + bool readingAddress = false; + int lastCh = ' '; + // Read the data + // We process a character at a time, as then we don't need to deal + // with changing buffer sizes dynamically, etc. + while (true) { + int c = std::fgetc(m_fp); + if (VL_UNLIKELY(c == EOF)) break; + const bool chIs4StateBin + = c == '0' || c == '1' || c == 'x' || c == 'X' || c == 'z' || c == 'Z'; + const bool chIs2StateHex = std::isxdigit(c); + const bool chIs4StateHex = std::isxdigit(c) || chIs4StateBin; + // printf("%d: Got '%c' Addr%lx IN%d IgE%d IgC%d\n", + // m_linenum, c, m_addr, inData, ignoreToEol, ignoreToComment); + // See if previous data value has completed, and if so return + if (c == '_') continue; // Ignore _ e.g. inside a number + if (inData && !chIs4StateHex) { + // printf("Got data @%lx = %s\n", m_addr, valuer.c_str()); + ungetc(c, m_fp); + addrr = m_addr; + ++m_addr; + return true; + } + // Parse line + if (c == '\n') { + ++m_linenum; + ignoreToEol = false; + readingAddress = false; + } else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') { + readingAddress = false; + } + // Skip // comments and detect /* comments + else if (ignoreToComment && lastCh == '*' && c == '/') { + ignoreToComment = false; + readingAddress = false; + } else if (!ignoreToEol && !ignoreToComment) { + if (lastCh == '/' && c == '*') { + ignoreToComment = true; + } else if (lastCh == '/' && c == '/') { + ignoreToEol = true; + } else if (c == '/') { // Part of /* or // + } else if (c == '#') { + ignoreToEol = true; + } else if (c == '@') { + readingAddress = true; + m_anyAddr = true; + m_addr = 0; + } else if (readingAddress && chIs2StateHex) { + c = std::tolower(c); + const int addressValue = (c >= 'a') ? (c - 'a' + 10) : (c - '0'); + m_addr = (m_addr << 4) + addressValue; + } else if (readingAddress && chIs4StateHex) { + VL_FATAL_MT(m_filename.c_str(), m_linenum, "", + "$readmem address contains 4-state characters"); + } else if (chIs4StateHex) { + inData = true; + valuer += static_cast(c); + if (VL_UNLIKELY(!m_hex && !chIs4StateBin)) { + VL_FATAL_MT(m_filename.c_str(), m_linenum, "", + "$readmemb (binary) file contains hex characters"); + } + } else { + VL_FATAL_MT(m_filename.c_str(), m_linenum, "", "$readmem file syntax error"); + } + } + lastCh = c; + } + + if (VL_UNLIKELY(m_end != ~0ULL && m_addr <= m_end && !m_anyAddr)) { + VL_WARN_MT(m_filename.c_str(), m_linenum, "", + "$readmem file ended before specified final address (IEEE 1800-2023 21.4)"); + } + + addrr = m_addr; + return inData; // EOF +} +void VlReadMem::setData(void* valuep, const std::string& rhs) { + const QData shift = m_hex ? 4ULL : 1ULL; + bool innum = false; + // Shift value in + for (const auto& i : rhs) { + const char c = std::tolower(i); + const int value = (c == 'x' || c == 'z') ? VL_RAND_RESET_I(m_hex ? 4 : 1) + : (c >= 'a') ? (c - 'a' + 10) + : (c - '0'); + if (m_bits <= 8) { + CData* const datap = reinterpret_cast(valuep); + if (!innum) *datap = 0; + *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); + } else if (m_bits <= 16) { + SData* const datap = reinterpret_cast(valuep); + if (!innum) *datap = 0; + *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); + } else if (m_bits <= VL_IDATASIZE) { + IData* const datap = reinterpret_cast(valuep); + if (!innum) *datap = 0; + *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits); + } else if (m_bits <= VL_QUADSIZE) { + QData* const datap = reinterpret_cast(valuep); + if (!innum) *datap = 0; + *datap = ((*datap << static_cast(shift)) + static_cast(value)) + & VL_MASK_Q(m_bits); + } else { + WDataOutP datap = reinterpret_cast(valuep); + if (!innum) VL_ZERO_W(m_bits, datap); + _vl_shiftl_inplace_w(m_bits, datap, static_cast(shift)); + datap[0] |= value; + } + innum = true; + } +} + +VlWriteMem::VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end) + : m_hex{hex} + , m_bits{bits} { + if (VL_UNLIKELY(start > end)) { + VL_FATAL_MT(filename.c_str(), 0, "", "$writemem invalid address range"); + return; + } + + m_fp = std::fopen(filename.c_str(), "w"); + if (VL_UNLIKELY(!m_fp)) { + VL_FATAL_MT(filename.c_str(), 0, "", "$writemem file not found"); + return; + } +} +VlWriteMem::~VlWriteMem() { + if (m_fp) { + std::fclose(m_fp); + m_fp = nullptr; + } +} +void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) { + if (VL_UNLIKELY(!m_fp)) return; + if (addr != m_addr && addrstamp) { // Only assoc has time stamps + fprintf(m_fp, "@%" PRIx64 "\n", addr); + } + m_addr = addr + 1; + if (m_bits <= 8) { + const CData* const datap = reinterpret_cast(valuep); + if (m_hex) { + fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap); + fprintf(m_fp, "\n"); + } else { + fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap)); + } + } else if (m_bits <= 16) { + const SData* const datap = reinterpret_cast(valuep); + if (m_hex) { + fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap); + fprintf(m_fp, "\n"); + } else { + fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap)); + } + } else if (m_bits <= 32) { + const IData* const datap = reinterpret_cast(valuep); + if (m_hex) { + fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap); + fprintf(m_fp, "\n"); + } else { + fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap)); + } + } else if (m_bits <= 64) { + const QData* const datap = reinterpret_cast(valuep); + const uint64_t value = VL_MASK_Q(m_bits) & *datap; + const uint32_t lo = value & 0xffffffff; + const uint32_t hi = value >> 32; + if (m_hex) { + fprintf(m_fp, memhFormat(m_bits - 32), hi); + fprintf(m_fp, "%08x\n", lo); + } else { + fprintf(m_fp, "%s", formatBinary(m_bits - 32, hi)); + fprintf(m_fp, "%s\n", formatBinary(32, lo)); + } + } else { + const WDataInP datap = reinterpret_cast(valuep); + // output as a sequence of VL_EDATASIZE'd words + // from MSB to LSB. Mask off the MSB word which could + // contain junk above the top of valid data. + int word_idx = ((m_bits - 1) / VL_EDATASIZE); + bool first = true; + while (word_idx >= 0) { + EData data = datap[word_idx]; + if (first) { + data &= VL_MASK_E(m_bits); + const int top_word_nbits = VL_BITBIT_E(m_bits - 1) + 1; + if (m_hex) { + fprintf(m_fp, memhFormat(top_word_nbits), data); + } else { + fprintf(m_fp, "%s", formatBinary(top_word_nbits, data)); + } + } else { + if (m_hex) { + fprintf(m_fp, "%08x", data); + } else { + fprintf(m_fp, "%s", formatBinary(32, data)); + } + } + --word_idx; + first = false; + } + fprintf(m_fp, "\n"); + } +} + +void VL_READMEM_N(bool hex, // Hex format, else binary + int bits, // M_Bits of each array row + QData depth, // Number of rows + int array_lsb, // Index of first row. Valid row addresses + // // range from array_lsb up to (array_lsb + depth - 1) + const std::string& filename, // Input file name + void* memp, // Array state + QData start, // First array row address to read + QData end // Last row address to read + ) VL_MT_SAFE { + if (start < static_cast(array_lsb)) start = array_lsb; + + VlReadMem rmem{hex, bits, filename, start, end}; + if (VL_UNLIKELY(!rmem.isOpen())) return; + while (true) { + QData addr = 0; + std::string value; + if (rmem.get(addr /*ref*/, value /*ref*/)) { + // printf("readmem.get [%" PRIu64 "]=%s\n", addr, value.c_str()); + if (VL_UNLIKELY(addr < static_cast(array_lsb) + || addr >= static_cast(array_lsb + depth))) { + VL_FATAL_MT(filename.c_str(), rmem.linenum(), "", + "$readmem file address beyond bounds of array"); + } else { + const QData entry = addr - array_lsb; + if (bits <= 8) { + CData* const datap = &(reinterpret_cast(memp))[entry]; + rmem.setData(datap, value); + } else if (bits <= 16) { + SData* const datap = &(reinterpret_cast(memp))[entry]; + rmem.setData(datap, value); + } else if (bits <= VL_IDATASIZE) { + IData* const datap = &(reinterpret_cast(memp))[entry]; + rmem.setData(datap, value); + } else if (bits <= VL_QUADSIZE) { + QData* const datap = &(reinterpret_cast(memp))[entry]; + rmem.setData(datap, value); + } else { + WDataOutP datap + = &(reinterpret_cast(memp))[entry * VL_WORDS_I(bits)]; + rmem.setData(datap, value); + } + } + } else { + break; + } + } +} + +void VL_WRITEMEM_N(bool hex, // Hex format, else binary + int bits, // Width of each array row + QData depth, // Number of rows + int array_lsb, // Index of first row. Valid row addresses + // // range from array_lsb up to (array_lsb + depth - 1) + const std::string& filename, // Output file name + const void* memp, // Array state + QData start, // First array row address to write + QData end // Last address to write, or ~0 when not specified + ) VL_MT_SAFE { + const QData addr_max = array_lsb + depth - 1; + if (start < static_cast(array_lsb)) start = array_lsb; + if (end > addr_max) end = addr_max; + + VlWriteMem wmem{hex, bits, filename, start, end}; + if (VL_UNLIKELY(!wmem.isOpen())) return; + + for (QData addr = start; addr <= end; ++addr) { + const QData row_offset = addr - array_lsb; + if (bits <= 8) { + const CData* const datap = &(reinterpret_cast(memp))[row_offset]; + wmem.print(addr, false, datap); + } else if (bits <= 16) { + const SData* const datap = &(reinterpret_cast(memp))[row_offset]; + wmem.print(addr, false, datap); + } else if (bits <= 32) { + const IData* const datap = &(reinterpret_cast(memp))[row_offset]; + wmem.print(addr, false, datap); + } else if (bits <= 64) { + const QData* const datap = &(reinterpret_cast(memp))[row_offset]; + wmem.print(addr, false, datap); + } else { + const WDataInP memDatap = reinterpret_cast(memp); + const WDataInP datap = &memDatap[row_offset * VL_WORDS_I(bits)]; + wmem.print(addr, false, datap); + } + } +} + +//=========================================================================== +// Timescale conversion + +static const char* vl_time_str(int scale) VL_PURE { + static const char* const s_names[] + = {"100s", "10s", "1s", "100ms", "10ms", "1ms", "100us", "10us", "1us", + "100ns", "10ns", "1ns", "100ps", "10ps", "1ps", "100fs", "10fs", "1fs"}; + if (VL_UNLIKELY(scale > 2 || scale < -15)) scale = 0; + return s_names[2 - scale]; +} +double vl_time_multiplier(int scale) VL_PURE { + // Return timescale multiplier -18 to +18 + // For speed, this does not check for illegal values + if (scale < 0) { + static const double neg10[] = {1.0, + 0.1, + 0.01, + 0.001, + 0.0001, + 0.00001, + 0.000001, + 0.0000001, + 0.00000001, + 0.000000001, + 0.0000000001, + 0.00000000001, + 0.000000000001, + 0.0000000000001, + 0.00000000000001, + 0.000000000000001, + 0.0000000000000001, + 0.00000000000000001, + 0.000000000000000001}; + return neg10[-scale]; + } else { + static const double pow10[] = {1.0, + 10.0, + 100.0, + 1000.0, + 10000.0, + 100000.0, + 1000000.0, + 10000000.0, + 100000000.0, + 1000000000.0, + 10000000000.0, + 100000000000.0, + 1000000000000.0, + 10000000000000.0, + 100000000000000.0, + 1000000000000000.0, + 10000000000000000.0, + 100000000000000000.0, + 1000000000000000000.0}; + return pow10[scale]; + } +} +uint64_t vl_time_pow10(int n) { + static const uint64_t pow10[20] = { + 1ULL, + 10ULL, + 100ULL, + 1000ULL, + 10000ULL, + 100000ULL, + 1000000ULL, + 10000000ULL, + 100000000ULL, + 1000000000ULL, + 10000000000ULL, + 100000000000ULL, + 1000000000000ULL, + 10000000000000ULL, + 100000000000000ULL, + 1000000000000000ULL, + 10000000000000000ULL, + 100000000000000000ULL, + 1000000000000000000ULL, + }; + return pow10[n]; +} + +std::string vl_timescaled_double(double value, const char* format) VL_PURE { + const char* suffixp = "s"; + // clang-format off + if (value >= 1e0) { suffixp = "s"; value *= 1e0; } + else if (value >= 1e-3) { suffixp = "ms"; value *= 1e3; } + else if (value >= 1e-6) { suffixp = "us"; value *= 1e6; } + else if (value >= 1e-9) { suffixp = "ns"; value *= 1e9; } + else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; } + else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; } + else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; } + // clang-format on + char valuestr[100]; + VL_SNPRINTF(valuestr, 100, format, value, suffixp); + return std::string{valuestr}; // Gets converted to string, so no ref to stack +} + +void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp, + const VerilatedContext* contextp) VL_MT_SAFE { + VL_PRINTF_MT("Time scale of %s is %s / %s\n", namep, timeunitp, + contextp->timeprecisionString()); +} +void VL_TIMEFORMAT_IINI(bool hasUnits, int units, bool hasPrecision, int precision, bool hasSuffix, + const std::string& suffix, bool hasWidth, int width, + VerilatedContext* contextp) VL_MT_SAFE { + if (hasUnits) contextp->impp()->timeFormatUnits(units); + if (hasPrecision) contextp->impp()->timeFormatPrecision(precision); + if (hasSuffix) contextp->impp()->timeFormatSuffix(suffix); + if (hasWidth) contextp->impp()->timeFormatWidth(width); +} + +//====================================================================== +// VerilatedContext:: Methods + +VerilatedContext::VerilatedContext() + : m_impdatap{new VerilatedContextImpData} { + Verilated::lastContextp(this); + Verilated::threadContextp(this); + m_ns.m_coverageFilename = "coverage.dat"; + m_ns.m_profExecFilename = "profile_exec.dat"; + m_ns.m_profVltFilename = "profile.vlt"; + m_ns.m_solverProgram = VlOs::getenvStr("VERILATOR_SOLVER", VL_SOLVER_DEFAULT); + m_fdps.resize(31); + std::fill(m_fdps.begin(), m_fdps.end(), static_cast(nullptr)); + m_fdFreeMct.resize(30); + IData id = 1; + for (std::size_t i = 0; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id; +} + +// Must declare here not in interface, as otherwise forward declarations not known +VerilatedContext::~VerilatedContext() { + checkMagic(this); + m_magic = 0x1; // Arbitrary but 0x1 is what Verilator src uses for a deleted pointer +} + +void VerilatedContext::checkMagic(const VerilatedContext* contextp) { + if (VL_UNLIKELY(!contextp || contextp->m_magic != MAGIC)) { + VL_FATAL_MT("", 0, "", // LCOV_EXCL_LINE + "Attempt to create model using a bad/deleted VerilatedContext pointer"); + } +} + +VerilatedContext::Serialized::Serialized() { + constexpr int8_t picosecond = -12; + m_timeunit = picosecond; // Initial value until overridden by _Vconfigure + m_timeprecision = picosecond; // Initial value until overridden by _Vconfigure +} + +bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; } +void VerilatedContext::assertOn(bool flag) VL_MT_SAFE { + // Set all assert and directive types when true, clear otherwise. + m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag; +} +bool VerilatedContext::assertOnGet(VerilatedAssertType_t type, + VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE { + // Check if selected directive type bit in the assertOn is enabled for assertion type. + // Note: it is assumed that this is checked only for one type at the time. + + // Flag unspecified assertion types as disabled. + if (type == 0) return false; + + // Get index of 3-bit group guarding assertion type status. + // Since the assertOnGet is generated __always__ for a single assert type, we assume that only + // a single bit will be set. Thus, ceil log2 will work fine. + VL_DEBUG_IFDEF(assert((type & (type - 1)) == 0);); + const IData typeMaskPosition = VL_CLOG2_I(type); + + // Check if directive type bit is enabled in corresponding assertion type bits. + return m_s.m_assertOn & (directive << (typeMaskPosition * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH)); +} +void VerilatedContext::assertOnSet(VerilatedAssertType_t types, + VerilatedAssertDirectiveType_t directives) VL_MT_SAFE { + // For each assertion type, set directive bits. + + // Iterate through all positions of assertion type bits. If bit for this assertion type is set, + // set directive type bits mask at this group index. + for (int i = 0; i < std::numeric_limits::digits; ++i) { + if (VL_BITISSET_I(types, i)) + m_s.m_assertOn |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH); + } +} +void VerilatedContext::assertOnClear(VerilatedAssertType_t types, + VerilatedAssertDirectiveType_t directives) VL_MT_SAFE { + // Iterate through all positions of assertion type bits. If bit for this assertion type is set, + // clear directive type bits mask at this group index. + for (int i = 0; i < std::numeric_limits::digits; ++i) { + if (VL_BITISSET_I(types, i)) + m_s.m_assertOn &= ~(directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH)); + } +} +void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_s.m_calcUnusedSigs = flag; +} +void VerilatedContext::coverageFilename(const std::string& flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_ns.m_coverageFilename = flag; +} +std::string VerilatedContext::coverageFilename() const VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + return m_ns.m_coverageFilename; +} +void VerilatedContext::dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { + const VerilatedLockGuard lock{m_timeDumpMutex}; + m_dumpfile = flag; +} +std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { + const VerilatedLockGuard lock{m_timeDumpMutex}; + return m_dumpfile; +} +std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { + std::string out = dumpfile(); + if (VL_UNLIKELY(out.empty())) { + VL_PRINTF_MT("%%Warning: $dumpvar ignored as not preceded by $dumpfile\n"); + return ""; + } + return out; +} +void VerilatedContext::errorCount(int val) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_s.m_errorCount = val; +} +void VerilatedContext::errorCountInc() VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + ++m_s.m_errorCount; +} +void VerilatedContext::errorLimit(int val) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_s.m_errorLimit = val; +} +void VerilatedContext::fatalOnError(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_s.m_fatalOnError = flag; +} +void VerilatedContext::fatalOnVpiError(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_s.m_fatalOnVpiError = flag; +} +void VerilatedContext::gotError(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_s.m_gotError = flag; +} +void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_s.m_gotFinish = flag; +} +void VerilatedContext::profExecStart(uint64_t flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_ns.m_profExecStart = flag; +} +void VerilatedContext::profExecWindow(uint64_t flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_ns.m_profExecWindow = flag; +} +void VerilatedContext::profExecFilename(const std::string& flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_ns.m_profExecFilename = flag; +} +std::string VerilatedContext::profExecFilename() const VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + return m_ns.m_profExecFilename; +} +void VerilatedContext::profVltFilename(const std::string& flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_ns.m_profVltFilename = flag; +} +std::string VerilatedContext::profVltFilename() const VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + return m_ns.m_profVltFilename; +} +void VerilatedContext::solverProgram(const std::string& flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_ns.m_solverProgram = flag; +} +std::string VerilatedContext::solverProgram() const VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + return m_ns.m_solverProgram; +} +void VerilatedContext::quiet(bool flag) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_s.m_quiet = flag; +} +void VerilatedContext::randReset(int val) VL_MT_SAFE { + const VerilatedLockGuard lock{m_mutex}; + m_s.m_randReset = val; +} +void VerilatedContext::timeunit(int value) VL_MT_SAFE { + if (value < 0) value = -value; // Stored as 0..15 + const VerilatedLockGuard lock{m_mutex}; + m_s.m_timeunit = value; +} +const char* VerilatedContext::timeunitString() const VL_MT_SAFE { return vl_time_str(timeunit()); } +const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE { + return vl_time_str(timeprecision()); +} + +void VerilatedContext::threads(unsigned n) { + if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "Simulation threads must be >= 1"); + + if (m_threadPool) { + VL_FATAL_MT( + __FILE__, __LINE__, "", + "%Error: Cannot set simulation threads after the thread pool has been created."); + } + + m_useNumaAssign = true; + if (m_threads == n) return; // To avoid unnecessary warnings + m_threads = n; + const unsigned threadsAvailableToProcess = VlOs::getProcessDefaultParallelism(); + if (m_threads > threadsAvailableToProcess) { + VL_PRINTF_MT("%%Warning: Process has %u hardware threads available, but simulation thread " + "count set to %u. This will likely cause significant slowdown.\n", + threadsAvailableToProcess, m_threads); + } +} + +void VerilatedContext::useNumaAssign(bool flag) { m_useNumaAssign = flag; } + +void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) { + // Not locking m_argMutex here, it is done in impp()->commandArgsAddGuts + // m_argMutex here is the same as in impp()->commandArgsAddGuts; + // due to clang limitations, it doesn't properly check it + impp()->commandArgsGuts(argc, argv); +} +void VerilatedContext::commandArgsAdd(int argc, const char** argv) + VL_MT_SAFE_EXCLUDES(m_argMutex) { + // Not locking m_argMutex here, it is done in impp()->commandArgsAddGuts + // m_argMutex here is the same as in impp()->commandArgsAddGuts; + // due to clang limitations, it doesn't properly check it + impp()->commandArgsAddGutsLock(argc, argv); +} +const char* VerilatedContext::commandArgsPlusMatch(const char* prefixp) + VL_MT_SAFE_EXCLUDES(m_argMutex) { + const std::string& match = impp()->argPlusMatch(prefixp); + static thread_local char t_outstr[VL_VALUE_STRING_MAX_WIDTH]; + if (match.empty()) return ""; + char* dp = t_outstr; + for (const char* sp = match.c_str(); *sp && (dp - t_outstr) < (VL_VALUE_STRING_MAX_WIDTH - 2);) + *dp++ = *sp++; + *dp++ = '\0'; + return t_outstr; +} +void VerilatedContext::internalsDump() const VL_MT_SAFE { + VL_PRINTF_MT("internalsDump:\n"); + VerilatedImp::versionDump(); + impp()->commandArgDump(); + impp()->scopesDump(); + VerilatedImp::exportsDump(); + VerilatedImp::userDump(); +} + +void VerilatedContext::addModel(const VerilatedModel* modelp) { + if (!quiet()) { + // CPU time isn't read as starting point until model creation, so that quiet() is set + // Thus if quiet(), avoids slow OS read affecting some usages that make many models + const VerilatedLockGuard lock{m_mutex}; + m_ns.m_cpuTimeStart.start(); + m_ns.m_wallTimeStart.start(); + } + + // We look for time passing, as opposed to post-eval(), as embedded + // models might get added inside initial blocks. + if (VL_UNLIKELY(time())) { + const std::string msg + = "Adding model '"s + modelp->hierName() + + "' when time is non-zero. ... Suggest check time(), or for restarting" + " model use a new VerilatedContext"; + VL_FATAL_MT("", 0, "", msg.c_str()); + } + + threadPoolp(); // Ensure thread pool is created, so m_threads cannot change any more + m_threadsInModels += modelp->threads(); + if (VL_UNLIKELY(modelp->threads() > m_threads)) { + std::ostringstream msg; + msg << "VerilatedContext has " << m_threads << " threads but model '" + << modelp->modelName() << "' (instantiated as '" << modelp->hierName() + << "') was Verilated with --threads " << modelp->threads() << ".\n"; + const std::string str = msg.str(); + VL_FATAL_MT(__FILE__, __LINE__, modelp->hierName(), str.c_str()); + } +} + +VerilatedVirtualBase* VerilatedContext::threadPoolp() { + if (m_threads == 1) return nullptr; + if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1}); + return m_threadPool.get(); +} + +void VerilatedContext::prepareClone() { delete m_threadPool.release(); } + +VerilatedVirtualBase* VerilatedContext::threadPoolpOnClone() { + if (VL_UNLIKELY(m_threadPool)) (void)m_threadPool.release(); + m_threadPool = std::unique_ptr(new VlThreadPool{this, m_threads - 1}); + return m_threadPool.get(); +} + +VerilatedVirtualBase* +VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) { + if (!m_executionProfiler) m_executionProfiler.reset(construct(*this)); + return m_executionProfiler.get(); +} + +//====================================================================== +// VerilatedContextImp:: Methods - command line + +void VerilatedContextImp::commandArgsGuts(int argc, const char** argv) + VL_MT_SAFE_EXCLUDES(m_argMutex) { + const VerilatedLockGuard lock{m_argMutex}; + m_args.m_argVec.clear(); // Empty first, then add + commandArgsAddGuts(argc, argv); +} + +void VerilatedContextImp::commandArgsAddGutsLock(int argc, const char** argv) + VL_MT_SAFE_EXCLUDES(m_argMutex) { + const VerilatedLockGuard lock{m_argMutex}; + commandArgsAddGuts(argc, argv); +} + +void VerilatedContextImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex) { + if (!m_args.m_argVecLoaded) m_args.m_argVec.clear(); + for (int i = 0; i < argc; ++i) { + m_args.m_argVec.emplace_back(argv[i]); + commandArgVl(argv[i]); + } + m_args.m_argVecLoaded = true; // Can't just test later for empty vector, no arguments is ok +} +void VerilatedContextImp::commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex) { + const VerilatedLockGuard lock{m_argMutex}; + VL_PRINTF_MT(" Argv:"); + for (const auto& i : m_args.m_argVec) VL_PRINTF_MT(" %s", i.c_str()); + VL_PRINTF_MT("\n"); +} +std::string VerilatedContextImp::argPlusMatch(const char* prefixp) + VL_MT_SAFE_EXCLUDES(m_argMutex) { + const VerilatedLockGuard lock{m_argMutex}; + // Note prefixp does not include the leading "+" + const size_t len = std::strlen(prefixp); + if (VL_UNLIKELY(!m_args.m_argVecLoaded)) { + m_args.m_argVecLoaded = true; // Complain only once + VL_FATAL_MT("unknown", 0, "", + "%Error: Verilog called $test$plusargs or $value$plusargs without" + " testbench C first calling Verilated::commandArgs(argc,argv)."); + } + for (const auto& i : m_args.m_argVec) { + if (i[0] == '+') { + if (0 == std::strncmp(prefixp, i.c_str() + 1, len)) return i; + } + } + return ""; +} +// Return string representing current argv +// Only used by VPI so uses static storage, only supports most recent called context +std::pair VerilatedContextImp::argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex) { + const VerilatedLockGuard lock{m_argMutex}; + static bool s_loaded = false; + static int s_argc = 0; + static char** s_argvp = nullptr; + if (VL_UNLIKELY(!s_loaded)) { + s_loaded = true; + s_argc = static_cast(m_args.m_argVec.size()); + s_argvp = new char*[s_argc + 1]; + int in = 0; + for (const auto& i : m_args.m_argVec) { + s_argvp[in] = new char[i.length() + 1]; + std::memcpy(s_argvp[in], i.c_str(), i.length() + 1); + ++in; + } + s_argvp[s_argc] = nullptr; + } + return std::make_pair(s_argc, s_argvp); +} + +void VerilatedContextImp::commandArgVl(const std::string& arg) { + if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) { + std::string str; + uint64_t u64; + if (commandArgVlString(arg, "+verilator+coverage+file+", str)) { + coverageFilename(str); + } else if (arg == "+verilator+debug") { + Verilated::debug(4); + } else if (commandArgVlUint64(arg, "+verilator+debugi+", u64, 0, + std::numeric_limits::max())) { + Verilated::debug(static_cast(u64)); + } else if (commandArgVlUint64(arg, "+verilator+error+limit+", u64, 0, + std::numeric_limits::max())) { + errorLimit(static_cast(u64)); + } else if (arg == "+verilator+help") { + VerilatedImp::versionDump(); + VL_PRINTF_MT("For help, please see 'verilator --help'\n"); + VL_FATAL_MT("COMMAND_LINE", 0, "", + "Exiting due to command line argument (not an error)"); + } else if (arg == "+verilator+noassert") { + assertOn(false); + } else if (commandArgVlUint64(arg, "+verilator+prof+exec+start+", u64)) { + profExecStart(u64); + } else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)) { + profExecWindow(u64); + } else if (commandArgVlString(arg, "+verilator+prof+exec+file+", str)) { + profExecFilename(str); + } else if (commandArgVlString(arg, "+verilator+prof+vlt+file+", str)) { + profVltFilename(str); + } else if (arg == "+verilator+quiet") { + quiet(true); + } else if (commandArgVlUint64(arg, "+verilator+rand+reset+", u64, 0, 2)) { + randReset(static_cast(u64)); + } else if (commandArgVlUint64(arg, "+verilator+wno+unsatconstr+", u64, 0, 1)) { + warnUnsatConstr(u64 == 0); // wno means disable, so invert + } else if (commandArgVlUint64(arg, "+verilator+seed+", u64, 1, + std::numeric_limits::max())) { + randSeed(static_cast(u64)); + } else if (arg == "+verilator+V") { + VerilatedImp::versionDump(); // Someday more info too + VL_FATAL_MT("COMMAND_LINE", 0, "", + "Exiting due to command line argument (not an error)"); + } else if (arg == "+verilator+version") { + VerilatedImp::versionDump(); + VL_FATAL_MT("COMMAND_LINE", 0, "", + "Exiting due to command line argument (not an error)"); + } else { + const std::string msg = "Unknown runtime argument: " + arg; + VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str()); + } + } +} + +bool VerilatedContextImp::commandArgVlString(const std::string& arg, const std::string& prefix, + std::string& valuer) { + const size_t len = prefix.length(); + if (0 == std::strncmp(prefix.c_str(), arg.c_str(), len)) { + valuer = arg.substr(len); + return true; + } else { + return false; + } +} + +bool VerilatedContextImp::commandArgVlUint64(const std::string& arg, const std::string& prefix, + uint64_t& valuer, uint64_t min, uint64_t max) { + std::string str; + if (commandArgVlString(arg, prefix, str)) { + const auto fail = [&](const std::string& extra = "") { + std::stringstream ss; + ss << "Argument '" << prefix << "' must be an unsigned integer"; + if (min != std::numeric_limits::min()) ss << ", greater than " << min - 1; + if (max != std::numeric_limits::max()) ss << ", less than " << max + 1; + if (!extra.empty()) ss << ". " << extra; + const std::string& msg = ss.str(); + VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str()); + }; + + if (std::any_of(str.cbegin(), str.cend(), [](int c) { return !std::isdigit(c); })) fail(); + char* end; + valuer = std::strtoull(str.c_str(), &end, 10); + if (errno == ERANGE) fail("Value out of range of uint64_t"); + if (valuer < min || valuer > max) fail(); + return true; + } + return false; +} + +//====================================================================== +// VerilatedContext:: + VerilatedContextImp:: Methods - random + +void VerilatedContext::randSeed(int val) VL_MT_SAFE { + // As we have per-thread state, the epoch must be static, + // and so the rand seed's mutex must also be static + const VerilatedLockGuard lock{VerilatedContextImp::s().s_randMutex}; + m_s.m_randSeed = val; + const uint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1; + // Observers must see new epoch AFTER seed updated + std::atomic_signal_fence(std::memory_order_release); + VerilatedContextImp::s().s_randSeedEpoch = newEpoch; +} +uint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE { + if (randSeed() != 0) { + return ((static_cast(randSeed()) << 32) ^ (static_cast(randSeed()))); + } else { + return ((static_cast(vl_sys_rand32()) << 32) + ^ (static_cast(vl_sys_rand32()))); + } +} + +//====================================================================== +// VerilatedContext:: Statistics + +double VerilatedContext::statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + return m_ns.m_cpuTimeStart.deltaTime(); +} +double VerilatedContext::statWallTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + return m_ns.m_wallTimeStart.deltaTime(); +} +void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE { + if (quiet()) return; + VL_PRINTF("- S i m u l a t i o n R e p o r t: %s %s\n", Verilated::productName(), + Verilated::productVersion()); + const std::string endwhy = gotError() ? "$stop" : gotFinish() ? "$finish" : "end"; + const double simtimeInUnits = VL_TIME_Q() * vl_time_multiplier(timeunit()) + * vl_time_multiplier(timeprecision() - timeunit()); + const std::string simtime = vl_timescaled_double(simtimeInUnits); + const double walltime = statWallTimeSinceStart(); + const double cputime = statCpuTimeSinceStart(); + const std::string simtimePerf + = vl_timescaled_double((cputime != 0.0) ? (simtimeInUnits / cputime) : 0, "%0.3f %s"); + VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(), + simtime.c_str(), walltime, simtimePerf.c_str()); + uint64_t memPeak, memCurrent; + VlOs::memUsageBytes(memPeak /*ref*/, memCurrent /*ref*/); + const double modelMB = memPeak / 1024.0 / 1024.0; + VL_PRINTF("- Verilator: cpu %0.3f s on %u threads; allocated %0.0f MB\n", cputime, + threadsInModels(), modelMB); +} + +//====================================================================== +// VerilatedContext:: Methods - scopes + +void VerilatedContext::scopesDump() const VL_MT_SAFE { + const VerilatedLockGuard lock{m_impdatap->m_nameMutex}; + VL_PRINTF_MT(" scopesDump:\n"); + for (const auto& i : m_impdatap->m_nameMap) { + const VerilatedScope* const scopep = i.second; + scopep->scopeDump(); + } + VL_PRINTF_MT("\n"); +} + +void VerilatedContextImp::scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE { + // Slow ok - called once/scope at construction + const VerilatedLockGuard lock{m_impdatap->m_nameMutex}; + const auto it = m_impdatap->m_nameMap.find(scopep->name()); + if (it == m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.emplace(scopep->name(), scopep); +} +void VerilatedContextImp::scopeErase(const VerilatedScope* scopep) VL_MT_SAFE { + // Slow ok - called once/scope at destruction + const VerilatedLockGuard lock{m_impdatap->m_nameMutex}; + VerilatedImp::userEraseScope(scopep); + const auto it = m_impdatap->m_nameMap.find(scopep->name()); + if (it != m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.erase(it); +} +const VerilatedScope* VerilatedContext::scopeFind(const char* namep) const VL_MT_SAFE { + // Thread save only assuming this is called only after model construction completed + const VerilatedLockGuard lock{m_impdatap->m_nameMutex}; + // If too slow, can assume this is only VL_MT_SAFE_POSINIT + const auto& it = m_impdatap->m_nameMap.find(namep); + if (VL_UNLIKELY(it == m_impdatap->m_nameMap.end())) return nullptr; + return it->second; +} +const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE { + return &(impp()->m_impdatap->m_nameMap); +} + +//====================================================================== +// VerilatedContext:: Methods - trace + +void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options) { + VL_DEBUG_IF(VL_DBG_MSGF("+ VerilatedContext::trace\n");); + if (tfp->isOpen()) { + VL_FATAL_MT("", 0, "", + "Testbench C call to 'VerilatedContext::trace()' must not be called" + " after 'VerilatedTrace*::open()'\n"); + } + { + // Legacy usage may call {modela}->trace(...) then {modelb}->trace(...) + // So check for and suppress second and later calls + if (tfp->modelConnected()) return; + tfp->modelConnected(true); + } + // We rely on m_ns.m_traceBaseModelCbs being stable when trace() is called + // nope: const VerilatedLockGuard lock{m_mutex}; + if (m_ns.m_traceBaseModelCbs.empty()) + VL_FATAL_MT("", 0, "", + "Testbench C call to 'VerilatedContext::trace()' requires model(s) Verilated" + " with --trace-fst or --trace-vcd option"); + for (const auto& cbr : m_ns.m_traceBaseModelCbs) cbr(tfp, levels, options); +} +void VerilatedContext::traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE { + // Model creation registering a callback for when Verilated::trace() called + const VerilatedLockGuard lock{m_mutex}; + m_ns.m_traceBaseModelCbs.push_back(cb); +} + +//====================================================================== +// VerilatedSyms:: Methods + +VerilatedSyms::VerilatedSyms(VerilatedContext* contextp) + : _vm_contextp__(contextp ? contextp : Verilated::threadContextp()) { + VerilatedContext::checkMagic(_vm_contextp__); + Verilated::threadContextp(_vm_contextp__); + __Vm_evalMsgQp = new VerilatedEvalMsgQueue; +} + +VerilatedSyms::~VerilatedSyms() { + VerilatedContext::checkMagic(_vm_contextp__); + delete __Vm_evalMsgQp; +} + +//=========================================================================== +// Verilated:: Methods + +void Verilated::debug(int level) VL_MT_SAFE { + s_debug = level; + if (level) { +#ifdef VL_DEBUG + VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on." + " Message prefix indicates {,}.\n");); +#else + VL_PRINTF_MT("- Verilated::debug attempted," + " but compiled without VL_DEBUG, so messages suppressed.\n" + "- Suggest remake using 'make ... CPPFLAGS=-DVL_DEBUG'\n"); +#endif + } +} + +const char* Verilated::catName(const char* n1, const char* n2, const char* delimiter) VL_MT_SAFE { + // Used by symbol table creation to make module names + static thread_local char* t_strp = nullptr; + static thread_local size_t t_len = 0; + const size_t newlen = std::strlen(n1) + std::strlen(n2) + std::strlen(delimiter) + 1; + if (VL_UNLIKELY(!t_strp || newlen > t_len)) { + if (t_strp) delete[] t_strp; + t_strp = new char[newlen]; + t_len = newlen; + } + char* dp = t_strp; + for (const char* sp = n1; *sp;) *dp++ = *sp++; + for (const char* sp = delimiter; *sp;) *dp++ = *sp++; + for (const char* sp = n2; *sp;) *dp++ = *sp++; + *dp++ = '\0'; + return t_strp; +} + +//========================================================================= +// Flush and exit callbacks + +// Keeping these out of class Verilated to avoid having to include +// in verilated.h (for compilation speed) +using VoidPCbList = std::list>; +static struct { + VerilatedMutex s_flushMutex; + VoidPCbList s_flushCbs VL_GUARDED_BY(s_flushMutex); + VerilatedMutex s_exitMutex; + VoidPCbList s_exitCbs VL_GUARDED_BY(s_exitMutex); +} VlCbStatic; + +static void addCbFlush(Verilated::VoidPCb cb, void* datap) + VL_MT_SAFE_EXCLUDES(VlCbStatic.s_flushMutex) { + const VerilatedLockGuard lock{VlCbStatic.s_flushMutex}; + std::pair pair(cb, datap); + VlCbStatic.s_flushCbs.remove(pair); // Just in case it's a duplicate + VlCbStatic.s_flushCbs.push_back(pair); +} +static void addCbExit(Verilated::VoidPCb cb, void* datap) + VL_MT_SAFE_EXCLUDES(VlCbStatic.s_exitMutex) { + const VerilatedLockGuard lock{VlCbStatic.s_exitMutex}; + std::pair pair(cb, datap); + VlCbStatic.s_exitCbs.remove(pair); // Just in case it's a duplicate + VlCbStatic.s_exitCbs.push_back(pair); +} +static void removeCbFlush(Verilated::VoidPCb cb, void* datap) + VL_MT_SAFE_EXCLUDES(VlCbStatic.s_flushMutex) { + const VerilatedLockGuard lock{VlCbStatic.s_flushMutex}; + std::pair pair(cb, datap); + VlCbStatic.s_flushCbs.remove(pair); +} +static void removeCbExit(Verilated::VoidPCb cb, void* datap) + VL_MT_SAFE_EXCLUDES(VlCbStatic.s_exitMutex) { + const VerilatedLockGuard lock{VlCbStatic.s_exitMutex}; + std::pair pair(cb, datap); + VlCbStatic.s_exitCbs.remove(pair); +} +static void runCallbacks(const VoidPCbList& cbs) VL_MT_SAFE { + for (const auto& i : cbs) i.first(i.second); +} + +void Verilated::addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE { addCbFlush(cb, datap); } +void Verilated::removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE { removeCbFlush(cb, datap); } +void Verilated::runFlushCallbacks() VL_MT_SAFE { + // Flush routines may call flush, so avoid mutex deadlock + static std::atomic s_recursing; + if (!s_recursing++) { + const VerilatedLockGuard lock{VlCbStatic.s_flushMutex}; + runCallbacks(VlCbStatic.s_flushCbs); + } + --s_recursing; + std::fflush(stderr); + std::fflush(stdout); + // When running internal code coverage (gcc --coverage, as opposed to + // verilator --coverage), dump coverage data to properly cover failing + // tests. + VL_GCOV_DUMP(); +} + +void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { addCbExit(cb, datap); } +void Verilated::removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { removeCbExit(cb, datap); } +void Verilated::runExitCallbacks() VL_MT_SAFE { + static std::atomic s_recursing; + if (!s_recursing++) { + const VerilatedLockGuard lock{VlCbStatic.s_exitMutex}; + runCallbacks(VlCbStatic.s_exitCbs); + } + --s_recursing; +} + +const char* Verilated::productName() VL_PURE { return VERILATOR_PRODUCT; } +const char* Verilated::productVersion() VL_PURE { return VERILATOR_VERSION; } + +void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE { + // Slowpath - Called only on error + VL_FATAL_MT(filename, linenum, "", "Null pointer dereferenced"); + VL_UNREACHABLE; +} + +void Verilated::overWidthError(const char* signame) VL_MT_SAFE { + // Slowpath - Called only when signal sets too high of a bit + const std::string msg = ("Testbench C set input '"s + signame + + "' to value that overflows what the signal's width can fit"); + VL_FATAL_MT("unknown", 0, "", msg.c_str()); + VL_UNREACHABLE; +} + +void Verilated::scTimePrecisionError(int sc_prec, int vl_prec) VL_MT_SAFE { + std::ostringstream msg; + msg << "SystemC's sc_set_time_resolution is 10^-" << sc_prec + << ", which does not match Verilog timeprecision 10^-" << vl_prec + << ". Suggest use 'sc_set_time_resolution(" << vl_time_str(vl_prec) + << ")', or Verilator '--timescale-override " << vl_time_str(sc_prec) << "/" + << vl_time_str(sc_prec) << "'"; + const std::string msgs = msg.str(); + VL_FATAL_MT("", 0, "", msgs.c_str()); + VL_UNREACHABLE; +} + +void Verilated::scTraceBeforeElaborationError() VL_MT_SAFE { + // Slowpath - Called only when trace file opened before SystemC elaboration + VL_FATAL_MT("unknown", 0, "", + "%Error: Verilated*Sc::open(...) was called before sc_core::sc_start(). " + "Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file."); + VL_UNREACHABLE; +} + +void Verilated::stackCheck(QData needSize) VL_MT_UNSAFE { + // Slowpath - Called only when constructing +#ifdef _VL_HAVE_GETRLIMIT + QData haveSize = 0; + rlimit rlim; + if (0 == getrlimit(RLIMIT_STACK, &rlim)) { + haveSize = rlim.rlim_cur; + if (haveSize == RLIM_INFINITY) haveSize = rlim.rlim_max; + if (haveSize == RLIM_INFINITY) haveSize = 0; + } + // VL_PRINTF_MT("-Info: stackCheck(%" PRIu64 ") have %" PRIu64 "\n", needSize, haveSize); + // Check and request for 1.5x need. This is automated so the user doesn't need to do anything. + QData requestSize = needSize + needSize / 2; + if (VL_UNLIKELY(haveSize && needSize && haveSize < requestSize)) { + // Try to increase the stack limit to the requested size + rlim.rlim_cur = requestSize; + if ( +#ifdef _VL_TEST_RLIMIT_FAIL + true || +#endif + setrlimit(RLIMIT_STACK, &rlim)) { + VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb" + " which may be too small; failed to request more" + " using 'ulimit -s %" PRIu64 "'\n", + haveSize / 1024, requestSize); + } + } +#else + (void)needSize; // Unused argument +#endif +} + +void Verilated::mkdir(const char* dirname) VL_MT_UNSAFE { +#if defined(_WIN32) || defined(__MINGW32__) + ::mkdir(dirname); +#else + ::mkdir(dirname, 0777); +#endif +} + +void Verilated::quiesce() VL_MT_SAFE { + // Wait until all threads under this evaluation are quiet +} + +int Verilated::exportFuncNum(const char* namep) VL_MT_SAFE { + return VerilatedImp::exportFindNum(namep); +} + +void Verilated::endOfThreadMTaskGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { + VL_DEBUG_IF(VL_DBG_MSGF("End of thread mtask\n");); + VerilatedThreadMsgQueue::flush(evalMsgQp); +} + +void Verilated::endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { + // It doesn't work to set endOfEvalReqd on the threadpool thread + // and then check it on the eval thread since it's thread local. + // It should be ok to call into endOfEvalGuts, it returns immediately + // if there are no transactions. + VL_DEBUG_IF(VL_DBG_MSGF("End-of-eval cleanup\n");); + VerilatedThreadMsgQueue::flush(evalMsgQp); + evalMsgQp->process(); +} + +//=========================================================================== +// VerilatedImp:: Methods + +void VerilatedImp::versionDump() VL_MT_SAFE { + VL_PRINTF_MT(" Version: %s %s\n", Verilated::productName(), Verilated::productVersion()); +} + +//=========================================================================== +// VerilatedModel:: Methods + +VerilatedModel::VerilatedModel(VerilatedContext& context) + : m_context{context} {} + +std::unique_ptr VerilatedModel::traceConfig() const { return nullptr; } + +//====================================================================== +// VerilatedVar:: Methods + +// cppcheck-suppress unusedFunction // Used by applications +uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE { + uint32_t size = 1; + switch (vltype()) { + case VLVT_PTR: size = sizeof(void*); break; + case VLVT_UINT8: size = sizeof(CData); break; + case VLVT_UINT16: size = sizeof(SData); break; + case VLVT_UINT32: size = sizeof(IData); break; + case VLVT_UINT64: size = sizeof(QData); break; + case VLVT_WDATA: size = VL_WORDS_I(entBits()) * sizeof(IData); break; + default: size = 0; break; // LCOV_EXCL_LINE + } + return size; +} + +size_t VerilatedVarProps::totalSize() const { + size_t size = entSize(); + for (int udim = 0; udim < udims(); ++udim) size *= m_unpacked[udim].elements(); + return size; +} + +void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE { + if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr; + if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr; + const int indxAdj = indx - low(dim); + uint8_t* bytep = reinterpret_cast(datap); + // If on index 1 of a 2 index array, then each index 1 is index2sz*entsz + size_t slicesz = entSize(); + for (int d = dim + 1; d <= udims(); ++d) slicesz *= elements(d); + bytep += indxAdj * slicesz; + return bytep; +} + +//====================================================================== +// VerilatedScope:: Methods + +VerilatedScope::VerilatedScope(VerilatedSyms* symsp, const char* suffixp, const char* identifier, + const char* defnamep, int8_t timeunit, Type type) + : m_symsp{symsp} + , m_namep{[symsp, suffixp]() { + // We don't want the space and reference-count access overhead of strings. + const char* prefixp = symsp->name(); + char* const namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2]; + char* dp = namep; + for (const char* sp = prefixp; *sp;) *dp++ = *sp++; + if (*prefixp && *suffixp) *dp++ = '.'; + for (const char* sp = suffixp; *sp;) *dp++ = *sp++; + *dp++ = '\0'; + return namep; + }()} + , m_identifierp{identifier} + , m_defnamep{defnamep} + , m_timeunit{timeunit} + , m_type{type} { + Verilated::threadContextp()->impp()->scopeInsert(this); +} + +VerilatedScope::~VerilatedScope() { + // Memory cleanup - not called during normal operation + Verilated::threadContextp()->impp()->scopeErase(this); + VL_DO_DANGLING(delete[] m_namep, m_namep); + VL_DO_DANGLING(delete[] m_callbacksp, m_callbacksp); + VL_DO_DANGLING(delete m_varsp, m_varsp); + VL_DEBUG_IFDEF(m_funcnumMax = 0;); +} + +void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE { + // Slowpath - called once/scope*export at construction + // Insert a exported function into scope table + const int funcnum = VerilatedImp::exportInsert(namep, cb); + if (!finalize) { + // Need two passes so we know array size to create + // Alternative is to dynamically stretch the array, which is more code, and slower. + if (funcnum >= m_funcnumMax) m_funcnumMax = funcnum + 1; + } else { + if (VL_UNCOVERABLE(funcnum >= m_funcnumMax)) { + VL_FATAL_MT(__FILE__, __LINE__, "", // LCOV_EXCL_LINE + "Internal: Bad funcnum vs. pre-finalize maximum"); + } + if (VL_UNLIKELY(!m_callbacksp)) { // First allocation + m_callbacksp = new void*[m_funcnumMax]; + std::memset(m_callbacksp, 0, m_funcnumMax * sizeof(void*)); + } + m_callbacksp[funcnum] = cb; + } +} + +void VerilatedScope::varInsert(const char* namep, void* datap, bool isParam, + VerilatedVarType vltype, int vlflags, int udims, + int pdims...) VL_MT_UNSAFE { + // Grab dimensions + // In the future we may just create a large table at emit time and + // statically construct from that. + + if (!m_varsp) m_varsp = new VerilatedVarNameMap; + VerilatedVar var(namep, datap, vltype, static_cast(vlflags), udims, pdims, + isParam); + + va_list ap; + va_start(ap, pdims); + for (int i = 0; i < udims; ++i) { + const int msb = va_arg(ap, int); + const int lsb = va_arg(ap, int); + var.m_unpacked[i].m_left = msb; + var.m_unpacked[i].m_right = lsb; + } + for (int i = 0; i < pdims; ++i) { + const int msb = va_arg(ap, int); + const int lsb = va_arg(ap, int); + var.m_packed[i].m_left = msb; + var.m_packed[i].m_right = lsb; + } + va_end(ap); + + m_varsp->emplace(namep, var); +} + +// cppcheck-suppress unusedFunction // Used by applications +VerilatedVar* VerilatedScope::varFind(const char* namep) const VL_MT_SAFE_POSTINIT { + if (VL_LIKELY(m_varsp)) { + const auto it = m_varsp->find(namep); + if (VL_LIKELY(it != m_varsp->end())) return &(it->second); + } + return nullptr; +} + +void* VerilatedScope::exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE { + if (VL_UNLIKELY(!scopep)) return exportFindNullError(funcnum); + // If function is registered only once across all scopes, fast path it. + // UVM for example expects to find uvm_polling_value_change_notify + // from a different scope than where decared. + VL_DEBUG_IFDEF(assert(funcnum < VerilatedImp::exportFlatCbs().size());); + { + void* const cbp = VerilatedImp::exportFlatCbs()[funcnum]; + if (VL_LIKELY(cbp)) return cbp; + } + // Else specific scope-based export call + if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) { + // m_callbacksp must be declared, as Max'es are > 0 + void* const cbp = scopep->m_callbacksp[funcnum]; + if (VL_LIKELY(cbp)) return cbp; + } + return scopep->exportFindError(funcnum); // LCOV_EXCL_LINE +} + +void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE { + // Slowpath - Called only when find has failed + const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum) + + "' but scope wasn't set, perhaps due to dpi import call without " + + "'context', or missing svSetScope. See IEEE 1800-2023 35.5.3."); + VL_FATAL_MT("unknown", 0, "", msg.c_str()); + return nullptr; +} + +void* VerilatedScope::exportFindError(int funcnum) const VL_MT_SAFE { + // Slowpath - Called only when find has failed + const std::string msg + = ("Testbench C called '"s + VerilatedImp::exportName(funcnum) + + "' but this DPI export function exists only in other scopes, not scope '" + name() + + "'"); + VL_FATAL_MT("unknown", 0, "", msg.c_str()); + return nullptr; +} + +void VerilatedScope::scopeDump() const { + VL_PRINTF_MT(" SCOPE %p: %s\n", this, name()); + for (int i = 0; i < m_funcnumMax; ++i) { + if (m_callbacksp && m_callbacksp[i]) { + VL_PRINTF_MT(" DPI-EXPORT %p: %s\n", m_callbacksp[i], + VerilatedImp::exportName(i)); + } + } + if (const VerilatedVarNameMap* const ivarsp = this->varsp()) { + for (const auto& i : *ivarsp) VL_PRINTF_MT(" VAR %p: %s\n", &(i.second), i.first); + } +} + +void VerilatedHierarchy::add(const VerilatedScope* fromp, const VerilatedScope* top) { + VerilatedImp::hierarchyAdd(fromp, top); +} + +void VerilatedHierarchy::remove(const VerilatedScope* fromp, const VerilatedScope* top) { + VerilatedImp::hierarchyRemove(fromp, top); +} + +void VerilatedHierarchy::clear() { VerilatedImp::hierarchyClear(); } + +//=========================================================================== +// VerilatedOneThreaded:: Methods + +#ifdef VL_DEBUG +void VerilatedAssertOneThread::fatal_different() VL_MT_SAFE { + VL_FATAL_MT(__FILE__, __LINE__, "", + "Routine called that is single threaded, but called from" + " a different thread than the expected constructing thread"); +} +#endif + +//=========================================================================== +// VlDeleter:: Methods + +void VlDeleter::deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE { + while (true) { + { + VerilatedLockGuard lock{m_mutex}; + if (m_newGarbage.empty()) break; + m_deleteMutex.lock(); + std::swap(m_newGarbage, m_deleteNow); + // m_mutex is unlocked here, so destructors can enqueue new objects + } + for (VlDeletable* const objp : m_deleteNow) delete objp; + m_deleteNow.clear(); + m_deleteMutex.unlock(); + } +} + +//=========================================================================== +// OS functions (last, so we have minimal OS dependencies above) + +#define VL_ALLOW_VERILATEDOS_C +#include "verilatedos_c.h" diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.h new file mode 100644 index 00000000000..15fdab26782 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.h @@ -0,0 +1,1056 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated common header, include for all Verilated C files +/// +/// This file is included automatically by Verilator at the top of all C++ +/// files it generates. It contains standard macros and classes required +/// by the Verilated code. +/// +/// User wrapper code may need to include this to get appropriate +/// structures, however they would generally just include the +/// Verilated-model's header instead (which then includes this). +/// +/// Those macro/function/variable starting or ending in _ are internal, +/// however many of the other function/macros here are also internal. +/// +//************************************************************************* + +#ifndef VERILATOR_VERILATED_H_ +#define VERILATOR_VERILATED_H_ +#define VERILATOR_VERILATED_H_INTERNAL_ +#ifdef VERILATOR_INTERNAL_ +// This file contains definition of VerilationMutex that should +// only be used by verilated code. Verilator itself should use +// mutex from V3Mutex.h. Make sure this file isn't included in +// verilator code. +#error "verilated.h should only be included in verilated code" +#endif + +// clang-format off +#include "verilated_config.h" +#include "verilatedos.h" +#if VM_SC +# include "verilated_sc.h" // Get SYSTEMC_VERSION and time declarations +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// avoided to reduce compile time +#include +#include +#include + +// Allow user to specify their own include file +#ifdef VL_VERILATED_INCLUDE +// cppcheck-suppress preprocessorErrorDirective +# include VL_VERILATED_INCLUDE +#endif +// clang-format on + +using namespace std::literals; // ""s; see SF.7 core guideline + +//============================================================================= +// Switches + +// clang-format off +#if VM_TRACE // Verilator tracing requested +# define WAVES 1 // Set backward compatibility flag +#endif + +// Version check +#if defined(SYSTEMC_VERSION) && (SYSTEMC_VERSION < 20111121) +# warning "Verilator requires SystemC 2.3.* or newer." +#endif +// clang-format on + +class VerilatedContext; +class VerilatedContextImp; +class VerilatedContextImpData; +class VerilatedCovContext; +class VerilatedEvalMsgQueue; +class VerilatedFst; +class VerilatedFstC; +class VerilatedFstSc; +class VerilatedScope; +class VerilatedScopeNameMap; +template +class VerilatedTrace; +class VerilatedTraceBaseC; +class VerilatedTraceConfig; +class VerilatedVar; +class VerilatedVarNameMap; +class VerilatedVcd; +class VerilatedVcdC; +class VerilatedVcdSc; + +//========================================================================= +// Basic types + +// Type letters +// clang-format off +// P // Packed data of bit type (C/S/I/Q/W) +using CData = uint8_t; ///< Data representing 'bit' of 1-8 packed bits +using SData = uint16_t; ///< Data representing 'bit' of 9-16 packed bits +using IData = uint32_t; ///< Data representing 'bit' of 17-32 packed bits +using QData = uint64_t; ///< Data representing 'bit' of 33-64 packed bits +using EData = uint32_t; ///< Data representing one element of WData array +using WData = EData; ///< Data representing >64 packed bits (used as pointer) +// F = float; // No typedef needed; Verilator uses float +// D = double; // No typedef needed; Verilator uses double +// N = std::string; // No typedef needed; Verilator uses string +// U = VlUnpacked; +// R = VlQueue; +// clang-format on + +using WDataInP = const WData*; ///< 'bit' of >64 packed bits as array input to a function +using WDataOutP = WData*; ///< 'bit' of >64 packed bits as array output from a function + +enum VerilatedVarType : uint8_t { + VLVT_UNKNOWN = 0, + VLVT_PTR, // Pointer to something + VLVT_UINT8, // AKA CData + VLVT_UINT16, // AKA SData + VLVT_UINT32, // AKA IData + VLVT_UINT64, // AKA QData + VLVT_WDATA, // AKA WData + VLVT_STRING, // C++ string + VLVT_REAL // AKA double +}; + +enum VerilatedVarFlags { + VLVD_0 = 0, // None + VLVD_IN = 1, // == vpiInput + VLVD_OUT = 2, // == vpiOutput + VLVD_INOUT = 3, // == vpiInOut + VLVD_NODIR = 5, // == vpiNoDirection + VLVF_MASK_DIR = 7, // Bit mask for above directions + // Flags + VLVF_PUB_RD = (1 << 8), // Public readable + VLVF_PUB_RW = (1 << 9), // Public writable + VLVF_DPI_CLAY = (1 << 10), // DPI compatible C standard layout + VLVF_CONTINUOUSLY = (1 << 11), // Is continously assigned + VLVF_FORCEABLE = (1 << 12), // Forceable + VLVF_SIGNED = (1 << 13), // Signed integer + VLVF_BITVAR = (1 << 14) // Four state bit (vs two state logic) +}; + +// IEEE 1800-2023 Table 20-6 +enum class VerilatedAssertType : uint8_t { + ASSERT_TYPE_CONCURRENT = (1 << 0), + ASSERT_TYPE_SIMPLE_IMMEDIATE = (1 << 1), + ASSERT_TYPE_OBSERVED_DEFERRED_IMMEDIATE = (1 << 2), + ASSERT_TYPE_FINAL_DEFERRED_IMMEDIATE = (1 << 3), + ASSERT_TYPE_EXPECT = (1 << 4), + ASSERT_TYPE_UNIQUE = (1 << 5), + ASSERT_TYPE_UNIQUE0 = (1 << 6), + ASSERT_TYPE_PRIORITY = (1 << 7), +}; + +// IEEE 1800-2023 Table 20-7 +enum class VerilatedAssertDirectiveType : uint8_t { + DIRECTIVE_TYPE_ASSERT = (1 << 0), + DIRECTIVE_TYPE_COVER = (1 << 1), + DIRECTIVE_TYPE_ASSUME = (1 << 2), +}; +using VerilatedAssertType_t = std::underlying_type::type; +using VerilatedAssertDirectiveType_t = std::underlying_type::type; + +// Type trait: whether T is a user-defined custom struct +template +struct VlIsCustomStruct : public std::false_type {}; + +// Type trait: used to detect if array element is a custom struct (e.g. for struct arrays) +template +struct VlContainsCustomStruct : VlIsCustomStruct {}; + +//========================================================================= +// Mutex and threading support + +// Return current thread ID (or 0), not super fast, cache if needed +extern uint32_t VL_THREAD_ID() VL_MT_SAFE; + +#ifndef VL_LOCK_SPINS +#define VL_LOCK_SPINS 50000 /// Number of times to spin for a mutex before yielding +#endif + +/// Mutex, wrapped to allow -fthread_safety checks +class VL_CAPABILITY("mutex") VerilatedMutex final { +private: + std::mutex m_mutex; // Mutex + +public: + /// Construct mutex (without locking it) + VerilatedMutex() = default; + ~VerilatedMutex() = default; + VL_UNCOPYABLE(VerilatedMutex); + const VerilatedMutex& operator!() const { return *this; } // For -fthread_safety + /// Acquire/lock mutex + void lock() VL_ACQUIRE() VL_MT_SAFE { + // Try to acquire the lock by spinning. If the wait is short, + // avoids a trap to the OS plus OS scheduler overhead. + if (VL_LIKELY(try_lock())) return; // Short circuit loop + for (int i = 0; i < VL_LOCK_SPINS; ++i) { + if (VL_LIKELY(try_lock())) return; + VL_CPU_RELAX(); + } + // Spinning hasn't worked, pay the cost of blocking. + m_mutex.lock(); + } + /// Release/unlock mutex + void unlock() VL_RELEASE() VL_MT_SAFE { m_mutex.unlock(); } + /// Try to acquire mutex. Returns true on success, and false on failure. + bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE { return m_mutex.try_lock(); } +}; + +/// Lock guard for mutex (ala std::unique_lock), wrapped to allow -fthread_safety checks +class VL_SCOPED_CAPABILITY VerilatedLockGuard final { + VL_UNCOPYABLE(VerilatedLockGuard); + +private: + VerilatedMutex& m_mutexr; + +public: + /// Construct and hold given mutex lock until destruction or unlock() + explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE + : m_mutexr(mutexr) { // Need () or GCC 4.8 false warning + mutexr.lock(); + } + /// Destruct and unlock the mutex + ~VerilatedLockGuard() VL_RELEASE() { m_mutexr.unlock(); } +}; + +// Internals: Remember the calling thread at construction time, and make +// sure later calls use same thread + +class VerilatedAssertOneThread final { + // MEMBERS +#ifdef VL_DEBUG + uint32_t m_threadid; // Thread that is legal +public: + // CONSTRUCTORS + // The constructor establishes the thread id for all later calls. + // If necessary, a different class could be made that inits it otherwise. + VerilatedAssertOneThread() + : m_threadid{VL_THREAD_ID()} {} + ~VerilatedAssertOneThread() { check(); } + // METHODS + // Check that the current thread ID is the same as the construction thread ID + void check() VL_MT_UNSAFE_ONE { + if (VL_UNCOVERABLE(m_threadid != VL_THREAD_ID())) { + if (m_threadid == 0) { + m_threadid = VL_THREAD_ID(); + } else { + fatal_different(); // LCOV_EXCL_LINE + } + } + } + static void fatal_different() VL_MT_SAFE; +#else // !VL_DEBUG +public: + void check() {} +#endif +}; + +//========================================================================= +/// Base class of a Verilator generated (Verilated) model. +/// +/// VerilatedModel is a base class of the user facing primary class generated +/// by Verilator. + +class VerilatedModel VL_NOT_FINAL { + VL_UNCOPYABLE(VerilatedModel); + + VerilatedContext& m_context; // The VerilatedContext this model is instantiated under + +protected: + explicit VerilatedModel(VerilatedContext& context); + virtual ~VerilatedModel() = default; + +public: + /// Returns the VerilatedContext this model is instantiated under + /// Used to get to e.g. simulation time via contextp()->time() + VerilatedContext* contextp() const VL_MT_SAFE { return &m_context; } + /// Returns the hierarchical name of this module instance. + virtual const char* hierName() const = 0; + /// Returns the name of this model (the name of the generated model class). + virtual const char* modelName() const = 0; + /// Returns the thread level parallelism, this model was Verilated with. Always 1 or higher. + virtual unsigned threads() const = 0; + +private: + // The following are for use by Verilator internals only + template + friend class VerilatedTrace; + // Run-time trace configuration requested by this model + virtual std::unique_ptr traceConfig() const; +}; + +//========================================================================= +// Functions overridable by user defines +// (Internals however must use VL_PRINTF_MT, which calls these.) + +// clang-format off +#ifndef VL_PRINTF +# define VL_PRINTF printf ///< Print ala printf, called from main thread; redefine if desired +#endif +#ifndef VL_VPRINTF +# define VL_VPRINTF vprintf ///< Print ala vprintf, called from main thread; redefine if desired +#endif +// clang-format on + +//=========================================================================== +// Internal: Base class to allow virtual destruction + +class VerilatedVirtualBase VL_NOT_FINAL { +public: + VerilatedVirtualBase() = default; + virtual ~VerilatedVirtualBase() = default; +}; + +//=========================================================================== +/// Verilator simulation context +/// +/// The VerilatedContext contains the information common across all models +/// that are interconnected, for example this contains the simulation time +/// and if $finish was executed. +/// +/// VerilatedContexts maybe created by the user wrapper code and passed +/// when a model is created. If this is not done, then Verilator will use +/// the Verilated::defaultContextp()'s global context. + +class VerilatedContext VL_NOT_FINAL { + friend class VerilatedContextImp; + +private: + // MEMBERS + // Numer of assertion directive type members. Then each of them will represented as 1-bit in a + // mask. + static constexpr size_t ASSERT_DIRECTIVE_TYPE_MASK_WIDTH = 3; + // Specifies how many groups of directive type bit groups there are based on a number of + // assertion types. + // Note: we add one bit to store information whether Verilator's internal + // directive types are enabled, for example `violation if`s. + static constexpr size_t ASSERT_ON_WIDTH + = ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits::digits + + 1; + +protected: + // TYPES + using traceBaseModelCb_t + = std::function; // Type of traceBaseModel callbacks + + // MEMBERS + // Slow path variables + mutable VerilatedMutex m_mutex; // Mutex for most s_s/s_ns members + + struct Serialized final { // All these members serialized/deserialized + // No std::strings or pointers or will serialize badly! + // Fast path + uint64_t m_time = 0; // Current $time (unscaled), 0=at zero, or legacy + std::atomic m_assertOn{ + std::numeric_limits::max()}; // Enabled assertions, + // for each VerilatedAssertType we store + // 3-bits, one for each directive type. Last + // bit guards internal directive types. + bool m_calcUnusedSigs = false; // Waves file on, need all signals calculated + bool m_fatalOnError = true; // Fatal on $stop/non-fatal error + bool m_fatalOnVpiError = true; // Fatal on vpi error/unsupported + bool m_gotError = false; // A $finish statement executed + bool m_gotFinish = false; // A $finish or $stop statement executed + bool m_quiet = false; // Quiet, no summary report + // Slow path + int8_t m_timeunit; // Time unit as 0..15 + int8_t m_timeprecision; // Time precision as 0..15 + int m_errorCount = 0; // Number of errors + int m_errorLimit = 1; // Stop on error number + int m_randReset = 0; // Random reset: 0=all 0s, 1=all 1s, 2=random + int m_randSeed = 0; // Random seed: 0=random + enum { UNITS_NONE = 99 }; // Default based on precision + int m_timeFormatUnits = UNITS_NONE; // $timeformat units + int m_timeFormatPrecision = 0; // $timeformat number of decimal places + int m_timeFormatWidth = 20; // $timeformat character width + // CONSTRUCTORS + Serialized(); + ~Serialized() = default; + } m_s; + + mutable VerilatedMutex m_timeDumpMutex; // Protect misc slow strings + std::string m_timeFormatSuffix VL_GUARDED_BY(m_timeDumpMutex); // $timeformat printf format + std::string m_dumpfile VL_GUARDED_BY(m_timeDumpMutex); // $dumpfile setting + + struct NonSerialized final { // Non-serialized information + // These are reloaded from on command-line settings, so do not need to persist + // Fast path + uint64_t m_profExecStart = 1; // +prof+exec+start time + uint32_t m_profExecWindow = 2; // +prof+exec+window size + // Slow path + std::string m_coverageFilename; // +coverage+file filename + std::string m_profExecFilename; // +prof+exec+file filename + std::string m_profVltFilename; // +prof+vlt filename + std::string m_solverProgram; // SMT solver program + bool m_warnUnsatConstr = true; // Warn on unsatisfied constraints + VlOs::DeltaCpuTime m_cpuTimeStart{false}; // CPU time, starts when create first model + VlOs::DeltaWallTime m_wallTimeStart{false}; // Wall time, starts when create first model + std::vector m_traceBaseModelCbs; // Callbacks to traceRegisterModel + } m_ns; + + mutable VerilatedMutex m_argMutex; // Protect m_argVec, m_argVecLoaded + // no need to be save-restored (serialized) the + // assumption is that the restore is allowed to pass different arguments + struct NonSerializedCommandArgs final { + // Medium speed + std::vector m_argVec; // Argument list + bool m_argVecLoaded = false; // Ever loaded argument list + } m_args VL_GUARDED_BY(m_argMutex); + + // Implementation details + const std::unique_ptr m_impdatap; + // Number of threads to use for simulation (size of m_threadPool + 1 for main thread) + unsigned m_threads = VlOs::getProcessDefaultParallelism(); + // Use numa automatic CPU-to-thread assignment + bool m_useNumaAssign = false; + // Number of threads in added models + unsigned m_threadsInModels = 0; + // The thread pool shared by all models added to this context + std::unique_ptr m_threadPool; + // The execution profiler shared by all models added to this context + std::unique_ptr m_executionProfiler; + // Coverage access + std::unique_ptr m_coveragep; // Pointer for coveragep() + + // File I/O + // Not serialized + mutable VerilatedMutex m_fdMutex; // Protect m_fdps, m_fdFree + std::vector m_fdps VL_GUARDED_BY(m_fdMutex); // File descriptors + // List of free descriptors (SLOW - FOPEN/CLOSE only) + std::vector m_fdFree VL_GUARDED_BY(m_fdMutex); + // List of free descriptors in the MCT region [4, 32) + std::vector m_fdFreeMct VL_GUARDED_BY(m_fdMutex); + + // Magic to check for bad construction + static constexpr uint64_t MAGIC = 0xC35F9A6E5298EE6EULL; // SHA256 "VerilatedContext" + uint64_t m_magic = MAGIC; + +private: + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedContext); + +public: + /// Construct context. Also sets Verilated::threadContextp to the created context. + VerilatedContext(); + ~VerilatedContext(); + + // METHODS - User called + + /// Return if assertions enabled + bool assertOn() const VL_MT_SAFE; + /// Enable all assertion types + void assertOn(bool flag) VL_MT_SAFE; + /// Get enabled status for given assertion types + bool assertOnGet(VerilatedAssertType_t type, + VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE; + /// Set enabled status for given assertion types + void assertOnSet(VerilatedAssertType_t types, + VerilatedAssertDirectiveType_t directives) VL_MT_SAFE; + /// Clear enabled status for given assertion types + void assertOnClear(VerilatedAssertType_t types, + VerilatedAssertDirectiveType_t directives) VL_MT_SAFE; + /// Return if calculating of unused signals (for traces) + bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; } + /// Enable calculation of unused signals (for traces) + void calcUnusedSigs(bool flag) VL_MT_SAFE; + /// Record command-line arguments, for retrieval by $test$plusargs/$value$plusargs, + /// and for parsing +verilator+ run-time arguments. + /// This should be called before the first model is created. + void commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex); + void commandArgs(int argc, char** argv) VL_MT_SAFE { + commandArgs(argc, const_cast(argv)); + } + /// Add a command-line argument to existing arguments + void commandArgsAdd(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex); + /// Match plusargs with a given prefix. Returns static char* valid only for a single call + const char* commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex); + /// Return VerilatedCovContext, allocate if needed + /// Note if get unresolved reference then likely forgot to link verilated_cov.cpp + VerilatedCovContext* coveragep() VL_MT_SAFE; + /// Return debug level + static inline int debug() VL_MT_SAFE; /// Set debug level + /// Debug is currently global, but for forward compatibility have a per-context method + static inline void debug(int val) VL_MT_SAFE; + /// Return current number of errors/assertions + int errorCount() const VL_MT_SAFE { return m_s.m_errorCount; } + /// Set current number of errors/assertions + void errorCount(int val) VL_MT_SAFE; + /// Increment current number of errors/assertions + void errorCountInc() VL_MT_SAFE; + /// Return number of errors/assertions before stop + int errorLimit() const VL_MT_SAFE { return m_s.m_errorLimit; } + /// Set number of errors/assertions before stop + void errorLimit(int val) VL_MT_SAFE; + /// Return if to throw fatal error on $stop/non-fatal + bool fatalOnError() const VL_MT_SAFE { return m_s.m_fatalOnError; } + /// Set to throw fatal error on $stop/non-fatal error + void fatalOnError(bool flag) VL_MT_SAFE; + /// Return if to throw fatal error on VPI errors + bool fatalOnVpiError() const VL_MT_SAFE { return m_s.m_fatalOnVpiError; } + /// Set to throw fatal error on VPI errors + void fatalOnVpiError(bool flag) VL_MT_SAFE; + /// Return if got a $stop or non-fatal error + bool gotError() const VL_MT_SAFE { return m_s.m_gotError; } + /// Set if got a $stop or non-fatal error + void gotError(bool flag) VL_MT_SAFE; + /// Return if got a $finish or $stop/error + bool gotFinish() const VL_MT_SAFE { return m_s.m_gotFinish; } + /// Set if got a $finish or $stop/error + void gotFinish(bool flag) VL_MT_SAFE; + /// Return if quiet enabled + bool quiet() const VL_MT_SAFE { return m_s.m_quiet; } + /// Enable quiet (also prevents need for OS calls to get CPU time) + void quiet(bool flag) VL_MT_SAFE; + /// Return randReset value + int randReset() VL_MT_SAFE { return m_s.m_randReset; } + /// Select initial value of otherwise uninitialized signals. + /// 0 = Set to zeros + /// 1 = Set all bits to one + /// 2 = Randomize all bits + void randReset(int val) VL_MT_SAFE; + /// Return default random seed + int randSeed() const VL_MT_SAFE { return m_s.m_randSeed; } + /// Set default random seed, 0 = seed it automatically + void randSeed(int val) VL_MT_SAFE; + + /// Return statistic: CPU time delta from model created until now + double statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex); + /// Return statistic: Wall time delta from model created until now + double statWallTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex); + /// Print statistics summary (if not quiet) + void statsPrintSummary() VL_MT_UNSAFE; + + // Time handling + /// Returns current simulation time in units of timeprecision(). + /// + /// How Verilator runtime gets the current simulation time: + /// + /// * If using SystemC, time comes from the SystemC kernel-defined + /// sc_time_stamp64(). User's wrapper must not call + /// SimulationContext::time(value) nor timeInc(value). + /// + /// * Else, if SimulationContext::time(value) or + /// SimulationContext::timeInc(value) is ever called with non-zero, + /// then time will come via the context. This allows multiple contexts + /// to exist and have different simulation times. This must not be used + /// with SystemC. Note Verilated::time(value) and + /// Verilated::timeInc(value) call into SimulationContext::time and + /// timeInc, operating on the thread's context. + /// + /// * Else, if VL_TIME_STAMP64 is defined, time comes from the legacy + /// 'uint64_t vl_time_stamp64()' which must a function be defined by + /// the user's wrapper. + /// + /// * Else, time comes from the legacy 'double sc_time_stamp()' which + /// must be a function defined by the user's wrapper. + inline uint64_t time() const VL_MT_SAFE; + /// Set current simulation time. See time() for side effect details + void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; } + /// Advance current simulation time. See time() for side effect details + void timeInc(uint64_t add) VL_MT_UNSAFE { m_s.m_time += add; } + /// Return time units as power-of-ten + int timeunit() const VL_MT_SAFE { return -m_s.m_timeunit; } + /// Set time units as power-of-ten + void timeunit(int value) VL_MT_SAFE; + /// Return time units as IEEE-standard text + const char* timeunitString() const VL_MT_SAFE; + /// Get time precision as power-of-ten + int timeprecision() const VL_MT_SAFE { return -m_s.m_timeprecision; } + /// Return time precision as power-of-ten + inline void timeprecision(int value) VL_MT_SAFE; + /// Get time precision as IEEE-standard text + const char* timeprecisionString() const VL_MT_SAFE; + + /// Get number of threads used for simulation (including the main thread) + unsigned threads() const { return m_threads; } + /// Get number of threads in added models (for statistical use only) + unsigned threadsInModels() const { return m_threadsInModels; } + /// Set number of threads used for simulation (including the main thread) + /// Can only be called before the thread pool is created (before first model is added). + void threads(unsigned n); + + /// Use numa automatic CPU-to-thread assignment. + bool useNumaAssign() const VL_MT_SAFE { return m_useNumaAssign; } + /// Set numa assignment of threads to cores + /// Defaults false; set true automatically when threads() called; + /// call this to override back to false if numa assignment not wanted. + void useNumaAssign(bool flag); + + /// Trace signals in models within the context; called by application code + void trace(VerilatedTraceBaseC* tfp, int levels, int options = 0); + /// Allow traces to at some point be enabled (disables some optimizations) + void traceEverOn(bool flag) VL_MT_SAFE { + if (flag) calcUnusedSigs(true); + } + + /// For debugging, print much of the Verilator internal state. + /// The output of this function may change in future + /// releases - contact the authors before production use. + void internalsDump() const VL_MT_SAFE; + + /// For debugging, print text list of all scope names with + /// dpiImport/Export context. This function may change in future + /// releases - contact the authors before production use. + void scopesDump() const VL_MT_SAFE; + + // METHODS - public but for internal use only + + // Internal: access to implementation class + VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast(this); } + const VerilatedContextImp* impp() const VL_MT_SAFE { + return reinterpret_cast(this); + } + + // Internal: Model and thread setup + void addModel(const VerilatedModel* modelp); + VerilatedVirtualBase* threadPoolp(); + void prepareClone(); + VerilatedVirtualBase* threadPoolpOnClone(); + VerilatedVirtualBase* + enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)); + + // Internal: coverage + std::string coverageFilename() const VL_MT_SAFE; + void coverageFilename(const std::string& flag) VL_MT_SAFE; + + // Internal: $dumpfile + std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex); + void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex); + std::string dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex); + + // Internal: --prof-exec related settings + uint64_t profExecStart() const VL_MT_SAFE { return m_ns.m_profExecStart; } + void profExecStart(uint64_t flag) VL_MT_SAFE; + uint32_t profExecWindow() const VL_MT_SAFE { return m_ns.m_profExecWindow; } + void profExecWindow(uint64_t flag) VL_MT_SAFE; + std::string profExecFilename() const VL_MT_SAFE; + void profExecFilename(const std::string& flag) VL_MT_SAFE; + std::string profVltFilename() const VL_MT_SAFE; + void profVltFilename(const std::string& flag) VL_MT_SAFE; + + // Internal: SMT solver program + std::string solverProgram() const VL_MT_SAFE; + void solverProgram(const std::string& flag) VL_MT_SAFE; + // Internal: Control display of unsatisfied constraints + bool warnUnsatConstr() const VL_MT_SAFE { return m_ns.m_warnUnsatConstr; } + void warnUnsatConstr(bool flag) VL_MT_SAFE { m_ns.m_warnUnsatConstr = flag; } + + // Internal: Find scope + const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE; + const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE; + + // Internal: Serialization setup + static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); } + void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; } + + // Internal: trace registration + void traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE; + + // Internal: Check magic number + static void checkMagic(const VerilatedContext* contextp); + void selfTestClearMagic() { m_magic = 0x2; } +}; + +//=========================================================================== +// Verilator symbol table base class +// Used for internal VPI implementation, and introspection into scopes + +class VerilatedSyms VL_NOT_FINAL { +public: // But for internal use only + // MEMBERS + // Keep first so is at zero offset for fastest code + VerilatedContext* const _vm_contextp__; // Context for current model + VerilatedEvalMsgQueue* __Vm_evalMsgQp; + explicit VerilatedSyms(VerilatedContext* contextp); // Pass null for default context + ~VerilatedSyms(); + VL_UNCOPYABLE(VerilatedSyms); + + virtual const char* name() const = 0; +}; + +//=========================================================================== +// Verilator scope information class +// Used for internal VPI implementation, and introspection into scopes + +class VerilatedScope final { +public: + enum Type : uint8_t { + SCOPE_MODULE, + SCOPE_OTHER, + SCOPE_PACKAGE + }; // Type of a scope, currently only module and package are interesting +private: + // Fastpath: + VerilatedSyms* const m_symsp; // Symbol table + void** m_callbacksp = nullptr; // Callback table pointer (Fastpath) + int m_funcnumMax = 0; // Maximum function number stored (Fastpath) + // 4 bytes padding (on -m64), for rent. + VerilatedVarNameMap* m_varsp = nullptr; // Variable map + const char* const m_namep; // Scope name (Slowpath) + const char* const m_identifierp; // Identifier of scope (with escapes removed) + const char* const m_defnamep; // Definition name (SCOPE_MODULE only) + const int8_t m_timeunit; // Timeunit in negative power-of-10 + const Type m_type; // Type of the scope + +public: // But internals only - called from verilated modules, VerilatedSyms + VerilatedScope(VerilatedSyms* symsp, const char* suffixp, const char* identifier, + const char* defnamep, int8_t timeunit, Type type); + ~VerilatedScope(); + + void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE; + void varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype, + int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE; + // ACCESSORS + const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; } + const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; } + const char* defname() const VL_MT_SAFE_POSTINIT { return m_defnamep; } + int8_t timeunit() const VL_MT_SAFE_POSTINIT { return m_timeunit; } + VerilatedSyms* symsp() const VL_MT_SAFE_POSTINIT { return m_symsp; } + VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT; + VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; } + void scopeDump() const; + void* exportFindError(int funcnum) const VL_MT_SAFE; + static void* exportFindNullError(int funcnum) VL_MT_SAFE; + static void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE; + Type type() const { return m_type; } +}; + +class VerilatedHierarchy final { +public: + static void add(const VerilatedScope* fromp, const VerilatedScope* top); + static void remove(const VerilatedScope* fromp, const VerilatedScope* top); + static void clear(); +}; + +//=========================================================================== +/// Verilator global static information class + +class Verilated final { + // MEMBERS + + // Internal Note: There should be no Serialized state in Verilated::, + // instead serialized state should all be in VerilatedContext:: as by + // definition it needs to vary per-simulation + + // Internal note: Globals may multi-construct, see verilated.cpp top. + + // Debug is reloaded from on command-line settings, so do not need to persist + static int s_debug; // See accessors... only when VL_DEBUG set + + static VerilatedContext* s_lastContextp; // Last context constructed/attached + + // Not covered by mutex, as per-thread + static thread_local struct ThreadLocal { + // No non-POD objects here due to this: + // Internal note: Globals may multi-construct, see verilated.cpp top. + + // Fast path + VerilatedContext* t_contextp = nullptr; // Thread's context + uint32_t t_mtaskId = 0; // mtask# executing on this thread + // Messages maybe pending on thread, needs end-of-eval calls + uint32_t t_endOfEvalReqd = 0; + const VerilatedScope* t_dpiScopep = nullptr; // DPI context scope + const char* t_dpiFilename = nullptr; // DPI context filename + int t_dpiLineno = 0; // DPI context line number + + ThreadLocal() = default; + ~ThreadLocal() = default; + } t_s; + + friend struct VerilatedInitializer; + + // CONSTRUCTORS + VL_UNCOPYABLE(Verilated); + +public: + // METHODS - User called + +#ifdef VL_DEBUG + /// Return debug level + /// When multithreaded this may not immediately react to another thread + /// changing the level (no mutex) + static int debug() VL_MT_SAFE { return s_debug; } +#else + /// Return constant 0 debug level, so C++'s optimizer rips up + static constexpr int debug() VL_PURE { return 0; } +#endif + /// Enable debug of internal verilated code + static void debug(int level) VL_MT_SAFE; + + /// Set the last VerilatedContext accessed + /// Generally threadContextp(value) should be called instead + static void lastContextp(VerilatedContext* contextp) VL_MT_SAFE { s_lastContextp = contextp; } + /// Return the last VerilatedContext accessed + /// Generally threadContextp() should be called instead + static VerilatedContext* lastContextp() VL_MT_SAFE { + if (!s_lastContextp) lastContextp(defaultContextp()); + return s_lastContextp; + } + /// Set the VerilatedContext used by the current thread + + /// If using multiple contexts, and threads are created by the user's + /// wrapper (not Verilator itself) then this must be called to set the + /// context that applies to each thread + static void threadContextp(VerilatedContext* contextp) VL_MT_SAFE { + t_s.t_contextp = contextp; + lastContextp(contextp); + } + /// Return the VerilatedContext for the current thread + static VerilatedContext* threadContextp() VL_MT_SAFE { + if (VL_UNLIKELY(!t_s.t_contextp)) t_s.t_contextp = lastContextp(); + return t_s.t_contextp; + } + /// Return the global VerilatedContext, used if none created by user + static VerilatedContext* defaultContextp() VL_MT_SAFE { + static VerilatedContext s_s; + return &s_s; + } + +#ifndef VL_NO_LEGACY + /// Return VerilatedContext::assertOn() using current thread's VerilatedContext + static bool assertOn() VL_MT_SAFE { return Verilated::threadContextp()->assertOn(); } + /// Call VerilatedContext::assertOn using current thread's VerilatedContext + static void assertOn(bool flag) VL_MT_SAFE { Verilated::threadContextp()->assertOn(flag); } + /// Return VerilatedContext::calcUnusedSigs using current thread's VerilatedContext + static bool calcUnusedSigs() VL_MT_SAFE { + return Verilated::threadContextp()->calcUnusedSigs(); + } + /// Call VerilatedContext::calcUnusedSigs using current thread's VerilatedContext + static void calcUnusedSigs(bool flag) VL_MT_SAFE { + Verilated::threadContextp()->calcUnusedSigs(flag); + } + /// Call VerilatedContext::commandArgs using current thread's VerilatedContext + static void commandArgs(int argc, const char** argv) VL_MT_SAFE { + Verilated::threadContextp()->commandArgs(argc, argv); + } + static void commandArgs(int argc, char** argv) VL_MT_SAFE { + commandArgs(argc, const_cast(argv)); + } + /// Call VerilatedContext::commandArgsAdd using current thread's VerilatedContext + static void commandArgsAdd(int argc, const char** argv) { + Verilated::threadContextp()->commandArgsAdd(argc, argv); + } + /// Return VerilatedContext::commandArgsPlusMatch using current thread's VerilatedContext + static const char* commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE { + return Verilated::threadContextp()->commandArgsPlusMatch(prefixp); + } + /// Return VerilatedContext::errorLimit using current thread's VerilatedContext + static int errorLimit() VL_MT_SAFE { return Verilated::threadContextp()->errorLimit(); } + /// Call VerilatedContext::errorLimit using current thread's VerilatedContext + static void errorLimit(int val) VL_MT_SAFE { Verilated::threadContextp()->errorLimit(val); } + /// Return VerilatedContext::fatalOnError using current thread's VerilatedContext + static bool fatalOnError() VL_MT_SAFE { return Verilated::threadContextp()->fatalOnError(); } + /// Call VerilatedContext::fatalOnError using current thread's VerilatedContext + static void fatalOnError(bool flag) VL_MT_SAFE { + Verilated::threadContextp()->fatalOnError(flag); + } + /// Return VerilatedContext::fatalOnVpiError using current thread's VerilatedContext + static bool fatalOnVpiError() VL_MT_SAFE { + return Verilated::threadContextp()->fatalOnVpiError(); + } + /// Call VerilatedContext::fatalOnVpiError using current thread's VerilatedContext + static void fatalOnVpiError(bool flag) VL_MT_SAFE { + Verilated::threadContextp()->fatalOnVpiError(flag); + } + /// Return VerilatedContext::gotError using current thread's VerilatedContext + static bool gotError() VL_MT_SAFE { return Verilated::threadContextp()->gotError(); } + /// Call VerilatedContext::gotError using current thread's VerilatedContext + static void gotError(bool flag) VL_MT_SAFE { Verilated::threadContextp()->gotError(flag); } + /// Return VerilatedContext::gotFinish using current thread's VerilatedContext + static bool gotFinish() VL_MT_SAFE { return Verilated::threadContextp()->gotFinish(); } + /// Call VerilatedContext::gotFinish using current thread's VerilatedContext + static void gotFinish(bool flag) VL_MT_SAFE { Verilated::threadContextp()->gotFinish(flag); } + /// Return VerilatedContext::randReset using current thread's VerilatedContext + static int randReset() VL_MT_SAFE { return Verilated::threadContextp()->randReset(); } + /// Call VerilatedContext::randReset using current thread's VerilatedContext + static void randReset(int val) VL_MT_SAFE { Verilated::threadContextp()->randReset(val); } + /// Return VerilatedContext::randSeed using current thread's VerilatedContext + static int randSeed() VL_MT_SAFE { return Verilated::threadContextp()->randSeed(); } + /// Call VerilatedContext::randSeed using current thread's VerilatedContext + static void randSeed(int val) VL_MT_SAFE { Verilated::threadContextp()->randSeed(val); } + /// Return VerilatedContext::time using current thread's VerilatedContext + static uint64_t time() VL_MT_SAFE { return Verilated::threadContextp()->time(); } + /// Call VerilatedContext::time using current thread's VerilatedContext + static void time(uint64_t val) VL_MT_SAFE { Verilated::threadContextp()->time(val); } + /// Call VerilatedContext::timeInc using current thread's VerilatedContext + static void timeInc(uint64_t add) VL_MT_UNSAFE { Verilated::threadContextp()->timeInc(add); } + // Deprecated + static int timeunit() VL_MT_SAFE { return Verilated::threadContextp()->timeunit(); } + static int timeprecision() VL_MT_SAFE { return Verilated::threadContextp()->timeprecision(); } + /// Call VerilatedContext::tracesEverOn using current thread's VerilatedContext + static void traceEverOn(bool flag) VL_MT_SAFE { + Verilated::threadContextp()->traceEverOn(flag); + } +#endif + + /// Callback typedef for addFlushCb, addExitCb + using VoidPCb = void (*)(void*); + /// Add callback to run on global flush + static void addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE; + /// Remove callback to run on global flush + static void removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE; + /// Run flush callbacks registered with addFlushCb + static void runFlushCallbacks() VL_MT_SAFE; +#ifndef VL_NO_LEGACY + static void flushCall() VL_MT_SAFE { runFlushCallbacks(); } // Deprecated +#endif + /// Add callback to run prior to exit termination + static void addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE; + /// Remove callback to run prior to exit termination + static void removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE; + /// Run exit callbacks registered with addExitCb + static void runExitCallbacks() VL_MT_SAFE; + + /// Return product name for (at least) VPI + static const char* productName() VL_PURE; + /// Return product version for (at least) VPI + static const char* productVersion() VL_PURE; + + /// Call OS to make a directory + static void mkdir(const char* dirname) VL_MT_UNSAFE; + + /// When multithreaded, quiesce the model to prepare for trace/saves/coverage + /// This may only be called when no locks are held. + static void quiesce() VL_MT_SAFE; + +#ifndef VL_NO_LEGACY + /// For debugging, print much of the Verilator internal state. + /// The output of this function may change in future + /// releases - contact the authors before production use. + static void internalsDump() VL_MT_SAFE { Verilated::threadContextp()->internalsDump(); } + /// For debugging, print text list of all scope names with + /// dpiImport/Export context. This function may change in future + /// releases - contact the authors before production use. + static void scopesDump() VL_MT_SAFE { Verilated::threadContextp()->scopesDump(); } + // Internal: Find scope + static const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE { + return Verilated::threadContextp()->scopeFind(namep); + } + static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE { + return Verilated::threadContextp()->scopeNameMap(); + } +#endif + + // METHODS - INTERNAL USE ONLY (but public due to what uses it) + // Internal: Create a new module name by concatenating two strings + // Returns pointer to thread-local static data (overwritten on next call) + static const char* catName(const char* n1, const char* n2, + const char* delimiter = ".") VL_MT_SAFE; + + // Internal: Throw signal assertion + static void nullPointerError(const char* filename, int linenum) VL_ATTR_NORETURN VL_MT_SAFE; + static void overWidthError(const char* signame) VL_ATTR_NORETURN VL_MT_SAFE; + static void scTimePrecisionError(int sc_prec, int vl_prec) VL_ATTR_NORETURN VL_MT_SAFE; + static void scTraceBeforeElaborationError() VL_ATTR_NORETURN VL_MT_SAFE; + static void stackCheck(QData needSize) VL_MT_UNSAFE; + + // Internal: Get and set DPI context + static const VerilatedScope* dpiScope() VL_MT_SAFE { return t_s.t_dpiScopep; } + static void dpiScope(const VerilatedScope* scopep) VL_MT_SAFE { t_s.t_dpiScopep = scopep; } + static void dpiContext(const VerilatedScope* scopep, const char* filenamep, + int lineno) VL_MT_SAFE { + t_s.t_dpiScopep = scopep; + t_s.t_dpiFilename = filenamep; + t_s.t_dpiLineno = lineno; + } + static void dpiClearContext() VL_MT_SAFE { t_s.t_dpiScopep = nullptr; } + static bool dpiInContext() VL_MT_SAFE { return t_s.t_dpiScopep != nullptr; } + static const char* dpiFilenamep() VL_MT_SAFE { return t_s.t_dpiFilename; } + static int dpiLineno() VL_MT_SAFE { return t_s.t_dpiLineno; } + static int exportFuncNum(const char* namep) VL_MT_SAFE; + + // Internal: Set the mtaskId, called when an mtask starts + // Per thread, so no need to be in VerilatedContext + static uint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; } + static void mtaskId(uint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; } + static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; } + static void endOfEvalReqdDec() VL_MT_SAFE { --t_s.t_endOfEvalReqd; } + + // Internal: Called at end of each thread mtask, before finishing eval + static void endOfThreadMTask(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { + mtaskId(0); + if (VL_UNLIKELY(t_s.t_endOfEvalReqd)) endOfThreadMTaskGuts(evalMsgQp); + } + // Internal: Called at end of eval loop + static void endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE; + +private: + static void endOfThreadMTaskGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE; +}; + +void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); } +int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); } + +//========================================================================= +// Data Types + +#include "verilated_types.h" + +//========================================================================= +// Functions + +#include "verilated_funcs.h" + +//====================================================================== + +void VerilatedContext::timeprecision(int value) VL_MT_SAFE { + if (value < 0) value = -value; // Stored as 0..15 +#if VM_SC + int sc_prec = 99; +#endif + { + const VerilatedLockGuard lock{m_mutex}; + m_s.m_timeprecision = value; +#if VM_SC + const sc_core::sc_time sc_res = sc_core::sc_get_time_resolution(); + double mult = 1.0; + for (int i = 0; i < 16; i++) { + if (sc_res == sc_core::sc_time(mult, sc_core::SC_FS)) { + sc_prec = 15 - i; + break; + } + mult *= 10.0; + } + // SC_AS, SC_ZS, SC_YS not supported as no Verilog equivalent; will error below +#endif + } +#if VM_SC + if (VL_UNLIKELY(value != sc_prec)) Verilated::scTimePrecisionError(sc_prec, value); +#endif +} + +#undef VERILATOR_VERILATED_H_INTERNAL_ +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_config.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_config.h new file mode 100644 index 00000000000..2b14db5a74b --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_config.h @@ -0,0 +1,30 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +/// +/// \file +/// \brief Verilator program version information header +/// +//************************************************************************* + +/// Verilator product name, e.g. "Verilator" +// Autoconf substitutes this with the strings from AC_INIT. +#define VERILATOR_PRODUCT "Verilator" + +/// Verilator version name, e.g. "1.002 2000-01-01" +// Autoconf substitutes this with the strings from AC_INIT. +#define VERILATOR_VERSION "5.046 2026-02-28" + +/// Verilator version number as integer +/// As major * 100000 + minor * 1000, e.g. 1002000 == 1.002 +// Autoconf substitutes this with the strings from AC_INIT. +#define VERILATOR_VERSION_INTEGER 5046000 diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.cpp new file mode 100644 index 00000000000..123fa6dbeb7 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.cpp @@ -0,0 +1,541 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated coverage analysis implementation code +/// +/// This file must be compiled and linked against all Verilated objects +/// that use coverage. +/// +/// Use "verilator --coverage" to add this to the Makefile for the linker. +/// +//============================================================================= + +#include "verilatedos.h" + +#include "verilated_cov.h" + +#include "verilated.h" +#include "verilated_cov_key.h" + +#include +#include +#include +#include + +//============================================================================= +// VerilatedCovConst +// Implementation constants + +struct VerilatedCovConst VL_NOT_FINAL { + // TYPES + enum { MAX_KEYS = 33 }; // Maximum user arguments + filename+lineno + enum { KEY_UNDEF = 0 }; // Magic key # for unspecified values +}; + +//============================================================================= +// VerilatedCovImpItem +// Implementation class for a VerilatedCov item + +class VerilatedCovImpItem VL_NOT_FINAL { +public: // But only local to this file + // MEMBERS + int m_keys[VerilatedCovConst::MAX_KEYS]; // Key + int m_vals[VerilatedCovConst::MAX_KEYS]; // Value for specified key + // CONSTRUCTORS + // Derived classes should call zero() in their constructor + VerilatedCovImpItem() { + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { + m_keys[i] = VerilatedCovConst::KEY_UNDEF; + m_vals[i] = 0; + } + } + virtual ~VerilatedCovImpItem() = default; + virtual uint64_t count() const = 0; + virtual void zero() const = 0; +}; + +//============================================================================= +// VerilatedCoverItem templated for a specific class +// Creates a new coverage item for the specified type. +// This isn't in the header file for auto-magic conversion because it +// inlines to too much code and makes compilation too slow. + +template +class VerilatedCoverItemSpec final : public VerilatedCovImpItem { +private: + // MEMBERS + T* m_countp; // Count value +public: + // METHODS + // cppcheck-suppress truncLongCastReturn + uint64_t count() const override { return *m_countp; } + void zero() const override { *m_countp = 0; } + // CONSTRUCTORS + // cppcheck-suppress noExplicitConstructor + explicit VerilatedCoverItemSpec(T* countp) + : m_countp{countp} { + *m_countp = 0; + } + ~VerilatedCoverItemSpec() override = default; +}; + +//============================================================================= +// VerilatedCovImp +// +// Implementation class for VerilatedCovContext. See that class for +// public method information. All value and keys are indexed into a +// unique number. Thus we can greatly reduce the storage requirements for +// otherwise identical keys. + +class VerilatedCovImp final : public VerilatedCovContext { +private: + // TYPES + using ValueIndexMap = std::map; + using IndexValueMap = std::map; + using ItemList = std::deque; + + // MEMBERS + VerilatedContext* const m_contextp; // Context VerilatedCovImp is pointed-to by + mutable VerilatedMutex m_mutex; // Protects all members + ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex); // Unique arbitrary value for values + IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex); // Unique arbitrary value for keys + ItemList m_items VL_GUARDED_BY(m_mutex); // List of all items + int m_nextIndex VL_GUARDED_BY(m_mutex) + = (VerilatedCovConst::KEY_UNDEF + 1); // Next insert value + + VerilatedCovImpItem* m_insertp VL_GUARDED_BY(m_mutex) = nullptr; // Item about to insert + const char* m_insertFilenamep VL_GUARDED_BY(m_mutex) = nullptr; // Filename about to insert + int m_insertLineno VL_GUARDED_BY(m_mutex) = 0; // Line number about to insert + bool m_forcePerInstance VL_GUARDED_BY(m_mutex) = false; // Force per_instance + +public: + // CONSTRUCTORS + explicit VerilatedCovImp(VerilatedContext* contextp) + : m_contextp{contextp} {} + VL_UNCOPYABLE(VerilatedCovImp); + +protected: + friend class VerilatedCovContext; + ~VerilatedCovImp() override { clearGuts(); } + +private: + // PRIVATE METHODS + int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) { + const auto iter = m_valueIndexes.find(value); + if (iter != m_valueIndexes.end()) return iter->second; + ++m_nextIndex; + assert(m_nextIndex > 0); // Didn't rollover + m_valueIndexes.emplace(value, m_nextIndex); + m_indexValues.emplace(m_nextIndex, value); + return m_nextIndex; + } + static std::string dequote(const std::string& text) VL_PURE { + // Quote any special characters + std::string rtn; + for (const char* pos = text.c_str(); *pos; ++pos) { + if (!std::isprint(*pos) || *pos == '%' || *pos == '"') { + constexpr size_t LEN_MAX_HEX = 20; + char hex[LEN_MAX_HEX]; + VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]); + rtn += hex; + } else { + rtn += *pos; + } + } + return rtn; + } + static bool legalKey(const std::string& key) VL_PURE { + // Because we compress long keys to a single letter, and + // don't want applications to either get confused if they use + // a letter differently, nor want them to rely on our compression... + // (Considered using numeric keys, but will remain back compatible.) + if (key.length() < 2) return false; + if (key.length() == 2 && std::isdigit(key[1])) return false; + return true; + } + static std::string keyValueFormatter(const std::string& key, + const std::string& value) VL_PURE { + std::string name; + if (key.length() == 1 && std::isalpha(key[0])) { + name += "\001"s + key; + } else { + name += "\001"s + dequote(key); + } + name += "\002"s + dequote(value); + return name; + } + static std::string combineHier(const std::string& old, const std::string& add) VL_PURE { + // (foo.a.x, foo.b.x) => foo.*.x + // (foo.a.x, foo.b.y) => foo.* + // (foo.a.x, foo.b) => foo.* + if (old == add) return add; + if (old.empty()) return add; + if (add.empty()) return old; + + const char* const a = old.c_str(); + const char* const b = add.c_str(); + + // Scan forward to first mismatch + const char* apre = a; + const char* bpre = b; + while (*apre == *bpre) { + ++apre; + ++bpre; + } + + // We used to backup and split on only .'s but it seems better to be verbose + // and not assume . is the separator + const size_t prefix_len = apre - a; + const std::string prefix = std::string{a, prefix_len}; + + // Scan backward to last mismatch + const char* apost = a + std::strlen(a) - 1; + const char* bpost = b + std::strlen(b) - 1; + while (*apost == *bpost && apost > apre && bpost > bpre) { + --apost; + --bpost; + } + + // Forward to . so we have a whole word + const std::string suffix = *bpost ? std::string{bpost + 1} : ""; + + std::string result = prefix + "*" + suffix; + + // std::cout << "\nch pre=" << prefix << " s=" << suffix << "\nch a=" + // << old << "\nch b=" << add << "\ncho=" << result << "\n"; + return result; + } + bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match) + VL_REQUIRES(m_mutex) { + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { + if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) { + // We don't compare keys, only values + const std::string val = m_indexValues[itemp->m_vals[i]]; + if (std::string::npos != val.find(match)) { // Found + return true; + } + } + } + return false; + } + static void selftest() VL_MT_SAFE { + // Little selftest +#define SELF_CHECK(got, exp) \ + do { \ + if ((got) != (exp)) VL_FATAL_MT(__FILE__, __LINE__, "", "selftest"); \ + } while (0) + SELF_CHECK(combineHier("a.b.c", "a.b.c"), "a.b.c"); + SELF_CHECK(combineHier("a.b.c", "a.b"), "a.b*"); + SELF_CHECK(combineHier("a.x.c", "a.y.c"), "a.*.c"); + SELF_CHECK(combineHier("a.z.z.z.c", "a.b.c"), "a.*.c"); + SELF_CHECK(combineHier("z", "a"), "*"); + SELF_CHECK(combineHier("q.a", "q.b"), "q.*"); + SELF_CHECK(combineHier("q.za", "q.zb"), "q.z*"); + SELF_CHECK(combineHier("1.2.3.a", "9.8.7.a"), "*.a"); +#undef SELF_CHECK + } + void clearGuts() VL_REQUIRES(m_mutex) { + for (const auto& itemp : m_items) VL_DO_DANGLING(delete itemp, itemp); + m_items.clear(); + m_indexValues.clear(); + m_valueIndexes.clear(); + m_nextIndex = VerilatedCovConst::KEY_UNDEF + 1; + } + +public: + // PUBLIC METHODS + // cppcheck-suppress duplInheritedMember + std::string defaultFilename() VL_MT_SAFE { return m_contextp->coverageFilename(); } + // cppcheck-suppress duplInheritedMember + void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) { + Verilated::quiesce(); + const VerilatedLockGuard lock{m_mutex}; + m_forcePerInstance = flag; + } + // cppcheck-suppress duplInheritedMember + void clear() VL_MT_SAFE_EXCLUDES(m_mutex) { + Verilated::quiesce(); + const VerilatedLockGuard lock{m_mutex}; + clearGuts(); + } + // cppcheck-suppress duplInheritedMember + void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) { + Verilated::quiesce(); + const VerilatedLockGuard lock{m_mutex}; + if (matchp && matchp[0]) { + ItemList newlist; + for (const auto& itemp : m_items) { + if (!itemMatchesString(itemp, matchp)) { + VL_DO_DANGLING(delete itemp, itemp); + } else { + newlist.push_back(itemp); + } + } + m_items = newlist; + } + } + // cppcheck-suppress duplInheritedMember + void zero() VL_MT_SAFE_EXCLUDES(m_mutex) { + Verilated::quiesce(); + const VerilatedLockGuard lock{m_mutex}; + for (const VerilatedCovImpItem* const itemp : m_items) itemp->zero(); + } + + // We assume there's always call to i/f/p in that order + void inserti(VerilatedCovImpItem* itemp) VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + assert(!m_insertp); + m_insertp = itemp; + } + void insertf(const char* const filenamep, const int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + m_insertFilenamep = filenamep; + m_insertLineno = lineno; + } + void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS], + const char* valps[VerilatedCovConst::MAX_KEYS]) VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + assert(m_insertp); + // First two key/vals are filename + ckeyps[0] = "filename"; + valps[0] = m_insertFilenamep; + const std::string linestr = std::to_string(m_insertLineno); + ckeyps[1] = "lineno"; + // cppcheck-suppress autoVariables // Used only below for insert + valps[1] = linestr.c_str(); + // Default page if not specified + const char* fnstartp = m_insertFilenamep; + while (const char* foundp = std::strchr(fnstartp, '/')) fnstartp = foundp + 1; + const char* fnendp = fnstartp; + for (; *fnendp && *fnendp != '.'; ++fnendp) {} + const size_t page_len = fnendp - fnstartp; + const std::string page_default = "sp_user/" + std::string{fnstartp, page_len}; + ckeyps[2] = "page"; + // cppcheck-suppress autoVariables // Used only below for insert + valps[2] = page_default.c_str(); + + // Keys -> strings + std::array keys; + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { + if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i]; + } + // Ignore empty keys + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { + if (!keys[i].empty()) { + for (int j = i + 1; j < VerilatedCovConst::MAX_KEYS; ++j) { + if (keys[i] == keys[j]) { // Duplicate key. Keep the last one + keys[i] = ""; + break; + } + } + } + } + // Insert the values + int addKeynum = 0; + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { + const std::string key = keys[i]; + if (!keys[i].empty()) { + const std::string val = valps[i]; + // std::cout << " " << __FUNCTION__ << " " << key << " = " << val << "\n"; + m_insertp->m_keys[addKeynum] = valueIndex(key); + m_insertp->m_vals[addKeynum] = valueIndex(val); + ++addKeynum; + if (VL_UNCOVERABLE(!legalKey(key))) { + const std::string msg + = ("%Error: Coverage keys of one character, or letter+digit are illegal: " + + key); // LCOV_EXCL_LINE + VL_FATAL_MT("", 0, "", msg.c_str()); + } + } + } + m_items.push_back(m_insertp); + // Prepare for next + m_insertp = nullptr; + } + + // cppcheck-suppress duplInheritedMember + void write(const std::string& filename) VL_MT_SAFE_EXCLUDES(m_mutex) { + Verilated::quiesce(); + const VerilatedLockGuard lock{m_mutex}; + selftest(); + + std::ofstream os{filename}; + if (os.fail()) { + const std::string msg = "%Error: Can't write '"s + filename + "'"; + VL_FATAL_MT("", 0, "", msg.c_str()); + return; + } + os << "# SystemC::Coverage-3\n"; + + // Build list of events; totalize if collapsing hierarchy + std::map> eventCounts; + for (const auto& itemp : m_items) { + std::string name; + std::string hier; + bool per_instance = false; + if (m_forcePerInstance) per_instance = true; + + for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) { + if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) { + const std::string key + = VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]); + const std::string val = m_indexValues[itemp->m_vals[i]]; + if (key == VL_CIK_PER_INSTANCE) { + if (val != "0") per_instance = true; + } + if (key == VL_CIK_HIER) { + hier = val; + } else { + // Print it + if (key == "page") { + const std::string type = val.substr(2, val.find('/') - 2); + name += keyValueFormatter(VL_CIK_TYPE, type); + } + name += keyValueFormatter(key, val); + } + } + } + if (per_instance) { // Not collapsing hierarchies + name += keyValueFormatter(VL_CIK_HIER, hier); + hier = ""; + } + + // Group versus point labels don't matter here, downstream + // deals with it. Seems bad for sizing though and doesn't + // allow easy addition of new group codes (would be + // inefficient) + + // Find or insert the named event + const auto cit = eventCounts.find(name); + if (cit != eventCounts.end()) { + const std::string& oldhier = cit->second.first; + cit->second.second += itemp->count(); + cit->second.first = combineHier(oldhier, hier); + } else { + eventCounts.emplace(name, std::make_pair(hier, itemp->count())); + } + } + + // Output body + for (const auto& i : eventCounts) { + os << "C '" << std::dec; + os << i.first; + if (!i.second.first.empty()) os << keyValueFormatter(VL_CIK_HIER, i.second.first); + os << "' " << i.second.second; + os << '\n'; + } + } +}; + +//============================================================================= +// VerilatedCovContext + +std::string VerilatedCovContext::defaultFilename() VL_MT_SAFE { return impp()->defaultFilename(); } +void VerilatedCovContext::forcePerInstance(bool flag) VL_MT_SAFE { + impp()->forcePerInstance(flag); +} +void VerilatedCovContext::clear() VL_MT_SAFE { impp()->clear(); } +void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE { + impp()->clearNonMatch(matchp); +} +void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); } +void VerilatedCovContext::write(const std::string& filename) VL_MT_SAFE { + impp()->write(filename); +} +void VerilatedCovContext::_inserti(uint32_t* itemp) VL_MT_SAFE { + impp()->inserti(new VerilatedCoverItemSpec{itemp}); +} +void VerilatedCovContext::_inserti(uint64_t* itemp) VL_MT_SAFE { + impp()->inserti(new VerilatedCoverItemSpec{itemp}); +} +void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE { + impp()->insertf(filename, lineno); +} + +#ifndef DOXYGEN +#define K(n) const char* key##n +#define A(n) const char *key##n, const char *valp##n // Argument list +#define C(n) key##n, valp##n // Calling argument list +#define N(n) "", "" // Null argument list +void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), + A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18), + A(19), A(20), A(21), A(22), A(23), A(24), A(25), A(26), A(27), + A(28), A(29)) VL_MT_SAFE { + const char* keyps[VerilatedCovConst::MAX_KEYS] + = {nullptr, nullptr, nullptr, // filename,lineno,page + key0, key1, key2, key3, key4, key5, key6, key7, key8, key9, + key10, key11, key12, key13, key14, key15, key16, key17, key18, key19, + key20, key21, key22, key23, key24, key25, key26, key27, key28, key29}; + const char* valps[VerilatedCovConst::MAX_KEYS] + = {nullptr, nullptr, nullptr, // filename,lineno,page + valp0, valp1, valp2, valp3, valp4, valp5, valp6, valp7, valp8, valp9, + valp10, valp11, valp12, valp13, valp14, valp15, valp16, valp17, valp18, valp19, + valp20, valp21, valp22, valp23, valp24, valp25, valp26, valp27, valp28, valp29}; + impp()->insertp(keyps, valps); +} + +// And versions with fewer arguments (oh for a language with named parameters!) +void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), + A(9)) VL_MT_SAFE { + _insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), N(10), N(11), N(12), + N(13), N(14), N(15), N(16), N(17), N(18), N(19), N(20), N(21), N(22), N(23), N(24), + N(25), N(26), N(27), N(28), N(29)); +} +void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), + A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18), + A(19)) VL_MT_SAFE { + _insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), C(10), C(11), C(12), + C(13), C(14), C(15), C(16), C(17), C(18), C(19), N(20), N(21), N(22), N(23), N(24), + N(25), N(26), N(27), N(28), N(29)); +} +// Backward compatibility for Verilator +void VerilatedCovContext::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), + const std::string& val4, A(5), A(6), A(7)) VL_MT_SAFE { + const std::string val2str = std::to_string(val2); + const std::string val3str = std::to_string(val3); + _insertp(C(0), C(1), key2, val2str.c_str(), key3, val3str.c_str(), key4, val4.c_str(), C(5), + C(6), C(7), N(8), N(9), N(10), N(11), N(12), N(13), N(14), N(15), N(16), N(17), N(18), + N(19), N(20), N(21), N(22), N(23), N(24), N(25), N(26), N(27), N(28), N(29)); +} +#undef A +#undef C +#undef N +#undef K + +#endif // DOXYGEN + +//============================================================================= +// VerilatedCov + +#ifndef VL_NO_LEGACY +VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE { + return Verilated::threadContextp()->coveragep(); +} +#endif + +//============================================================================= +// VerilatedContext + +VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE { + static VerilatedMutex s_mutex; + // cppcheck-suppress identicalInnerCondition + if (VL_UNLIKELY(!m_coveragep)) { + const VerilatedLockGuard lock{s_mutex}; + // cppcheck-suppress identicalInnerCondition + if (VL_LIKELY(!m_coveragep)) { // LCOV_EXCL_LINE // Not redundant, prevents race + m_coveragep.reset(new VerilatedCovImp{this}); + } + } + return reinterpret_cast(m_coveragep.get()); +} diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.h new file mode 100644 index 00000000000..a6cbd2d4ecc --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.h @@ -0,0 +1,249 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated coverage analysis support header +/// +/// This must be included in user wrapper code that wants to save coverage +/// data. +/// +/// It declares the VerilatedCovContext::write() which writes the collected +/// coverage information. +/// +//============================================================================= + +#ifndef VERILATOR_VERILATED_COV_H_ +#define VERILATOR_VERILATED_COV_H_ + +#include "verilatedos.h" + +#include "verilated.h" + +#include +#include +#include + +class VerilatedCovImp; + +//============================================================================= +/// Insert an item for coverage analysis. +/// The first argument is a pointer to the count to be dumped. +/// The remaining arguments occur in pairs: A string key, and a value. +/// The value may be a string, or another type which will be auto-converted to a string. +/// +/// Some typical keys: +/// +/// filename File the recording occurs in. Defaults to __FILE__. +/// lineno Line number the recording occurs in. Defaults to __LINE__ +/// column Column number (or occurrence# for dup file/lines). Defaults to undef. +/// hier Hierarchical name. Defaults to name() +/// type Type of coverage. Defaults to "user" +/// Other types are 'block', 'fsm', 'toggle'. +/// comment Description of the coverage event. Should be set by the user. +/// Comments for type==block: 'if', 'else', 'elsif', 'case' +/// thresh Threshold to consider fully covered. +/// If unspecified, downstream tools will determine it. +/// per_instance If non-zero don't combine all hierarchies into one count +/// +/// Example: +/// +/// uint32_t m_cases[10]; // Storage for coverage data +/// constructor() { +/// // Initialize +/// for (int i = 0; i < 10; ++i) m_cases[i] = 0; +/// // Insert +/// for (int i = 0; i < 10; ++i) +/// VL_COVER_INSERT(covp, name(), &m_cases[i], "comment", "Coverage Case", +/// "i", cvtToNumStr(i)); +/// } + +#define VL_COVER_INSERT(covcontextp, name, countp, ...) \ + do { \ + auto const ccontextp = covcontextp; \ + ccontextp->_inserti(countp); \ + ccontextp->_insertf(__FILE__, __LINE__); \ + ccontextp->_insertp("hier", name, __VA_ARGS__); \ + } while (false) + +static inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData, + const IData oldData) { + const IData chgData = newData ^ oldData; + for (int i = 0; i < width; ++i) { + *(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1; + } +} + +static inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const QData newData, + const QData oldData) { + const QData chgData = newData ^ oldData; + for (int i = 0; i < width; ++i) { + *(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1; + } +} + +static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData, + WDataInP oldData) { + for (int i = 0; i < VL_WORDS_I(width); ++i) { + const EData chgData = newData[i] ^ oldData[i]; + if (chgData) { + for (int j = 0; j < width - i * VL_EDATASIZE; ++j) { + *(covp + (i * VL_EDATASIZE + j) * 2 + ((newData[i] >> j) & 1)) + += (chgData >> j) & 1; + } + } + } +} + +static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic* covp, + const IData newData, const IData oldData) VL_MT_SAFE { + const IData chgData = newData ^ oldData; + for (int i = 0; i < width; ++i) { + if (VL_BITISSET_I(chgData, i)) { + (covp + 2 * i + ((newData >> i) & 1))->fetch_add(1, std::memory_order_relaxed); + } + } +} + +static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic* covp, + const QData newData, const QData oldData) VL_MT_SAFE { + const QData chgData = newData ^ oldData; + for (int i = 0; i < width; ++i) { + if (VL_BITISSET_Q(chgData, i)) { + (covp + 2 * i + ((newData >> i) & 1))->fetch_add(1, std::memory_order_relaxed); + } + } +} + +static inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic* covp, + WDataInP newData, WDataInP oldData) VL_MT_SAFE { + for (int i = 0; i < VL_WORDS_I(width); ++i) { + const EData chgData = newData[i] ^ oldData[i]; + if (chgData) { + for (int j = 0; j < width - i * VL_EDATASIZE; ++j) { + if (VL_BITISSET_E(chgData, j)) { + (covp + (i * VL_EDATASIZE + j) * 2 + ((newData[i] >> j) & 1)) + ->fetch_add(1, std::memory_order_relaxed); + } + } + } + } +} + +//============================================================================= +// VerilatedCov +/// Per-VerilatedContext coverage data class. +/// All public methods in this class are thread safe. +/// +/// This structure is accessed and constructed on first access via +/// VerilatedContext::coveragep() + +class VerilatedCovContext VL_NOT_FINAL : public VerilatedVirtualBase { + VL_UNCOPYABLE(VerilatedCovContext); + +public: + // METHODS + /// Return default filename, may override with +verilator+coverage+file + std::string defaultFilename() VL_MT_SAFE; + /// Make all data per_instance, overriding point's per_instance + void forcePerInstance(bool flag) VL_MT_SAFE; + /// Write all coverage data to a file + void write() VL_MT_SAFE { write(defaultFilename()); } + void write(const std::string& filename) VL_MT_SAFE; + /// Clear coverage points (and call delete on all items) + void clear() VL_MT_SAFE; + /// Clear items not matching the provided string + void clearNonMatch(const char* matchp) VL_MT_SAFE; + /// Zero coverage points + void zero() VL_MT_SAFE; + + // METHODS - public but Internal use only + + // Insert a coverage item + // We accept from 1-30 key/value pairs, all as strings. + // Call _insert1, followed by _insert2 and _insert3 + // Do not call directly; use VL_COVER_INSERT or higher level macros instead + // _insert1: Remember item pointer with count. (Not const, as may add zeroing function) + void _inserti(uint32_t* itemp) VL_MT_SAFE; + void _inserti(uint64_t* itemp) VL_MT_SAFE; + // _insert2: Set default filename and line number + void _insertf(const char* filename, int lineno) VL_MT_SAFE; + // _insert3: Set parameters + // We could have just the maximum argument version, but this compiles + // much slower (nearly 2x) than having smaller versions also. However + // there's not much more gain in having a version for each number of args. +#ifndef DOXYGEN +#define K(n) const char* key##n +#define A(n) const char *key##n, const char *valp##n // Argument list +#define D(n) const char *key##n = nullptr, const char *valp##n = nullptr // Argument list + void _insertp(D(0), D(1), D(2), D(3), D(4), D(5), D(6), D(7), D(8), D(9)) VL_MT_SAFE; + void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), D(11), D(12), + D(13), D(14), D(15), D(16), D(17), D(18), D(19)) VL_MT_SAFE; + void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), A(11), A(12), + A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), D(21), D(22), D(23), + D(24), D(25), D(26), D(27), D(28), D(29)) VL_MT_SAFE; + // Backward compatibility for Verilator + void _insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), const std::string& val4, A(5), + A(6), A(7)) VL_MT_SAFE; + +#undef K +#undef A +#undef D +#endif // DOXYGEN + +protected: + friend class VerilatedCovImp; + // CONSTRUCTORS + // Internal: Only made as part of VerilatedCovImp + VerilatedCovContext() = default; + ~VerilatedCovContext() override = default; + + // METHODS + // Internal: access to implementation class + VerilatedCovImp* impp() VL_MT_SAFE { return reinterpret_cast(this); } +}; + +//============================================================================= +// VerilatedCov +/// Coverage global class. +/// +/// Global class that accesses via current thread's context's +/// VerilatedCovContext. This class is provided only for +/// backward-compatibility, use VerilatedContext::coveragep() instead. + +#ifndef VL_NO_LEGACY +class VerilatedCov final { + VL_UNCOPYABLE(VerilatedCov); + +public: + // METHODS + /// Return default filename for the current thread + static std::string defaultFilename() VL_MT_SAFE { return threadCovp()->defaultFilename(); } + /// Write all coverage data to a file for the current thread + static void write() VL_MT_SAFE { write(defaultFilename()); } + static void write(const std::string& filename) VL_MT_SAFE { threadCovp()->write(filename); } + /// Clear coverage points (and call delete on all items) for the current thread + static void clear() VL_MT_SAFE { threadCovp()->clear(); } + /// Clear items not matching the provided string for the current thread + static void clearNonMatch(const char* matchp) VL_MT_SAFE { + threadCovp()->clearNonMatch(matchp); + } + /// Zero coverage points for the current thread + static void zero() VL_MT_SAFE { threadCovp()->zero(); } + +private: + // Current thread's coverage structure + static VerilatedCovContext* threadCovp() VL_MT_SAFE; +}; +#endif // VL_NO_LEGACY + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov_key.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov_key.h new file mode 100644 index 00000000000..cb0a2efa988 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov_key.h @@ -0,0 +1,85 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated coverage item keys internal header +/// +/// This file is not part of the Verilated public-facing API. +/// It is only for internal use by the Verilated library coverage routines. +/// +//============================================================================= + +#ifndef VERILATOR_VERILATED_COV_KEY_H_ +#define VERILATOR_VERILATED_COV_KEY_H_ + +#include "verilatedos.h" + +#include + +//============================================================================= +// Data used to edit below file, using vlcovgen + +#define VLCOVGEN_ITEM(string_parsed_by_vlcovgen) + +// clang-format off +VLCOVGEN_ITEM("'name':'column', 'short':'n', 'group':1, 'default':0, 'descr':'Column number for the item. Used to disambiguate multiple coverage points on the same line number'") +VLCOVGEN_ITEM("'name':'filename', 'short':'f', 'group':1, 'default':None, 'descr':'Filename of the item'") +VLCOVGEN_ITEM("'name':'linescov', 'short':'S', 'group':1, 'default':'', 'descr':'List of comma-separated lines covered'") +VLCOVGEN_ITEM("'name':'per_instance','short':'P', 'group':1, 'default':0, 'descr':'True if every hierarchy is independently counted; otherwise all hierarchies will be combined into a single count'") +VLCOVGEN_ITEM("'name':'thresh', 'short':'s', 'group':1, 'default':None, 'descr':'Number of hits to consider covered (aka at_least)'") +VLCOVGEN_ITEM("'name':'type', 'short':'t', 'group':1, 'default':'', 'descr':'Type of coverage (block, line, fsm, etc)'") +// Bin attributes +VLCOVGEN_ITEM("'name':'comment', 'short':'o', 'group':0, 'default':'', 'descr':'Textual description for the item'") +VLCOVGEN_ITEM("'name':'hier', 'short':'h', 'group':0, 'default':'', 'descr':'Hierarchy path name for the item'") +VLCOVGEN_ITEM("'name':'lineno', 'short':'l', 'group':0, 'default':0, 'descr':'Line number for the item'") +VLCOVGEN_ITEM("'name':'weight', 'short':'w', 'group':0, 'default':None, 'descr':'For totaling items, weight of this item'") +// clang-format on + +// VLCOVGEN_CIK_AUTO_EDIT_BEGIN +#define VL_CIK_COLUMN "n" +#define VL_CIK_COMMENT "o" +#define VL_CIK_FILENAME "f" +#define VL_CIK_HIER "h" +#define VL_CIK_LINENO "l" +#define VL_CIK_LINESCOV "S" +#define VL_CIK_PER_INSTANCE "P" +#define VL_CIK_THRESH "s" +#define VL_CIK_TYPE "t" +#define VL_CIK_WEIGHT "w" +// VLCOVGEN_CIK_AUTO_EDIT_END + +//============================================================================= +// VerilatedCovKey +// Namespace-style static class for \internal use. + +class VerilatedCovKey final { +public: + // Return the short key code for a given a long coverage key + static std::string shortKey(const std::string& key) VL_PURE { + // VLCOVGEN_SHORT_AUTO_EDIT_BEGIN + if (key == "column") return VL_CIK_COLUMN; + if (key == "comment") return VL_CIK_COMMENT; + if (key == "filename") return VL_CIK_FILENAME; + if (key == "hier") return VL_CIK_HIER; + if (key == "lineno") return VL_CIK_LINENO; + if (key == "linescov") return VL_CIK_LINESCOV; + if (key == "per_instance") return VL_CIK_PER_INSTANCE; + if (key == "thresh") return VL_CIK_THRESH; + if (key == "type") return VL_CIK_TYPE; + if (key == "weight") return VL_CIK_WEIGHT; + // VLCOVGEN_SHORT_AUTO_EDIT_END + return key; + } +}; + +#endif // guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.cpp new file mode 100644 index 00000000000..d8f75f57797 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.cpp @@ -0,0 +1,814 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2009-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//========================================================================= +/// +/// \file +/// \brief Verilated DPI implementation code +/// +/// This file must be compiled and linked against all Verilated objects +/// that use the DPI. +/// +/// Declare any DPI routine inside Verilog to add this to the Makefile for +/// the linker. +/// +/// For documentation on the exported functions (named sv*) that are +/// implemented here, refer to the IEEE DPI chapter. +/// +//========================================================================= + +#define VERILATOR_VERILATED_DPI_CPP_ + +#include "verilatedos.h" + +#include "verilated_dpi.h" + +#include "verilated_imp.h" + +// On MSVC++ we need svdpi.h to declare exports, not imports +#define DPI_PROTOTYPES +#undef XXTERN +#define XXTERN DPI_EXTERN DPI_DLLESPEC +#undef EETERN +#define EETERN DPI_EXTERN DPI_DLLESPEC + +#include "vltstd/svdpi.h" + +//====================================================================== +// Internal macros + +#define VL_SVDPI_WARN_(...) VL_PRINTF_MT(__VA_ARGS__) + +// Function requires a "context" in the import declaration +#define VL_SVDPI_CONTEXT_WARN_() \ + VL_SVDPI_WARN_("%%Warning: DPI C Function called by Verilog DPI import with missing " \ + "'context' keyword.\n") + +//====================================================================== +//====================================================================== +//====================================================================== +// DPI ROUTINES + +const char* svDpiVersion() { return "1800-2005"; } + +//====================================================================== +// Bit-select utility functions. + +svBit svGetBitselBit(const svBitVecVal* sp, int bit) { return VL_BITRSHIFT_W(sp, bit) & 1; } +svLogic svGetBitselLogic(const svLogicVecVal* sp, int bit) { + // Not VL_BITRSHIFT_W as sp is a different structure type + // Verilator doesn't support X/Z so only aval + return (((sp[VL_BITWORD_I(bit)].aval >> VL_BITBIT_I(bit)) & 1) + | (((sp[VL_BITWORD_I(bit)].bval >> VL_BITBIT_I(bit)) & 1) << 1)); +} + +void svPutBitselBit(svBitVecVal* dp, int bit, svBit s) { VL_ASSIGNBIT_WI(bit, dp, s); } +void svPutBitselLogic(svLogicVecVal* dp, int bit, svLogic s) { + // Verilator doesn't support X/Z so only aval + dp[VL_BITWORD_I(bit)].aval = ((dp[VL_BITWORD_I(bit)].aval & ~(VL_UL(1) << VL_BITBIT_I(bit))) + | ((s & 1) << VL_BITBIT_I(bit))); + dp[VL_BITWORD_I(bit)].bval = ((dp[VL_BITWORD_I(bit)].bval & ~(VL_UL(1) << VL_BITBIT_I(bit))) + | ((s & 2) >> 1 << VL_BITBIT_I(bit))); +} + +void svGetPartselBit(svBitVecVal* dp, const svBitVecVal* sp, int lsb, int width) { + // Verilator supports > 32 bit widths, which is an extension to IEEE DPI + // See also VL_SEL_WWI + const int msb = lsb + width - 1; + const int word_shift = VL_BITWORD_I(lsb); + if (VL_BITBIT_I(lsb) == 0) { + // Just a word extract + for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift]; + } else { + const int loffset = lsb & VL_SIZEBITS_I; + const int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0) + // Middle words + const int words = VL_WORDS_I(msb - lsb + 1); + for (int i = 0; i < words; ++i) { + dp[i] = sp[i + word_shift] >> loffset; + const int upperword = i + word_shift + 1; + if (upperword <= static_cast(VL_BITWORD_I(msb))) { + dp[i] |= sp[upperword] << nbitsfromlow; + } + } + } + // Clean result + dp[VL_WORDS_I(width) - 1] &= VL_MASK_I(width); +} +void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int width) { + // Verilator supports > 32 bit widths, which is an extension to IEEE DPI + const int msb = lsb + width - 1; + const int word_shift = VL_BITWORD_I(lsb); + if (VL_BITBIT_I(lsb) == 0) { + // Just a word extract + for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift]; + } else { + const int loffset = lsb & VL_SIZEBITS_I; + const int nbitsfromlow = 32 - loffset; // bits that end up in lword (know loffset!=0) + // Middle words + const int words = VL_WORDS_I(msb - lsb + 1); + for (int i = 0; i < words; ++i) { + dp[i].aval = sp[i + word_shift].aval >> loffset; + dp[i].bval = sp[i + word_shift].bval >> loffset; + const int upperword = i + word_shift + 1; + if (upperword <= static_cast(VL_BITWORD_I(msb))) { + dp[i].aval |= sp[upperword].aval << nbitsfromlow; + dp[i].bval |= sp[upperword].bval << nbitsfromlow; + } + } + } + // Clean result + dp[VL_WORDS_I(width) - 1].aval &= VL_MASK_I(width); + dp[VL_WORDS_I(width) - 1].bval &= VL_MASK_I(width); +} +void svPutPartselBit(svBitVecVal* dp, const svBitVecVal s, int lbit, int width) { + // See also _vl_insert_WI + const int hbit = lbit + width - 1; + const int hoffset = VL_BITBIT_I(hbit); + const int loffset = VL_BITBIT_I(lbit); + if (hoffset == VL_SIZEBITS_I && loffset == 0) { + // Fast and common case, word based insertion + dp[VL_BITWORD_I(lbit)] = s; + } else { + const int hword = VL_BITWORD_I(hbit); + const int lword = VL_BITWORD_I(lbit); + if (hword == lword) { // know < 32 bits because above checks it + const IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset; + dp[lword] = (dp[lword] & ~insmask) | ((s << loffset) & insmask); + } else { + const IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0; + const IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset; + const int nbitsonright = 32 - loffset; // bits that end up in lword + dp[lword] = (dp[lword] & ~linsmask) | ((s << loffset) & linsmask); + dp[hword] = (dp[hword] & ~hinsmask) | ((s >> nbitsonright) & hinsmask); + } + } +} +// cppcheck-suppress passedByValue +void svPutPartselLogic(svLogicVecVal* dp, const svLogicVecVal s, int lbit, int width) { + const int hbit = lbit + width - 1; + const int hoffset = VL_BITBIT_I(hbit); + const int loffset = VL_BITBIT_I(lbit); + if (hoffset == VL_SIZEBITS_I && loffset == 0) { + // Fast and common case, word based insertion + dp[VL_BITWORD_I(lbit)].aval = s.aval; + dp[VL_BITWORD_I(lbit)].bval = s.bval; + } else { + const int hword = VL_BITWORD_I(hbit); + const int lword = VL_BITWORD_I(lbit); + if (hword == lword) { // know < 32 bits because above checks it + const IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset; + dp[lword].aval = (dp[lword].aval & ~insmask) | ((s.aval << loffset) & insmask); + dp[lword].bval = (dp[lword].bval & ~insmask) | ((s.bval << loffset) & insmask); + } else { + const IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0; + const IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset; + const int nbitsonright = 32 - loffset; // bits that end up in lword + dp[lword].aval = (dp[lword].aval & ~linsmask) | ((s.aval << loffset) & linsmask); + dp[lword].bval = (dp[lword].bval & ~linsmask) | ((s.bval << loffset) & linsmask); + dp[hword].aval = (dp[hword].aval & ~hinsmask) | ((s.aval >> nbitsonright) & hinsmask); + dp[hword].bval = (dp[hword].bval & ~hinsmask) | ((s.bval >> nbitsonright) & hinsmask); + } + } +} + +//====================================================================== +// Open array internals + +static const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) VL_MT_SAFE { + if (VL_UNLIKELY(!h)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "%%Error: DPI svOpenArrayHandle function called with nullptr handle"); + } + const VerilatedDpiOpenVar* const varp = reinterpret_cast(h); + if (VL_UNLIKELY(!varp->magicOk())) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "%%Error: DPI svOpenArrayHandle function called with non-Verilator handle"); + } + return varp; +} + +//====================================================================== +// Open array querying functions + +int svLeft(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->left(d); } +int svRight(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->right(d); } +int svLow(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->low(d); } +int svHigh(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->high(d); } +int svIncrement(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->increment(d); } +int svSize(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->elements(d); } +int svDimensions(const svOpenArrayHandle h) { return _vl_openhandle_varp(h)->udims(); } + +// Return pointer to open array data, or nullptr if not in IEEE standard C layout +void* svGetArrayPtr(const svOpenArrayHandle h) { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h); + if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr; + return varp->datap(); +} +// Return size of open array, or 0 if not in IEEE standard C layout +int svSizeOfArray(const svOpenArrayHandle h) { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h); + if (VL_UNLIKELY(!varp->isDpiStdLayout())) return 0; + // Truncate 64 bits to int; DPI is limited to 4GB + return static_cast(varp->totalSize()); +} + +//====================================================================== +// Open array access internals + +static void* _vl_sv_adjusted_datap(const VerilatedDpiOpenVar* varp, int nargs, int indx1, + int indx2, int indx3) VL_MT_SAFE { + void* datap = varp->datap(); + if (VL_UNLIKELY(nargs != varp->udims())) { + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function called on" + " %d dimensional array using %d dimensional function.\n", + varp->udims(), nargs); + return nullptr; + } + if (nargs >= 1) { + datap = varp->datapAdjustIndex(datap, 1, indx1); + if (VL_UNLIKELY(!datap)) { + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function index 1 " + "out of bounds; %d outside [%d:%d].\n", + indx1, varp->left(1), varp->right(1)); + return nullptr; + } + } + if (nargs >= 2) { + datap = varp->datapAdjustIndex(datap, 2, indx2); + if (VL_UNLIKELY(!datap)) { + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function index 2 " + "out of bounds; %d outside [%d:%d].\n", + indx2, varp->left(2), varp->right(2)); + return nullptr; + } + } + if (nargs >= 3) { + datap = varp->datapAdjustIndex(datap, 3, indx3); + if (VL_UNLIKELY(!datap)) { + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function index 3 " + "out of bounds; %d outside [%d:%d].\n", + indx1, varp->left(3), varp->right(3)); + return nullptr; + } + } + return datap; +} + +// Return pointer to simulator open array element, or nullptr if outside range +static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h, int nargs, int indx1, int indx2, + int indx3) VL_MT_SAFE { + const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h); + if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr; + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + return datap; +} + +// Copy to user bit array from simulator open array +static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int nargs, + int indx1, int indx2, int indx3) VL_MT_SAFE { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + if (VL_UNLIKELY(!datap)) return; + switch (varp->vltype()) { // LCOV_EXCL_BR_LINE + case VLVT_UINT8: d[0] = *(reinterpret_cast(datap)); return; + case VLVT_UINT16: d[0] = *(reinterpret_cast(datap)); return; + case VLVT_UINT32: d[0] = *(reinterpret_cast(datap)); return; + case VLVT_UINT64: { + VlWide<2> lwp; + VL_SET_WQ(lwp, *(reinterpret_cast(datap))); + d[0] = lwp[0]; + d[1] = lwp[1]; + break; + } + case VLVT_WDATA: { + WDataInP wdatap = (reinterpret_cast(datap)); + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) d[i] = wdatap[i]; + return; + } + default: // LCOV_EXCL_START // Errored earlier + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + varp->vltype()); + return; // LCOV_EXCL_STOP + } +} +// Copy to user logic array from simulator open array +static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int nargs, + int indx1, int indx2, int indx3) VL_MT_SAFE { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + if (VL_UNLIKELY(!datap)) return; + switch (varp->vltype()) { // LCOV_EXCL_BR_LINE + case VLVT_UINT8: + d[0].aval = *(reinterpret_cast(datap)); + d[0].bval = 0; + return; + case VLVT_UINT16: + d[0].aval = *(reinterpret_cast(datap)); + d[0].bval = 0; + return; + case VLVT_UINT32: + d[0].aval = *(reinterpret_cast(datap)); + d[0].bval = 0; + return; + case VLVT_UINT64: { + VlWide<2> lwp; + VL_SET_WQ(lwp, *(reinterpret_cast(datap))); + d[0].aval = lwp[0]; + d[0].bval = 0; + d[1].aval = lwp[1]; + d[1].bval = 0; + break; + } + case VLVT_WDATA: { + WDataInP wdatap = (reinterpret_cast(datap)); + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) { + d[i].aval = wdatap[i]; + d[i].bval = 0; + } + return; + } + default: // LCOV_EXCL_START // Errored earlier + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + varp->vltype()); + return; // LCOV_EXCL_STOP + } +} + +// Copy to simulator open array from from user bit array +static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int nargs, + int indx1, int indx2, int indx3) VL_MT_SAFE { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + if (VL_UNLIKELY(!datap)) return; + switch (varp->vltype()) { // LCOV_EXCL_BR_LINE + case VLVT_UINT8: *(reinterpret_cast(datap)) = s[0]; return; + case VLVT_UINT16: *(reinterpret_cast(datap)) = s[0]; return; + case VLVT_UINT32: *(reinterpret_cast(datap)) = s[0]; return; + case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1], s[0]); break; + case VLVT_WDATA: { + WDataOutP wdatap = (reinterpret_cast(datap)); + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i]; + return; + } + default: // LCOV_EXCL_START // Errored earlier + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + varp->vltype()); + return; // LCOV_EXCL_STOP + } +} +// Copy to simulator open array from from user logic array +static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, + int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + if (VL_UNLIKELY(!datap)) return; + switch (varp->vltype()) { // LCOV_EXCL_BR_LINE + case VLVT_UINT8: *(reinterpret_cast(datap)) = s[0].aval; return; + case VLVT_UINT16: *(reinterpret_cast(datap)) = s[0].aval; return; + case VLVT_UINT32: *(reinterpret_cast(datap)) = s[0].aval; return; + case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1].aval, s[0].aval); break; + case VLVT_WDATA: { + WDataOutP wdatap = (reinterpret_cast(datap)); + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i].aval; + return; + } + default: // LCOV_EXCL_START // Errored earlier + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + varp->vltype()); + return; // LCOV_EXCL_STOP + } +} + +// Return bit from simulator open array +static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1, int indx2, + int indx3, int) VL_MT_SAFE { + // One extra index supported, as need bit number + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + if (VL_UNLIKELY(!datap)) return 0; + switch (varp->vltype()) { // LCOV_EXCL_BR_LINE + case VLVT_UINT8: return (*(reinterpret_cast(datap))) & 1; + default: // LCOV_EXCL_START // Errored earlier + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + varp->vltype()); + return 0; // LCOV_EXCL_STOP + } +} +// Update simulator open array from bit +static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int nargs, int indx1, + int indx2, int indx3, int) VL_MT_SAFE { + // One extra index supported, as need bit number + value &= 1; // Make sure clean + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3); + if (VL_UNLIKELY(!datap)) return; + switch (varp->vltype()) { // LCOV_EXCL_BR_LINE + case VLVT_UINT8: *(reinterpret_cast(datap)) = value; return; + default: // LCOV_EXCL_START // Errored earlier + VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n", + varp->vltype()); + return; // LCOV_EXCL_STOP + } +} + +//====================================================================== +// DPI accessors that call above functions + +void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h); + void* datap; + va_list ap; + va_start(ap, indx1); + // va_arg is a macro, so need temporaries as used below + switch (varp->udims()) { + case 1: datap = _vl_svGetArrElemPtr(h, 1, indx1, 0, 0); break; + case 2: { + const int indx2 = va_arg(ap, int); + datap = _vl_svGetArrElemPtr(h, 2, indx1, indx2, 0); + break; + } + case 3: { + const int indx2 = va_arg(ap, int); + const int indx3 = va_arg(ap, int); + datap = _vl_svGetArrElemPtr(h, 3, indx1, indx2, indx3); + break; + } + default: datap = _vl_svGetArrElemPtr(h, -1, 0, 0, 0); break; // Will error + } + va_end(ap); + return datap; +} +void* svGetArrElemPtr1(const svOpenArrayHandle h, int indx1) { + return _vl_svGetArrElemPtr(h, 1, indx1, 0, 0); +} +void* svGetArrElemPtr2(const svOpenArrayHandle h, int indx1, int indx2) { + return _vl_svGetArrElemPtr(h, 2, indx1, indx2, 0); +} +void* svGetArrElemPtr3(const svOpenArrayHandle h, int indx1, int indx2, int indx3) { + return _vl_svGetArrElemPtr(h, 3, indx1, indx2, indx3); +} + +void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, ...) { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + va_list ap; + va_start(ap, indx1); + switch (varp->udims()) { + case 1: _vl_svPutBitArrElemVecVal(d, s, 1, indx1, 0, 0); break; + case 2: { + const int indx2 = va_arg(ap, int); + _vl_svPutBitArrElemVecVal(d, s, 2, indx1, indx2, 0); + break; + } + case 3: { + const int indx2 = va_arg(ap, int); + const int indx3 = va_arg(ap, int); + _vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3); + break; + } + default: _vl_svPutBitArrElemVecVal(d, s, -1, 0, 0, 0); break; // Will error + } + va_end(ap); +} +void svPutBitArrElem1VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1) { + _vl_svPutBitArrElemVecVal(d, s, 1, indx1, 0, 0); +} +void svPutBitArrElem2VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, + int indx2) { + _vl_svPutBitArrElemVecVal(d, s, 2, indx1, indx2, 0); +} +void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, int indx2, + int indx3) { + _vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3); +} +void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, ...) { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + va_list ap; + va_start(ap, indx1); + switch (varp->udims()) { + case 1: _vl_svPutLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break; + case 2: { + const int indx2 = va_arg(ap, int); + _vl_svPutLogicArrElemVecVal(d, s, 2, indx1, indx2, 0); + break; + } + case 3: { + const int indx2 = va_arg(ap, int); + const int indx3 = va_arg(ap, int); + _vl_svPutLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3); + break; + } + default: _vl_svPutLogicArrElemVecVal(d, s, -1, 0, 0, 0); break; // Will error + } + va_end(ap); +} +void svPutLogicArrElem1VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1) { + _vl_svPutLogicArrElemVecVal(d, s, 1, indx1, 0, 0); +} +void svPutLogicArrElem2VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, + int indx2) { + _vl_svPutLogicArrElemVecVal(d, s, 2, indx1, indx2, 0); +} +void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, + int indx2, int indx3) { + _vl_svPutLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3); +} + +//====================================================================== +// From simulator storage into user space + +void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, ...) { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + va_list ap; + va_start(ap, indx1); + switch (varp->udims()) { + case 1: _vl_svGetBitArrElemVecVal(d, s, 1, indx1, 0, 0); break; + case 2: { + const int indx2 = va_arg(ap, int); + _vl_svGetBitArrElemVecVal(d, s, 2, indx1, indx2, 0); + break; + } + case 3: { + const int indx2 = va_arg(ap, int); + const int indx3 = va_arg(ap, int); + _vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3); + break; + } + default: _vl_svGetBitArrElemVecVal(d, s, -1, 0, 0, 0); break; // Will error + } + va_end(ap); +} +void svGetBitArrElem1VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1) { + _vl_svGetBitArrElemVecVal(d, s, 1, indx1, 0, 0); +} +void svGetBitArrElem2VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, int indx2) { + _vl_svGetBitArrElemVecVal(d, s, 2, indx1, indx2, 0); +} +void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, int indx2, + int indx3) { + _vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3); +} +void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, ...) { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + va_list ap; + va_start(ap, indx1); + switch (varp->udims()) { + case 1: _vl_svGetLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break; + case 2: { + const int indx2 = va_arg(ap, int); + _vl_svGetLogicArrElemVecVal(d, s, 2, indx1, indx2, 0); + break; + } + case 3: { + const int indx2 = va_arg(ap, int); + const int indx3 = va_arg(ap, int); + _vl_svGetLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3); + break; + } + default: _vl_svGetLogicArrElemVecVal(d, s, -1, 0, 0, 0); break; // Will error + } + va_end(ap); +} +void svGetLogicArrElem1VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1) { + _vl_svGetLogicArrElemVecVal(d, s, 1, indx1, 0, 0); +} +void svGetLogicArrElem2VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, int indx2) { + _vl_svGetLogicArrElemVecVal(d, s, 2, indx1, indx2, 0); +} +void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, int indx2, + int indx3) { + _vl_svGetLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3); +} + +svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + svBit out; + va_list ap; + va_start(ap, indx1); + switch (varp->udims()) { + case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break; + case 2: { + const int indx2 = va_arg(ap, int); + out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0); + break; + } + case 3: { + const int indx2 = va_arg(ap, int); + const int indx3 = va_arg(ap, int); + out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0); + break; + } + default: out = _vl_svGetBitArrElem(s, -1, 0, 0, 0, 0); break; // Will error + } + va_end(ap); + return out; +} +svBit svGetBitArrElem1(const svOpenArrayHandle s, int indx1) { + return _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); +} +svBit svGetBitArrElem2(const svOpenArrayHandle s, int indx1, int indx2) { + return _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0); +} +svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx3) { + return _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0); +} +svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) { + // Verilator doesn't support X/Z so can just call Bit version + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s); + svBit out; + va_list ap; + va_start(ap, indx1); + switch (varp->udims()) { + case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break; + case 2: { + const int indx2 = va_arg(ap, int); + out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0); + break; + } + case 3: { + const int indx2 = va_arg(ap, int); + const int indx3 = va_arg(ap, int); + out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0); + break; + } + default: out = _vl_svGetBitArrElem(s, -1, 0, 0, 0, 0); break; // Will error + } + va_end(ap); + return out; +} +svLogic svGetLogicArrElem1(const svOpenArrayHandle s, int indx1) { + // Verilator doesn't support X/Z so can just call Bit version + return svGetBitArrElem1(s, indx1); +} +svLogic svGetLogicArrElem2(const svOpenArrayHandle s, int indx1, int indx2) { + // Verilator doesn't support X/Z so can just call Bit version + return svGetBitArrElem2(s, indx1, indx2); +} +svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx3) { + // Verilator doesn't support X/Z so can just call Bit version + return svGetBitArrElem3(s, indx1, indx2, indx3); +} + +void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) { + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + va_list ap; + va_start(ap, indx1); + switch (varp->udims()) { + case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break; + case 2: { + const int indx2 = va_arg(ap, int); + _vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0); + break; + } + case 3: { + const int indx2 = va_arg(ap, int); + const int indx3 = va_arg(ap, int); + _vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0); + break; + } + default: _vl_svPutBitArrElem(d, value, -1, 0, 0, 0, 0); break; // Will error + } + va_end(ap); +} +void svPutBitArrElem1(const svOpenArrayHandle d, svBit value, int indx1) { + _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); +} +void svPutBitArrElem2(const svOpenArrayHandle d, svBit value, int indx1, int indx2) { + _vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0); +} +void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, int indx2, int indx3) { + _vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0); +} +void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...) { + // Verilator doesn't support X/Z so can just call Bit version + const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d); + va_list ap; + va_start(ap, indx1); + switch (varp->udims()) { + case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break; + case 2: { + const int indx2 = va_arg(ap, int); + _vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0); + break; + } + case 3: { + const int indx2 = va_arg(ap, int); + const int indx3 = va_arg(ap, int); + _vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0); + break; + } + default: _vl_svPutBitArrElem(d, value, -1, 0, 0, 0, 0); break; // Will error + } + va_end(ap); +} +void svPutLogicArrElem1(const svOpenArrayHandle d, svLogic value, int indx1) { + // Verilator doesn't support X/Z so can just call Bit version + svPutBitArrElem1(d, value, indx1); +} +void svPutLogicArrElem2(const svOpenArrayHandle d, svLogic value, int indx1, int indx2) { + // Verilator doesn't support X/Z so can just call Bit version + svPutBitArrElem2(d, value, indx1, indx2); +} +void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value, int indx1, int indx2, + int indx3) { + // Verilator doesn't support X/Z so can just call Bit version + svPutBitArrElem3(d, value, indx1, indx2, indx3); +} + +//====================================================================== +// Functions for working with DPI context + +svScope svGetScope() { + if (VL_UNLIKELY(!Verilated::dpiInContext())) { + VL_SVDPI_CONTEXT_WARN_(); + return nullptr; + } + // NOLINTNEXTLINE(google-readability-casting) + return (svScope)(Verilated::dpiScope()); +} + +svScope svSetScope(const svScope scope) { + const VerilatedScope* const prevScopep = Verilated::dpiScope(); + const VerilatedScope* const vscopep = reinterpret_cast(scope); + Verilated::dpiScope(vscopep); + // NOLINTNEXTLINE(google-readability-casting) + return (svScope)(prevScopep); +} + +const char* svGetNameFromScope(const svScope scope) { + const VerilatedScope* const vscopep = reinterpret_cast(scope); + return vscopep->name(); +} + +svScope svGetScopeFromName(const char* scopeName) { + // NOLINTNEXTLINE(google-readability-casting) + return (svScope)(Verilated::threadContextp()->scopeFind(scopeName)); +} + +int svPutUserData(const svScope scope, void* userKey, void* userData) { + VerilatedImp::userInsert(scope, userKey, userData); + return 0; +} + +void* svGetUserData(const svScope scope, void* userKey) { + return VerilatedImp::userFind(scope, userKey); +} + +int svGetCallerInfo(const char** fileNamepp, int* lineNumberp) { + if (VL_UNLIKELY(!Verilated::dpiInContext())) { + VL_SVDPI_CONTEXT_WARN_(); + return false; + } + if (VL_LIKELY(fileNamepp)) *fileNamepp = Verilated::dpiFilenamep(); // thread local + if (VL_LIKELY(lineNumberp)) *lineNumberp = Verilated::dpiLineno(); // thread local + return true; +} + +//====================================================================== +// Time + +int svGetTime(const svScope scope, svTimeVal* time) { + if (VL_UNLIKELY(!time)) return -1; + const QData qtime = VL_TIME_Q(); + VlWide<2> itime; + VL_SET_WQ(itime, qtime); + time->low = itime[0]; + time->high = itime[1]; + return 0; +} + +int svGetTimeUnit(const svScope scope, int32_t* time_unit) { + if (VL_UNLIKELY(!time_unit)) return -1; + const VerilatedScope* const vscopep = reinterpret_cast(scope); + if (!vscopep) { // Null asks for global, not unlikely + *time_unit = Verilated::threadContextp()->timeunit(); + } else { + *time_unit = vscopep->timeunit(); + } + return 0; +} + +int svGetTimePrecision(const svScope scope, int32_t* time_precision) { + if (VL_UNLIKELY(!time_precision)) return -1; + *time_precision = Verilated::threadContextp()->timeprecision(); + return 0; +} + +//====================================================================== +// Disables + +int svIsDisabledState() { + return 0; // Disables not implemented +} + +void svAckDisabledState() { + // Disables not implemented +} diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.h new file mode 100644 index 00000000000..cb6efff6895 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.h @@ -0,0 +1,112 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated DPI header +/// +/// This file is included automatically by Verilator at the top of all C++ +/// files it generates where DPI is used. It contains DPI interface +/// functions required by the Verilated code. +/// +/// This file is not part of the Verilated public-facing API. +/// It is only for internal use. +/// +//************************************************************************* + +#ifndef VERILATOR_VERILATED_DPI_H_ +#define VERILATOR_VERILATED_DPI_H_ + +#include "verilatedos.h" + +#include "verilated.h" // Also presumably included by caller +#include "verilated_sym_props.h" + +#include "svdpi.h" + +//=================================================================== +// SETTING OPERATORS + +// Convert svBitVecVal to Verilator internal data +static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i]; + owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits); +} +static inline void VL_SET_Q_SVBV(int obits, QData& out, const svBitVecVal* lwp) VL_MT_SAFE { + out = VL_MASK_Q(obits) & VL_SET_QII(lwp[1], lwp[0]); +} +static inline void VL_SET_I_SVBV(int obits, IData& out, const svBitVecVal* lwp) VL_MT_SAFE { + out = VL_MASK_I(obits) & lwp[0]; +} +static inline void VL_SET_S_SVBV(int obits, SData& out, const svBitVecVal* lwp) VL_MT_SAFE { + out = VL_MASK_I(obits) & lwp[0]; +} +static inline void VL_SET_C_SVBV(int obits, CData& out, const svBitVecVal* lwp) VL_MT_SAFE { + out = VL_MASK_I(obits) & lwp[0]; +} + +// Convert Verilator internal data to svBitVecVal +static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i]; + owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits); +} +static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; } +static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE { + VL_SET_WQ(owp, ld); +} + +// Convert svLogicVecVal to Verilator internal data +// Note these functions ignore X/Z in svLogicVecVal +static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval; + owp[words - 1] = lwp[words - 1].aval & VL_MASK_I(obits); +} +static inline void VL_SET_Q_SVLV(int obits, QData& out, const svLogicVecVal* lwp) VL_MT_SAFE { + out = VL_MASK_Q(obits) & VL_SET_QII(lwp[1].aval, lwp[0].aval); +} +static inline void VL_SET_I_SVLV(int obits, IData& out, const svLogicVecVal* lwp) VL_MT_SAFE { + out = VL_MASK_I(obits) & lwp[0].aval; +} +static inline void VL_SET_S_SVLV(int obits, SData& out, const svLogicVecVal* lwp) VL_MT_SAFE { + out = VL_MASK_I(obits) & lwp[0].aval; +} +static inline void VL_SET_C_SVLV(int obits, CData& out, const svLogicVecVal* lwp) VL_MT_SAFE { + out = VL_MASK_I(obits) & lwp[0].aval; +} + +// Convert Verilator internal data to svLogicVecVal +// Note these functions never create X/Z in svLogicVecVal +static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, const WDataInP lwp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + for (int i = 0; i < words; ++i) owp[i].bval = 0; + for (int i = 0; i < words - 1; ++i) owp[i].aval = lwp[i]; + owp[words - 1].aval = lwp[words - 1] & VL_MASK_I(obits); +} +static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, const IData ld) VL_MT_SAFE { + owp[0].aval = ld; + owp[0].bval = 0; +} +static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, const QData ld) VL_MT_SAFE { + VlWide<2> lwp; + VL_SET_WQ(lwp, ld); + owp[0].aval = lwp[0]; + owp[0].bval = 0; + owp[1].aval = lwp[1]; + owp[1].bval = 0; +} + +//====================================================================== + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.cpp new file mode 100644 index 00000000000..f79e7316887 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.cpp @@ -0,0 +1,405 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated C++ tracing in FST format implementation code +/// +/// This file must be compiled and linked against all Verilated objects +/// that use --trace-fst. +/// +/// Use "verilator --trace-fst" to add this to the Makefile for the linker. +/// +//============================================================================= + +// clang-format off + +#include "verilated.h" +#include "verilated_fst_c.h" + +// GTKWave configuration +#define HAVE_LIBPTHREAD +#define FST_WRITER_PARALLEL +#define LZ4_DISABLE_DEPRECATE_WARNINGS + +// Include the GTKWave implementation directly +#define FST_CONFIG_INCLUDE "fst_config.h" +#include "gtkwave/fastlz.c" +#include "gtkwave/fstapi.c" +#include "gtkwave/lz4.c" + +#include +#include +#include +#include + +#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) +# include +#else +# include +#endif + +// clang-format on + +//============================================================================= +// Check that forward declared types matches the FST API types + +static_assert(std::is_same::value, "vlFstHandle mismatch"); +static_assert(std::is_same::value, "vlFstHandle mismatch"); + +//============================================================================= +// Specialization of the generics for this trace format + +#define VL_SUB_T VerilatedFst +#define VL_BUF_T VerilatedFstBuffer +#include "verilated_trace_imp.h" +#undef VL_SUB_T +#undef VL_BUF_T + +//============================================================================= +// VerilatedFst + +VerilatedFst::VerilatedFst(void* /*fst*/) {} + +VerilatedFst::~VerilatedFst() { + if (m_fst) fstWriterClose(m_fst); + if (m_symbolp) VL_DO_CLEAR(delete[] m_symbolp, m_symbolp = nullptr); + if (m_strbufp) VL_DO_CLEAR(delete[] m_strbufp, m_strbufp = nullptr); +} + +void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + m_fst = fstWriterCreate(filename, 1); + fstWriterSetPackType(m_fst, FST_WR_PT_LZ4); + fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str()); // lintok-begin-on-ref + if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1); + constDump(true); // First dump must contain the const signals + fullDump(true); // First dump must be full for fst + + Super::traceInit(); + + // convert m_code2symbol into an array for fast lookup + if (!m_symbolp) { + m_symbolp = new fstHandle[nextCode()]{0}; + for (const auto& i : m_code2symbol) m_symbolp[i.first] = i.second; + } + m_code2symbol.clear(); + + // Allocate string buffer for arrays + if (!m_strbufp) m_strbufp = new char[maxBits() + 32]; +} + +void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + Super::closeBase(); + emitTimeChangeMaybe(); + fstWriterClose(m_fst); + m_fst = nullptr; +} + +void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + Super::flushBase(); + emitTimeChangeMaybe(); + fstWriterFlushContext(m_fst); +} + +void VerilatedFst::emitTimeChange(uint64_t timeui) { + if (!timeui) fstWriterEmitTimeChange(m_fst, timeui); + m_timeui = timeui; +} + +VL_ATTR_ALWINLINE +void VerilatedFst::emitTimeChangeMaybe() { + if (VL_UNLIKELY(m_timeui)) { + fstWriterEmitTimeChange(m_fst, m_timeui); + m_timeui = 0; + } +} + +//============================================================================= +// Decl + +void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, uint32_t elements, + unsigned int minValbits, const char** itemNamesp, + const char** itemValuesp) { + const fstEnumHandle enumNum + = fstWriterCreateEnumTable(m_fst, name, elements, minValbits, itemNamesp, itemValuesp); + const bool newEntry = m_local2fstdtype[initUserp()].emplace(dtypenum, enumNum).second; + assert(newEntry); +} + +// TODO: should return std::optional, but I can't have C++17 +static std::pair toFstScopeType(VerilatedTracePrefixType type) { + switch (type) { + case VerilatedTracePrefixType::SCOPE_MODULE: return {true, FST_ST_VCD_MODULE}; + case VerilatedTracePrefixType::SCOPE_INTERFACE: return {true, FST_ST_VCD_INTERFACE}; + case VerilatedTracePrefixType::STRUCT_PACKED: + case VerilatedTracePrefixType::STRUCT_UNPACKED: return {true, FST_ST_VCD_STRUCT}; + case VerilatedTracePrefixType::UNION_PACKED: return {true, FST_ST_VCD_UNION}; + default: return {false, /* unused so whatever, just need a value */ FST_ST_VCD_SCOPE}; + } +} + +void VerilatedFst::pushPrefix(const char* namep, VerilatedTracePrefixType type) { + assert(!m_prefixStack.empty()); // Constructor makes an empty entry + const std::string name{namep}; + // An empty name means this is the root of a model created with + // name()=="". The tools get upset if we try to pass this as empty, so + // we put the signals under a new $rootio scope, but the signals + // further down will be peers, not children (as usual for name()!=""). + const std::string prevPrefix = m_prefixStack.back().first; + if (name == "$rootio" && !prevPrefix.empty()) { + // Upper has name, we can suppress inserting $rootio, but still push so popPrefix works + m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER); + return; + } else if (name.empty()) { + m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER); + return; + } + + // This code assumes a signal at a given prefix level is declared before + // any pushPrefix are done at that same level. + const std::string newPrefix = prevPrefix + name; + const auto pair = toFstScopeType(type); + const bool properScope = pair.first; + const fstScopeType scopeType = pair.second; + m_prefixStack.emplace_back(newPrefix + (properScope ? " " : ""), type); + if (properScope) { + const std::string scopeName = lastWord(newPrefix); + fstWriterSetScope(m_fst, scopeType, scopeName.c_str(), nullptr); + } +} + +void VerilatedFst::popPrefix() { + assert(!m_prefixStack.empty()); + const bool properScope = toFstScopeType(m_prefixStack.back().second).first; + if (properScope) fstWriterSetUpscope(m_fst); + m_prefixStack.pop_back(); + assert(!m_prefixStack.empty()); // Always one left, the constructor's initial one +} + +void VerilatedFst::declare(uint32_t code, const char* name, int dtypenum, + VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind, + VerilatedTraceSigType type, bool array, int arraynum, bool bussed, + int msb, int lsb) { + const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1; + + const std::string hierarchicalName = m_prefixStack.back().first + name; + + const bool enabled = Super::declCode(code, hierarchicalName, bits); + if (!enabled) return; + + assert(hierarchicalName.rfind(' ') != std::string::npos); + std::stringstream name_ss; + name_ss << lastWord(hierarchicalName); + if (array) name_ss << "[" << arraynum << "]"; + if (bussed) name_ss << " [" << msb << ":" << lsb << "]"; + const std::string name_str = name_ss.str(); + + if (dtypenum > 0) { + fstWriterEmitEnumTableRef(m_fst, m_local2fstdtype.at(initUserp()).at(dtypenum)); + } + + fstVarDir varDir = FST_VD_IMPLICIT; + switch (direction) { + case VerilatedTraceSigDirection::INOUT: varDir = FST_VD_INOUT; break; + case VerilatedTraceSigDirection::OUTPUT: varDir = FST_VD_OUTPUT; break; + case VerilatedTraceSigDirection::INPUT: varDir = FST_VD_INPUT; break; + case VerilatedTraceSigDirection::NONE: varDir = FST_VD_IMPLICIT; break; + } + + fstVarType varType; + // Doubles have special decoding properties, so must indicate if a double + if (type == VerilatedTraceSigType::DOUBLE) { + if (kind == VerilatedTraceSigKind::PARAMETER) { + varType = FST_VT_VCD_REAL_PARAMETER; + } else { + varType = FST_VT_VCD_REAL; + } + } + // clang-format off + else if (kind == VerilatedTraceSigKind::PARAMETER) varType = FST_VT_VCD_PARAMETER; + else if (kind == VerilatedTraceSigKind::SUPPLY0) varType = FST_VT_VCD_SUPPLY0; + else if (kind == VerilatedTraceSigKind::SUPPLY1) varType = FST_VT_VCD_SUPPLY1; + else if (kind == VerilatedTraceSigKind::TRI) varType = FST_VT_VCD_TRI; + else if (kind == VerilatedTraceSigKind::TRI0) varType = FST_VT_VCD_TRI0; + else if (kind == VerilatedTraceSigKind::TRI1) varType = FST_VT_VCD_TRI1; + else if (kind == VerilatedTraceSigKind::TRIAND) varType = FST_VT_VCD_TRIAND; + else if (kind == VerilatedTraceSigKind::TRIOR) varType = FST_VT_VCD_TRIOR; + else if (kind == VerilatedTraceSigKind::TRIREG) varType = FST_VT_VCD_TRIREG; + else if (kind == VerilatedTraceSigKind::WIRE) varType = FST_VT_VCD_WIRE; + // + else if (type == VerilatedTraceSigType::INTEGER) varType = FST_VT_VCD_INTEGER; + else if (type == VerilatedTraceSigType::BIT) varType = FST_VT_SV_BIT; + else if (type == VerilatedTraceSigType::LOGIC) varType = FST_VT_SV_LOGIC; + else if (type == VerilatedTraceSigType::INT) varType = FST_VT_SV_INT; + else if (type == VerilatedTraceSigType::SHORTINT) varType = FST_VT_SV_SHORTINT; + else if (type == VerilatedTraceSigType::LONGINT) varType = FST_VT_SV_LONGINT; + else if (type == VerilatedTraceSigType::BYTE) varType = FST_VT_SV_BYTE; + else if (type == VerilatedTraceSigType::EVENT) varType = FST_VT_VCD_EVENT; + else if (type == VerilatedTraceSigType::TIME) varType = FST_VT_VCD_TIME; + else { assert(0); /* Unreachable */ } + // clang-format on + + const auto it = vlstd::as_const(m_code2symbol).find(code); + if (it == m_code2symbol.end()) { // New + m_code2symbol[code] + = fstWriterCreateVar(m_fst, varType, varDir, bits, name_str.c_str(), 0); + } else { // Alias + fstWriterCreateVar(m_fst, varType, varDir, bits, name_str.c_str(), it->second); + } +} + +void VerilatedFst::declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind, + VerilatedTraceSigType type, bool array, int arraynum) { + declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 0, 0); +} +void VerilatedFst::declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind, + VerilatedTraceSigType type, bool array, int arraynum) { + declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 0, 0); +} +void VerilatedFst::declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind, + VerilatedTraceSigType type, bool array, int arraynum, int msb, + int lsb) { + declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb); +} +void VerilatedFst::declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind, + VerilatedTraceSigType type, bool array, int arraynum, int msb, + int lsb) { + declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb); +} +void VerilatedFst::declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind, + VerilatedTraceSigType type, bool array, int arraynum, int msb, + int lsb) { + declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb); +} +void VerilatedFst::declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind, + VerilatedTraceSigType type, bool array, int arraynum) { + declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 63, 0); +} + +//============================================================================= +// Get/commit trace buffer + +VerilatedFst::Buffer* VerilatedFst::getTraceBuffer(uint32_t fidx) { + if (offload()) return new OffloadBuffer{*this}; + return new Buffer{*this}; +} + +void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) { + if (offload()) { + const OffloadBuffer* const offloadBufferp = static_cast(bufp); + if (offloadBufferp->m_offloadBufferWritep) { + m_offloadBufferWritep = offloadBufferp->m_offloadBufferWritep; + return; // Buffer will be deleted by the offload thread + } + } + delete bufp; +} + +//============================================================================= +// Configure + +void VerilatedFst::configure(const VerilatedTraceConfig& config) { + // If at least one model requests the FST writer thread, then use it + m_useFstWriterThread |= config.m_useFstWriterThread; +} + +//============================================================================= +// VerilatedFstBuffer implementation + +//============================================================================= +// Trace rendering primitives + +// Note: emit* are only ever called from one place (full* in +// verilated_trace_imp.h, which is included in this file at the top), +// so always inline them. + +VL_ATTR_ALWINLINE +void VerilatedFstBuffer::emitEvent(uint32_t code) { + VL_DEBUG_IFDEF(assert(m_symbolp[code]);); + m_owner.emitTimeChangeMaybe(); + fstWriterEmitValueChange(m_fst, m_symbolp[code], "1"); +} + +VL_ATTR_ALWINLINE +void VerilatedFstBuffer::emitBit(uint32_t code, CData newval) { + VL_DEBUG_IFDEF(assert(m_symbolp[code]);); + m_owner.emitTimeChangeMaybe(); + fstWriterEmitValueChange(m_fst, m_symbolp[code], newval ? "1" : "0"); +} + +VL_ATTR_ALWINLINE +void VerilatedFstBuffer::emitCData(uint32_t code, CData newval, int bits) { + char buf[VL_BYTESIZE]; + VL_DEBUG_IFDEF(assert(m_symbolp[code]);); + cvtCDataToStr(buf, newval << (VL_BYTESIZE - bits)); + m_owner.emitTimeChangeMaybe(); + fstWriterEmitValueChange(m_fst, m_symbolp[code], buf); +} + +VL_ATTR_ALWINLINE +void VerilatedFstBuffer::emitSData(uint32_t code, SData newval, int bits) { + char buf[VL_SHORTSIZE]; + VL_DEBUG_IFDEF(assert(m_symbolp[code]);); + cvtSDataToStr(buf, newval << (VL_SHORTSIZE - bits)); + m_owner.emitTimeChangeMaybe(); + fstWriterEmitValueChange(m_fst, m_symbolp[code], buf); +} + +VL_ATTR_ALWINLINE +void VerilatedFstBuffer::emitIData(uint32_t code, IData newval, int bits) { + char buf[VL_IDATASIZE]; + VL_DEBUG_IFDEF(assert(m_symbolp[code]);); + cvtIDataToStr(buf, newval << (VL_IDATASIZE - bits)); + m_owner.emitTimeChangeMaybe(); + fstWriterEmitValueChange(m_fst, m_symbolp[code], buf); +} + +VL_ATTR_ALWINLINE +void VerilatedFstBuffer::emitQData(uint32_t code, QData newval, int bits) { + char buf[VL_QUADSIZE]; + VL_DEBUG_IFDEF(assert(m_symbolp[code]);); + cvtQDataToStr(buf, newval << (VL_QUADSIZE - bits)); + m_owner.emitTimeChangeMaybe(); + fstWriterEmitValueChange(m_fst, m_symbolp[code], buf); +} + +VL_ATTR_ALWINLINE +void VerilatedFstBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) { + int words = VL_WORDS_I(bits); + char* wp = m_strbufp; + // Convert the most significant word + const int bitsInMSW = VL_BITBIT_E(bits) ? VL_BITBIT_E(bits) : VL_EDATASIZE; + cvtEDataToStr(wp, newvalp[--words] << (VL_EDATASIZE - bitsInMSW)); + wp += bitsInMSW; + // Convert the remaining words + while (words > 0) { + cvtEDataToStr(wp, newvalp[--words]); + wp += VL_EDATASIZE; + } + m_owner.emitTimeChangeMaybe(); + fstWriterEmitValueChange(m_fst, m_symbolp[code], m_strbufp); +} + +VL_ATTR_ALWINLINE +void VerilatedFstBuffer::emitDouble(uint32_t code, double newval) { + m_owner.emitTimeChangeMaybe(); + fstWriterEmitValueChange(m_fst, m_symbolp[code], &newval); +} diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.h new file mode 100644 index 00000000000..b2394ab0704 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.h @@ -0,0 +1,262 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated tracing in FST format header +/// +/// User wrapper code should use this header when creating FST traces. +/// +//============================================================================= + +#ifndef VERILATOR_VERILATED_FST_C_H_ +#define VERILATOR_VERILATED_FST_C_H_ + +#include "verilated.h" +#include "verilated_trace.h" + +#include +#include +#include +#include + +typedef uint32_t vlFstHandle; +typedef uint32_t vlFstEnumHandle; + +class VerilatedFstBuffer; + +struct fstWriterContext; + +//============================================================================= +// VerilatedFst +// Base class to create a Verilator FST dump +// This is an internally used class - see VerilatedFstC for what to call from applications + +class VerilatedFst final : public VerilatedTrace { +public: + using Super = VerilatedTrace; + +private: + friend VerilatedFstBuffer; // Give the buffer access to the private bits + + //========================================================================= + // FST-specific internals + + fstWriterContext* m_fst = nullptr; + std::map m_code2symbol; + std::map> m_local2fstdtype; + vlFstHandle* m_symbolp = nullptr; // same as m_code2symbol, but as an array + char* m_strbufp = nullptr; // String buffer long enough to hold maxBits() chars + uint64_t m_timeui = 0; // Time to emit, 0 = not needed + + bool m_useFstWriterThread = false; // Whether to use the separate FST writer thread + + // Prefixes to add to signal names/scope types + std::vector> m_prefixStack{ + {"", VerilatedTracePrefixType::SCOPE_MODULE}}; + + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedFst); + void declare(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection, + VerilatedTraceSigKind, VerilatedTraceSigType, bool array, int arraynum, + bool bussed, int msb, int lsb); + +protected: + //========================================================================= + // Implementation of VerilatedTrace interface + + // Called when the trace moves forward to a new time point + void emitTimeChange(uint64_t timeui) override; + void emitTimeChangeMaybe(); + + // Hooks called from VerilatedTrace + bool preFullDump() override { return isOpen(); } + bool preChangeDump() override { return isOpen(); } + + // Trace buffer management + Buffer* getTraceBuffer(uint32_t fidx) override; + void commitTraceBuffer(Buffer*) override; + + // Configure sub-class + void configure(const VerilatedTraceConfig&) override; + +public: + //========================================================================= + // External interface to client code + + // CONSTRUCTOR + explicit VerilatedFst(void* fst = nullptr); + ~VerilatedFst(); + + // METHODS - All must be thread safe + // Open the file; call isOpen() to see if errors + void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex); + // Close the file + void close() VL_MT_SAFE_EXCLUDES(m_mutex); + // Flush any remaining data to this file + void flush() VL_MT_SAFE_EXCLUDES(m_mutex); + // Return if file is open + bool isOpen() const VL_MT_SAFE { return m_fst != nullptr; } + + //========================================================================= + // Internal interface to Verilator generated code + + void pushPrefix(const char*, VerilatedTracePrefixType); + void popPrefix(); + + void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum); + void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum); + void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum, int msb, int lsb); + void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum, int msb, int lsb); + void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum, int msb, int lsb); + void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum); + + void declDTypeEnum(int dtypenum, const char* name, uint32_t elements, unsigned int minValbits, + const char** itemNamesp, const char** itemValuesp); +}; + +#ifndef DOXYGEN +// Declare specialization here as it's used in VerilatedFstC just below +template <> +void VerilatedFst::Super::dump(uint64_t time); +template <> +void VerilatedFst::Super::set_time_unit(const char* unitp); +template <> +void VerilatedFst::Super::set_time_unit(const std::string& unit); +template <> +void VerilatedFst::Super::set_time_resolution(const char* unitp); +template <> +void VerilatedFst::Super::set_time_resolution(const std::string& unit); +template <> +void VerilatedFst::Super::dumpvars(int level, const std::string& hier); +#endif + +//============================================================================= +// VerilatedFstBuffer + +class VerilatedFstBuffer VL_NOT_FINAL { + // Give the trace file access to the private bits + friend VerilatedFst; + friend VerilatedFst::Super; + friend VerilatedFst::Buffer; + friend VerilatedFst::OffloadBuffer; + + VerilatedFst& m_owner; // Trace file owning this buffer. Required by subclasses. + + // The FST file handle + fstWriterContext* const m_fst = m_owner.m_fst; + // code to fstHande map, as an array + const vlFstHandle* const m_symbolp = m_owner.m_symbolp; + // String buffer long enough to hold maxBits() chars + char* const m_strbufp = m_owner.m_strbufp; + + // CONSTRUCTOR + explicit VerilatedFstBuffer(VerilatedFst& owner) + : m_owner{owner} {} + virtual ~VerilatedFstBuffer() = default; + + //========================================================================= + // Implementation of VerilatedTraceBuffer interface + + // Implementations of duck-typed methods for VerilatedTraceBuffer. These are + // called from only one place (the full* methods), so always inline them. + VL_ATTR_ALWINLINE void emitEvent(uint32_t code); + VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval); + VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits); + VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits); + VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits); + VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits); + VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits); + VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval); +}; + +//============================================================================= +// VerilatedFstC +/// Create a FST dump file in C standalone (no SystemC) simulations. +/// Also derived for use in SystemC simulations. + +class VerilatedFstC VL_NOT_FINAL : public VerilatedTraceBaseC { + VerilatedFst m_sptrace; // Trace file being created + + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedFstC); + +public: + /// Construct the dump. Optional argument is ignored. + explicit VerilatedFstC(void* filep = nullptr) + : m_sptrace{filep} {} + /// Destruct, flush, and close the dump + virtual ~VerilatedFstC() { close(); } + + // METHODS - User called + + /// Return if file is open + bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); } + /// Open a new FST file + virtual void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); } + /// Close dump + void close() VL_MT_SAFE { + m_sptrace.close(); + modelConnected(false); + } + /// Flush dump + void flush() VL_MT_SAFE { m_sptrace.flush(); } + /// Write one cycle of dump data + /// Call with the current context's time just after eval'ed, + /// e.g. ->dump(contextp->time()) + void dump(uint64_t timeui) { m_sptrace.dump(timeui); } + /// Write one cycle of dump data - backward compatible and to reduce + /// conversion warnings. It's better to use a uint64_t time instead. + void dump(double timestamp) { dump(static_cast(timestamp)); } + void dump(uint32_t timestamp) { dump(static_cast(timestamp)); } + void dump(int timestamp) { dump(static_cast(timestamp)); } + + // METHODS - Internal/backward compatible + // \protectedsection + + // Set time units (s/ms, defaults to ns) + // Users should not need to call this, as for Verilated models, these + // propagate from the Verilated default timeunit + void set_time_unit(const char* unitp) VL_MT_SAFE { m_sptrace.set_time_unit(unitp); } + void set_time_unit(const std::string& unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); } + // Set time resolution (s/ms, defaults to ns) + // Users should not need to call this, as for Verilated models, these + // propagate from the Verilated default timeprecision + void set_time_resolution(const char* unitp) VL_MT_SAFE { + m_sptrace.set_time_resolution(unitp); + } + void set_time_resolution(const std::string& unit) VL_MT_SAFE { + m_sptrace.set_time_resolution(unit); + } + // Set variables to dump, using $dumpvars format + // If level = 0, dump everything and hier is then ignored + void dumpvars(int level, const std::string& hier) VL_MT_SAFE { + m_sptrace.dumpvars(level, hier); + } + + // Internal class access + VerilatedFst* spTrace() { return &m_sptrace; } +}; + +#endif // guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.cpp new file mode 100644 index 00000000000..e19a491f189 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.cpp @@ -0,0 +1,24 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// THIS MODULE IS PUBLICLY LICENSED +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated tracing in FST for SystemC implementation code +/// +/// This file is deprecated, only verilated_fst_sc.h is needed. +/// It is provided only for backward compatibility with user's linker scripts. +/// +//============================================================================= + +#ifdef VL_NO_LEGACY +#error "verilated_fst_sc.cpp is deprecated; verilated_fst_sc.h is self-sufficient" +#endif diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.h new file mode 100644 index 00000000000..fe554add466 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.h @@ -0,0 +1,56 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated tracing in FST format for SystemC header +/// +/// User wrapper code should use this header when creating FST SystemC traces. +/// +/// This class is not threadsafe, as the SystemC kernel is not threadsafe. +/// +//============================================================================= + +#ifndef VERILATOR_VERILATED_FST_SC_H_ +#define VERILATOR_VERILATED_FST_SC_H_ + +#include "verilatedos.h" + +#include "verilated_fst_c.h" +#include "verilated_sc_trace.h" + +//============================================================================= +// VerilatedFstSc +/// Trace file used to create FST dump for SystemC version of Verilated models. It's very similar +/// to its C version (see the class VerilatedFstC) + +class VerilatedFstSc final : VerilatedScTraceBase, public VerilatedFstC { + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedFstSc); + +public: + VerilatedFstSc() { + spTrace()->set_time_unit(VerilatedScTraceBase::getScTimeUnit()); + spTrace()->set_time_resolution(VerilatedScTraceBase::getScTimeResolution()); + } + + // METHODS + // Override VerilatedFstC. Must be called after starting simulation. + void open(const char* filename) override VL_MT_SAFE { + VerilatedScTraceBase::checkScElaborationDone(); + VerilatedFstC::open(filename); + } + + // METHODS - for SC kernel + // Called from SystemC kernel + void cycle() override { VerilatedFstC::dump(sc_core::sc_time_stamp().to_double()); } +}; + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_funcs.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_funcs.h new file mode 100644 index 00000000000..e3e4534ffc2 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_funcs.h @@ -0,0 +1,3059 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated common functions +/// +/// verilated.h should be included instead of this file. +/// +/// Those macro/function/variable starting or ending in _ are internal, +/// however many of the other function/macros here are also internal. +/// +//************************************************************************* + +#ifndef VERILATOR_VERILATED_FUNCS_H_ +#define VERILATOR_VERILATED_FUNCS_H_ + +#ifndef VERILATOR_VERILATED_H_INTERNAL_ +#error "verilated_funcs.h should only be included by verilated.h" +#endif + +#include + +//========================================================================= +// Extern functions -- User may override -- See verilated.cpp + +/// Routine to call for $finish +/// User code may wish to replace this function, to do so, define VL_USER_FINISH. +/// This code does not have to be thread safe. +/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this. +extern void vl_finish(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE; + +/// Routine to call for $stop and non-fatal error +/// User code may wish to replace this function, to do so, define VL_USER_STOP. +/// This code does not have to be thread safe. +/// Verilator internal code must call VL_STOP_MT instead, which eventually calls this. +extern void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE; + +/// Routine to call for fatal messages +/// User code may wish to replace this function, to do so, define VL_USER_FATAL. +/// This code does not have to be thread safe. +/// Verilator internal code must call VL_FATAL_MT instead, which eventually calls this. +extern void vl_fatal(const char* filename, int linenum, const char* hier, + const char* msg) VL_MT_UNSAFE; + +/// Routine to call for warning messages +/// User code may wish to replace this function, to do so, define VL_USER_WARN. +/// This code does not have to be thread safe. +/// Verilator internal code must call VL_WARN_MT instead, which eventually calls this. +extern void vl_warn(const char* filename, int linenum, const char* hier, + const char* msg) VL_MT_UNSAFE; + +//========================================================================= +// Extern functions -- Slow path + +/// Multithread safe wrapper for calls to $finish +extern void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE; +/// Multithread safe wrapper for calls to $stop +extern void VL_STOP_MT(const char* filename, int linenum, const char* hier, + bool maybe = true) VL_MT_SAFE; +/// Multithread safe wrapper to call for fatal messages +extern void VL_FATAL_MT(const char* filename, int linenum, const char* hier, + const char* msg) VL_MT_SAFE; +/// Multithread safe wrapper to call for warning messages +extern void VL_WARN_MT(const char* filename, int linenum, const char* hier, + const char* msg) VL_MT_SAFE; + +// clang-format off +/// Print a string, multithread safe. Eventually VL_PRINTF will get called. +extern void VL_PRINTF_MT(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE; +// clang-format on + +/// Print a debug message from internals with standard prefix, with printf style format +extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE; + +/// Print a debug message from string via VL_DBG_MSGF +inline void VL_DBG_MSGS(const std::string& str) VL_MT_SAFE { VL_DBG_MSGF("%s", str.c_str()); } + +// EMIT_RULE: VL_RANDOM: oclean=dirty +inline IData VL_RANDOM_I() VL_MT_SAFE { return vl_rand64(); } +inline QData VL_RANDOM_Q() VL_MT_SAFE { return vl_rand64(); } +extern WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE; +extern IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE; +extern IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE; +inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) { + const uint64_t rnd = vl_rand64(); + if (VL_LIKELY(hi > lo)) { + // (hi - lo + 1) can be zero when hi is UINT_MAX and lo is zero + if (VL_UNLIKELY(hi - lo + 1 == 0)) return rnd; + // Modulus isn't very fast but it's common that hi-low is power-of-two + return (rnd % (hi - lo + 1)) + lo; + } else { + if (VL_UNLIKELY(lo - hi + 1 == 0)) return rnd; + return (rnd % (lo - hi + 1)) + hi; + } +} + +/// Random reset a signal of given width (init time only, var-specific PRNG) +extern IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE; +/// Random reset a signal of given width (init time only, var-specific PRNG) +extern QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE; +/// Random reset a signal of given width (init time only, var-specific PRNG) +extern WDataOutP VL_SCOPED_RAND_RESET_W(int obits, WDataOutP outwp, uint64_t scopeHash, + uint64_t salt) VL_MT_UNSAFE; + +/// Random reset a signal of given width (assign time only) +extern IData VL_SCOPED_RAND_RESET_ASSIGN_I(int obits, uint64_t scopeHash, + uint64_t salt) VL_MT_UNSAFE; +/// Random reset a signal of given width (assign time only) +extern QData VL_SCOPED_RAND_RESET_ASSIGN_Q(int obits, uint64_t scopeHash, + uint64_t salt) VL_MT_UNSAFE; +/// Random reset a signal of given width (assign time only) +extern WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint64_t scopeHash, + uint64_t salt) VL_MT_UNSAFE; + +/// Random reset a signal of given width (init time only) +extern IData VL_RAND_RESET_I(int obits) VL_MT_SAFE; +/// Random reset a signal of given width (init time only) +extern QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE; +/// Random reset a signal of given width (init time only) +extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE; + +/// Zero reset a signal (slow - else use VL_ZERO_W) +extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE; + +extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp, + const VerilatedContext* contextp) VL_MT_SAFE; + +extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP const lwp, WDataInP const rwp, + bool is_modulus) VL_MT_SAFE; + +extern void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp, + size_t posstart, size_t posend) VL_MT_SAFE; + +extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE; + +extern void VL_FFLUSH_I(IData fdi) VL_MT_SAFE; +extern IData VL_FSEEK_I(IData fdi, IData offset, IData origin) VL_MT_SAFE; +extern IData VL_FTELL_I(IData fdi) VL_MT_SAFE; +extern void VL_FCLOSE_I(IData fdi) VL_MT_SAFE; + +extern IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi, + IData start, IData count) VL_MT_SAFE; + +extern void VL_WRITEF_NX(const std::string& format, int argc, ...) VL_MT_SAFE; +extern void VL_FWRITEF_NX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE; + +extern IData VL_FSCANF_INX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE; +extern IData VL_SSCANF_IINX(int lbits, IData ld, const std::string& format, int argc, + ...) VL_MT_SAFE; +extern IData VL_SSCANF_IQNX(int lbits, QData ld, const std::string& format, int argc, + ...) VL_MT_SAFE; +extern IData VL_SSCANF_IWNX(int lbits, WDataInP const lwp, const std::string& format, int argc, + ...) VL_MT_SAFE; + +extern void VL_SFORMAT_NX(int obits, CData& destr, const std::string& format, int argc, + ...) VL_MT_SAFE; +extern void VL_SFORMAT_NX(int obits, SData& destr, const std::string& format, int argc, + ...) VL_MT_SAFE; +extern void VL_SFORMAT_NX(int obits, IData& destr, const std::string& format, int argc, + ...) VL_MT_SAFE; +extern void VL_SFORMAT_NX(int obits, QData& destr, const std::string& format, int argc, + ...) VL_MT_SAFE; +extern void VL_SFORMAT_NX(int obits, void* destp, const std::string& format, int argc, + ...) VL_MT_SAFE; + +extern void VL_STACKTRACE() VL_MT_SAFE; +extern std::string VL_STACKTRACE_N() VL_MT_SAFE; +extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp) VL_MT_SAFE; +extern IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE; +inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); } +extern IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE; + +extern IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE; +extern const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE; // PLIish + +//========================================================================= +// Base macros + +// Return true if data[bit] set; not 0/1 return, but 0/non-zero return. +// Arguments must not have side effects +#define VL_BITISSETLIMIT_W(data, width, bit) (((bit) < (width)) && VL_BITISSET_W(data, bit)) + +// Shift appropriate word by bit. Does not account for wrapping between two words +// Argument 'bit' must not have side effects +#define VL_BITRSHIFT_W(data, bit) ((data)[VL_BITWORD_E(bit)] >> VL_BITBIT_E(bit)) + +// Create two 32-bit words from quadword +// WData is always at least 2 words; does not clean upper bits +#define VL_SET_WQ(owp, data) \ + do { \ + (owp)[0] = static_cast(data); \ + (owp)[1] = static_cast((data) >> VL_EDATASIZE); \ + } while (false) +#define VL_SET_WI(owp, data) \ + do { \ + (owp)[0] = static_cast(data); \ + (owp)[1] = 0; \ + } while (false) +#define VL_SET_QW(lwp) \ + ((static_cast((lwp)[0])) \ + | (static_cast((lwp)[1]) << (static_cast(VL_EDATASIZE)))) +#define VL_SET_QII(ld, rd) ((static_cast(ld) << 32ULL) | static_cast(rd)) + +// Return FILE* from IData +extern FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE; + +// clang-format off +// Use a union to avoid cast-to-different-size warnings +// Return void* from QData +static inline void* VL_CVT_Q_VP(QData lhs) VL_PURE { + union { void* fp; QData q; } u; + u.q = lhs; + return u.fp; +} +// Return QData from const void* +static inline QData VL_CVT_VP_Q(const void* fp) VL_PURE { + union { const void* fp; QData q; } u; + u.q = 0; + u.fp = fp; + return u.q; +} +// Return double from QData (bits, not numerically) +static inline double VL_CVT_D_Q(QData lhs) VL_PURE { + union { double d; QData q; } u; + u.q = lhs; + return u.d; +} +// Return QData from double (bits, not numerically) +static inline QData VL_CVT_Q_D(double lhs) VL_PURE { + union { double d; QData q; } u; + u.d = lhs; + return u.q; +} +// clang-format on +// Return string from DPI char* +static inline std::string VL_CVT_N_CSTR(const char* lhsp) VL_PURE { + return lhsp ? std::string{lhsp} : ""s; +} + +// Return queue from an unpacked array +template +static inline VlQueue VL_CVT_UNPACK_TO_Q(const VlUnpacked& q) VL_PURE { + VlQueue ret; + for (size_t i = 0; i < N_Depth; ++i) ret.push_back(q[i]); + return ret; +} + +// Return double from lhs (numeric) unsigned +double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE; +static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE { + return static_cast(static_cast(lhs)); +} +static inline double VL_ITOR_D_Q(int, QData lhs) VL_PURE { + return static_cast(static_cast(lhs)); +} +// Return double from lhs (numeric) signed +double VL_ISTOR_D_W(int lbits, WDataInP const lwp) VL_MT_SAFE; +static inline double VL_ISTOR_D_I(int lbits, IData lhs) VL_MT_SAFE { + if (lbits == 32) return static_cast(static_cast(lhs)); + VlWide lwp; + VL_SET_WI(lwp, lhs); + return VL_ISTOR_D_W(lbits, lwp); +} +static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_MT_SAFE { + if (lbits == 64) return static_cast(static_cast(lhs)); + VlWide lwp; + VL_SET_WQ(lwp, lhs); + return VL_ISTOR_D_W(lbits, lwp); +} +// Return IData truncated from double (numeric) +static inline IData VL_RTOI_I_D(double lhs) VL_PURE { return static_cast(VL_TRUNC(lhs)); } + +// Sign extend such that if MSB set, we get ffff_ffff, else 0s +// (Requires clean input) +#define VL_SIGN_I(nbits, lhs) ((lhs) >> VL_BITBIT_I((nbits) - VL_UL(1))) +#define VL_SIGN_Q(nbits, lhs) ((lhs) >> VL_BITBIT_Q((nbits) - 1ULL)) +#define VL_SIGN_E(nbits, lhs) ((lhs) >> VL_BITBIT_E((nbits) - VL_EUL(1))) +#define VL_SIGN_W(nbits, rwp) \ + ((rwp)[VL_BITWORD_E((nbits) - VL_EUL(1))] >> VL_BITBIT_E((nbits) - VL_EUL(1))) +#define VL_SIGNONES_E(nbits, lhs) (-(VL_SIGN_E(nbits, lhs))) + +// Sign bit extended up to MSB, doesn't include unsigned portion +// Optimization bug in GCC 3.3 returns different bitmasks to later states for +static inline IData VL_EXTENDSIGN_I(int lbits, IData lhs) VL_PURE { + return (-((lhs) & (VL_UL(1) << (lbits - 1)))); +} +static inline QData VL_EXTENDSIGN_Q(int lbits, QData lhs) VL_PURE { + return (-((lhs) & (1ULL << (lbits - 1)))); +} + +// Debugging prints +extern void _vl_debug_print_w(int lbits, WDataInP const iwp) VL_MT_SAFE; + +//========================================================================= +// Time handling + +// clang-format off + +#if defined(SYSTEMC_VERSION) +/// Return current simulation time +// Already defined: extern sc_time sc_time_stamp(); +inline uint64_t vl_time_stamp64() VL_MT_SAFE { return sc_core::sc_time_stamp().value(); } +#else // Non-SystemC +# if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY) +# ifdef VL_TIME_STAMP64 +// vl_time_stamp64() may be optionally defined by the user to return time. +// On MSVC++ weak symbols are not supported so must be declared, or define +// VL_TIME_CONTEXT. +extern uint64_t vl_time_stamp64() VL_ATTR_WEAK VL_MT_SAFE; +# else +// sc_time_stamp() may be optionally defined by the user to return time. +// On MSVC++ weak symbols are not supported so must be declared, or define +// VL_TIME_CONTEXT. +extern double sc_time_stamp() VL_ATTR_WEAK VL_MT_SAFE; // Verilator 4.032 and newer +inline uint64_t vl_time_stamp64() VL_MT_SAFE { + // clang9.0.1 requires & although we really do want the weak symbol value + // cppcheck-suppress duplicateValueTernary + return VL_LIKELY(&sc_time_stamp) ? static_cast(sc_time_stamp()) : 0; +} +# endif +# endif +#endif + +// clang-format on + +uint64_t VerilatedContext::time() const VL_MT_SAFE { + // When using non-default context, fastest path is return time + if (VL_LIKELY(m_s.m_time)) return m_s.m_time; +#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY)) + // Zero time could mean really at zero, or using callback + // clang9.0.1 requires & although we really do want the weak symbol value + if (VL_LIKELY(&vl_time_stamp64)) { // else is weak symbol that is not defined + return vl_time_stamp64(); + } +#endif + return 0; +} + +#define VL_TIME_Q() (Verilated::threadContextp()->time()) +#define VL_TIME_D() (static_cast(VL_TIME_Q())) + +// Time scaled from 1-per-precision into a module's time units ("Unit"-ed, not "United") +// Optimized assuming scale is always constant. +// Can't use multiply in Q flavor, as might lose precision +#define VL_TIME_ROUND(t, p) (((t) + ((p) / 2)) / (p)) +#define VL_TIME_UNITED_Q(scale) VL_TIME_ROUND(VL_TIME_Q(), static_cast(scale)) +#define VL_TIME_UNITED_D(scale) (VL_TIME_D() / static_cast(scale)) + +// Return time precision as multiplier of time units +double vl_time_multiplier(int scale) VL_PURE; +// Return power of 10. e.g. returns 100 if n==2 +uint64_t vl_time_pow10(int n) VL_PURE; +// Return time as string with timescale suffix +std::string vl_timescaled_double(double value, const char* format = "%0.0f%s") VL_PURE; + +//========================================================================= +// Functional macros/routines +// These all take the form +// VL_func_IW(bits, bits, op, op) +// VL_func_WW(bits, bits, out, op, op) +// The I/W indicates if it's a integer or wide for the output and each operand. +// The bits indicate the bit width of the output and each operand. +// If wide output, a temporary storage location is specified. + +//=================================================================== +// SETTING OPERATORS + +VL_ATTR_ALWINLINE +static WDataOutP VL_MEMSET_ZERO_W(WDataOutP owp, int words) VL_MT_SAFE { + return static_cast(std::memset(owp, 0, words * sizeof(EData))); +} +VL_ATTR_ALWINLINE +static WDataOutP VL_MEMSET_ONES_W(WDataOutP owp, int words) VL_MT_SAFE { + return static_cast(std::memset(owp, 0xff, words * sizeof(EData))); +} +VL_ATTR_ALWINLINE +static WDataOutP VL_MEMCPY_W(WDataOutP owp, WDataInP const iwp, int words) VL_MT_SAFE { + return static_cast(std::memcpy(owp, iwp, words * sizeof(EData))); +} + +// Output clean +// EMIT_RULE: VL_CLEAN: oclean=clean; obits=lbits; +#define VL_CLEAN_II(obits, lbits, lhs) ((lhs) & (VL_MASK_I(obits))) +#define VL_CLEAN_QQ(obits, lbits, lhs) ((lhs) & (VL_MASK_Q(obits))) + +// EMIT_RULE: VL_ASSIGNCLEAN: oclean=clean; obits==lbits; +#define VL_ASSIGNCLEAN_W(obits, owp, lwp) VL_CLEAN_WW((obits), (owp), (lwp)) +static inline WDataOutP _vl_clean_inplace_w(int obits, WDataOutP owp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + owp[words - 1] &= VL_MASK_E(obits); + return owp; +} +static inline WDataOutP VL_CLEAN_WW(int obits, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + VL_MEMCPY_W(owp, lwp, words - 1); + owp[words - 1] = lwp[words - 1] & VL_MASK_E(obits); + return owp; +} +static inline WDataOutP VL_ZERO_W(int obits, WDataOutP owp) VL_MT_SAFE { + return VL_MEMSET_ZERO_W(owp, VL_WORDS_I(obits)); +} +static inline WDataOutP VL_ALLONES_W(int obits, WDataOutP owp) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + VL_MEMSET_ONES_W(owp, words - 1); + owp[words - 1] = VL_MASK_E(obits); + return owp; +} + +// EMIT_RULE: VL_ASSIGN: oclean=rclean; obits==lbits; +// For now, we always have a clean rhs. +// Note: If a ASSIGN isn't clean, use VL_ASSIGNCLEAN instead to do the same thing. +static inline WDataOutP VL_ASSIGN_W(int obits, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + return VL_MEMCPY_W(owp, lwp, VL_WORDS_I(obits)); +} + +// EMIT_RULE: VL_ASSIGNBIT: rclean=clean; +static inline void VL_ASSIGNBIT_II(int bit, CData& lhsr, IData rhs) VL_PURE { + lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_II(int bit, SData& lhsr, IData rhs) VL_PURE { + lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_II(int bit, IData& lhsr, IData rhs) VL_PURE { + lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_QI(int bit, QData& lhsr, QData rhs) VL_PURE { + lhsr = ((lhsr & ~(1ULL << VL_BITBIT_Q(bit))) | (static_cast(rhs) << VL_BITBIT_Q(bit))); +} +static inline void VL_ASSIGNBIT_WI(int bit, WDataOutP owp, IData rhs) VL_MT_SAFE { + const EData orig = owp[VL_BITWORD_E(bit)]; + owp[VL_BITWORD_E(bit)] = ((orig & ~(VL_EUL(1) << VL_BITBIT_E(bit))) + | (static_cast(rhs) << VL_BITBIT_E(bit))); +} +// Alternative form that is an instruction faster when rhs is constant one. +static inline void VL_ASSIGNBIT_IO(int bit, CData& lhsr) VL_PURE { + lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_IO(int bit, SData& lhsr) VL_PURE { + lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_IO(int bit, IData& lhsr) VL_PURE { + lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit))); +} +static inline void VL_ASSIGNBIT_QO(int bit, QData& lhsr) VL_PURE { + lhsr = (lhsr | (1ULL << VL_BITBIT_Q(bit))); +} +static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE { + const EData orig = owp[VL_BITWORD_E(bit)]; + owp[VL_BITWORD_E(bit)] = (orig | (VL_EUL(1) << VL_BITBIT_E(bit))); +} + +//=================================================================== +// SYSTEMC OPERATORS +// Copying verilog format to systemc integers, doubles, and bit vectors. +// Get a SystemC variable + +#define VL_ASSIGN_DSD(obits, vvar, svar) \ + { (vvar) = (svar).read(); } +#define VL_ASSIGN_ISI(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read()); } +#define VL_ASSIGN_QSQ(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read()); } + +#define VL_ASSIGN_ISW(obits, od, svar) \ + { (od) = ((svar).read().get_word(0)) & VL_MASK_I(obits); } +#define VL_ASSIGN_QSW(obits, od, svar) \ + { \ + (od) = ((static_cast((svar).read().get_word(1))) << VL_IDATASIZE \ + | (svar).read().get_word(0)) \ + & VL_MASK_Q(obits); \ + } +#define VL_ASSIGN_WSW(obits, owp, svar) \ + { \ + const int words = VL_WORDS_I(obits); \ + for (int i = 0; i < words; ++i) (owp)[i] = (svar).read().get_word(i); \ + (owp)[words - 1] &= VL_MASK_E(obits); \ + } + +#define VL_ASSIGN_ISU(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read().to_uint()); } +#define VL_ASSIGN_QSU(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read().to_uint64()); } +#define VL_ASSIGN_ISB(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read().to_uint()); } +#define VL_ASSIGN_QSB(obits, vvar, svar) \ + { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read().to_uint64()); } +#define VL_ASSIGN_WSB(obits, owp, svar) \ + { \ + const int words = VL_WORDS_I(obits); \ + sc_dt::sc_biguint<(obits)> _butemp = (svar).read(); \ + uint32_t* chunkp = _butemp.get_raw(); \ + int32_t lsb = 0; \ + while (lsb < obits - BITS_PER_DIGIT) { \ + const uint32_t data = *chunkp; \ + ++chunkp; \ + _vl_insert_WI(owp.data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \ + lsb += BITS_PER_DIGIT; \ + } \ + if (lsb < obits) { \ + const uint32_t msb_data = *chunkp; \ + _vl_insert_WI(owp.data(), msb_data, obits - 1, lsb); \ + } \ + (owp)[words - 1] &= VL_MASK_E(obits); \ + } + +// Copying verilog format from systemc integers, doubles, and bit vectors. +// Set a SystemC variable + +#define VL_ASSIGN_SDD(obits, svar, vvar) \ + { (svar).write(vvar); } +#define VL_ASSIGN_SII(obits, svar, vvar) \ + { (svar).write(vvar); } +#define VL_ASSIGN_SQQ(obits, svar, vvar) \ + { (svar).write(vvar); } + +#define VL_ASSIGN_SWI(obits, svar, rd) \ + { \ + sc_dt::sc_bv<(obits)> _bvtemp; \ + _bvtemp.set_word(0, (rd)); \ + (svar).write(_bvtemp); \ + } +#define VL_ASSIGN_SWQ(obits, svar, rd) \ + { \ + sc_dt::sc_bv<(obits)> _bvtemp; \ + _bvtemp.set_word(0, static_cast(rd)); \ + _bvtemp.set_word(1, static_cast((rd) >> VL_IDATASIZE)); \ + (svar).write(_bvtemp); \ + } +#define VL_ASSIGN_SWW(obits, svar, rwp) \ + { \ + sc_dt::sc_bv<(obits)> _bvtemp; \ + for (int i = 0; i < VL_WORDS_I(obits); ++i) _bvtemp.set_word(i, (rwp)[i]); \ + (svar).write(_bvtemp); \ + } + +#define VL_ASSIGN_SUI(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SUQ(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SBI(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SBQ(obits, svar, rd) \ + { (svar).write(rd); } +#define VL_ASSIGN_SBW(obits, svar, rwp) \ + { \ + sc_dt::sc_biguint<(obits)> _butemp; \ + int32_t lsb = 0; \ + uint32_t* chunkp = _butemp.get_raw(); \ + while (lsb + BITS_PER_DIGIT < (obits)) { \ + static_assert(std::is_same::value, "IData and EData mismatch"); \ + const uint32_t data \ + = VL_SEL_IWII(lsb + BITS_PER_DIGIT + 1, (rwp).data(), lsb, BITS_PER_DIGIT); \ + *chunkp = data & VL_MASK_E(BITS_PER_DIGIT); \ + ++chunkp; \ + lsb += BITS_PER_DIGIT; \ + } \ + if (lsb < (obits)) { \ + const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits) - lsb); \ + *chunkp = msb_data & VL_MASK_E((obits) - lsb); \ + } \ + _butemp.set(0, *(rwp).data() & 1); /* force update the sign */ \ + (svar).write(_butemp); \ + } + +//=================================================================== +// Extending sizes + +// CAREFUL, we're width changing, so obits!=lbits + +// Right must be clean because otherwise size increase would pick up bad bits +// EMIT_RULE: VL_EXTEND: oclean=clean; rclean==clean; +#define VL_EXTEND_II(obits, lbits, lhs) ((lhs)) +#define VL_EXTEND_QI(obits, lbits, lhs) (static_cast(lhs)) +#define VL_EXTEND_QQ(obits, lbits, lhs) ((lhs)) + +static inline WDataOutP VL_EXTEND_WI(int obits, int, WDataOutP owp, IData ld) VL_MT_SAFE { + // Note for extracts that obits != lbits + owp[0] = ld; + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + return owp; +} +static inline WDataOutP VL_EXTEND_WQ(int obits, int, WDataOutP owp, QData ld) VL_MT_SAFE { + VL_SET_WQ(owp, ld); + VL_MEMSET_ZERO_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E); + return owp; +} +static inline WDataOutP VL_EXTEND_WW(int obits, int lbits, WDataOutP owp, + WDataInP const lwp) VL_MT_SAFE { + const int lwords = VL_WORDS_I(lbits); + VL_PREFETCH_RD(lwp); + VL_MEMSET_ZERO_W(owp + lwords, VL_WORDS_I(obits) - lwords); + return VL_MEMCPY_W(owp, lwp, lwords); +} + +// EMIT_RULE: VL_EXTENDS: oclean=*dirty*; obits=lbits; +// Sign extension; output dirty +static inline IData VL_EXTENDS_II(int, int lbits, IData lhs) VL_PURE { + return VL_EXTENDSIGN_I(lbits, lhs) | lhs; +} +static inline QData VL_EXTENDS_QI(int, int lbits, QData lhs /*Q_as_need_extended*/) VL_PURE { + return VL_EXTENDSIGN_Q(lbits, lhs) | lhs; +} +static inline QData VL_EXTENDS_QQ(int, int lbits, QData lhs) VL_PURE { + return VL_EXTENDSIGN_Q(lbits, lhs) | lhs; +} + +static inline WDataOutP VL_EXTENDS_WI(int obits, int lbits, WDataOutP owp, IData ld) VL_MT_SAFE { + owp[0] = ld; + if (VL_SIGN_E(lbits, owp[0])) { + owp[0] |= ~VL_MASK_E(lbits); + VL_MEMSET_ONES_W(owp + 1, VL_WORDS_I(obits) - 1); + } else { + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + } + return owp; +} +static inline WDataOutP VL_EXTENDS_WQ(int obits, int lbits, WDataOutP owp, QData ld) VL_MT_SAFE { + VL_SET_WQ(owp, ld); + if (VL_SIGN_E(lbits, owp[1])) { + owp[1] |= ~VL_MASK_E(lbits); + VL_MEMSET_ONES_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E); + } else { + VL_MEMSET_ZERO_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E); + } + return owp; +} +static inline WDataOutP VL_EXTENDS_WW(int obits, int lbits, WDataOutP owp, + WDataInP const lwp) VL_MT_SAFE { + const int lwords = VL_WORDS_I(lbits); + VL_PREFETCH_RD(lwp); + owp[lwords - 1] = lwp[lwords - 1]; + if (VL_SIGN_E(lbits, lwp[lwords - 1])) { + owp[lwords - 1] |= ~VL_MASK_E(lbits); + VL_MEMSET_ONES_W(owp + lwords, VL_WORDS_I(obits) - lwords); + } else { + VL_MEMSET_ZERO_W(owp + lwords, VL_WORDS_I(obits) - lwords); + } + return VL_MEMCPY_W(owp, lwp, lwords - 1); +} + +//=================================================================== +// REDUCTION OPERATORS + +// EMIT_RULE: VL_REDAND: oclean=clean; lclean==clean; obits=1; +#define VL_REDAND_II(lbits, lhs) ((lhs) == VL_MASK_I(lbits)) +#define VL_REDAND_IQ(lbits, lhs) ((lhs) == VL_MASK_Q(lbits)) +static inline IData VL_REDAND_IW(int lbits, WDataInP const lwp) VL_PURE { + const int words = VL_WORDS_I(lbits); + EData combine = lwp[0]; + for (int i = 1; i < words - 1; ++i) combine &= lwp[i]; + combine &= ~VL_MASK_E(lbits) | lwp[words - 1]; + // cppcheck-suppress knownConditionTrueFalse + return ((~combine) == 0); +} + +// EMIT_RULE: VL_REDOR: oclean=clean; lclean==clean; obits=1; +#define VL_REDOR_I(lhs) ((lhs) != 0) +#define VL_REDOR_Q(lhs) ((lhs) != 0) +static inline IData VL_REDOR_W(int words, WDataInP const lwp) VL_PURE { + EData equal = 0; + for (int i = 0; i < words; ++i) equal |= lwp[i]; + return (equal != 0); +} + +// EMIT_RULE: VL_REDXOR: oclean=dirty; obits=1; +static inline IData VL_REDXOR_2(IData r) VL_PURE { + // Experiments show VL_REDXOR_2 is faster than __builtin_parityl + r = (r ^ (r >> 1)); + return r; +} +static inline IData VL_REDXOR_4(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + return r; +#endif +} +static inline IData VL_REDXOR_8(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + return r; +#endif +} +static inline IData VL_REDXOR_16(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + r = (r ^ (r >> 8)); + return r; +#endif +} +static inline IData VL_REDXOR_32(IData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityl(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + r = (r ^ (r >> 8)); + r = (r ^ (r >> 16)); + return r; +#endif +} +static inline IData VL_REDXOR_64(QData r) VL_PURE { +#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS) + return __builtin_parityll(r); +#else + r = (r ^ (r >> 1)); + r = (r ^ (r >> 2)); + r = (r ^ (r >> 4)); + r = (r ^ (r >> 8)); + r = (r ^ (r >> 16)); + r = (r ^ (r >> 32)); + return static_cast(r); +#endif +} +static inline IData VL_REDXOR_W(int words, WDataInP const lwp) VL_PURE { + EData r = lwp[0]; + for (int i = 1; i < words; ++i) r ^= lwp[i]; + return VL_REDXOR_32(r); +} + +// EMIT_RULE: VL_COUNTONES_II: oclean = false; lhs clean +static inline IData VL_COUNTONES_I(IData lhs) VL_PURE { + // This is faster than __builtin_popcountl + IData r = lhs - ((lhs >> 1) & 033333333333) - ((lhs >> 2) & 011111111111); + r = (r + (r >> 3)) & 030707070707; + r = (r + (r >> 6)); + r = (r + (r >> 12) + (r >> 24)) & 077; + return r; +} +static inline IData VL_COUNTONES_Q(QData lhs) VL_PURE { + return VL_COUNTONES_I(static_cast(lhs)) + VL_COUNTONES_I(static_cast(lhs >> 32)); +} +#define VL_COUNTONES_E VL_COUNTONES_I +static inline IData VL_COUNTONES_W(int words, WDataInP const lwp) VL_PURE { + EData r = 0; + for (int i = 0; i < words; ++i) r += VL_COUNTONES_E(lwp[i]); + return r; +} + +// EMIT_RULE: VL_COUNTBITS_II: oclean = false; lhs clean +static inline IData VL_COUNTBITS_I(int lbits, IData lhs, IData ctrl0, IData ctrl1, + IData ctrl2) VL_PURE { + const int ctrlSum = (ctrl0 & 0x1) + (ctrl1 & 0x1) + (ctrl2 & 0x1); + if (ctrlSum == 3) { + return VL_COUNTONES_I(lhs); + } else if (ctrlSum == 0) { + const IData mask = (lbits == 32) ? -1 : ((1 << lbits) - 1); + return VL_COUNTONES_I(~lhs & mask); + } else { + return (lbits == 32) ? 32 : lbits; + } +} +static inline IData VL_COUNTBITS_Q(int lbits, QData lhs, IData ctrl0, IData ctrl1, + IData ctrl2) VL_PURE { + return VL_COUNTBITS_I(32, static_cast(lhs), ctrl0, ctrl1, ctrl2) + + VL_COUNTBITS_I(lbits - 32, static_cast(lhs >> 32), ctrl0, ctrl1, ctrl2); +} +#define VL_COUNTBITS_E VL_COUNTBITS_I +static inline IData VL_COUNTBITS_W(int lbits, int words, WDataInP const lwp, IData ctrl0, + IData ctrl1, IData ctrl2) VL_MT_SAFE { + EData r = 0; + IData wordLbits = 32; + for (int i = 0; i < words; ++i) { + if (i == words - 1) wordLbits = lbits % 32; + r += VL_COUNTBITS_E(wordLbits, lwp[i], ctrl0, ctrl1, ctrl2); + } + return r; +} + +static inline IData VL_ONEHOT_I(IData lhs) VL_PURE { + return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); +} +static inline IData VL_ONEHOT_Q(QData lhs) VL_PURE { + return (((lhs & (lhs - 1)) == 0) & (lhs != 0)); +} +static inline IData VL_ONEHOT_W(int words, WDataInP const lwp) VL_PURE { + EData one = 0; + for (int i = 0; (i < words); ++i) { + if (lwp[i]) { + if (one) return 0; + one = 1; + if (lwp[i] & (lwp[i] - 1)) return 0; + } + } + return one; +} + +static inline IData VL_ONEHOT0_I(IData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); } +static inline IData VL_ONEHOT0_Q(QData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); } +static inline IData VL_ONEHOT0_W(int words, WDataInP const lwp) VL_PURE { + bool one = false; + for (int i = 0; (i < words); ++i) { + if (lwp[i]) { + if (one) return 0; + one = true; + if (lwp[i] & (lwp[i] - 1)) return 0; + } + } + return 1; +} + +static inline IData VL_CLOG2_I(IData lhs) VL_PURE { + // There are faster algorithms, or fls GCC4 builtins, but rarely used + // In C++20 there will be std::bit_width(lhs) - 1 + if (VL_UNLIKELY(!lhs)) return 0; + --lhs; + int shifts = 0; + for (; lhs != 0; ++shifts) lhs = lhs >> 1; + return shifts; +} +static inline IData VL_CLOG2_Q(QData lhs) VL_PURE { + if (VL_UNLIKELY(!lhs)) return 0; + --lhs; + int shifts = 0; + for (; lhs != 0; ++shifts) lhs = lhs >> 1ULL; + return shifts; +} +static inline IData VL_CLOG2_W(int words, WDataInP const lwp) VL_PURE { + const EData adjust = (VL_COUNTONES_W(words, lwp) == 1) ? 0 : 1; + for (int i = words - 1; i >= 0; --i) { + if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken + for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { + if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) { + return i * VL_EDATASIZE + bit + adjust; + } + } + // Can't get here - one bit must be set + } + } + return 0; +} + +static inline IData VL_MOSTSETBITP1_W(int words, WDataInP const lwp) VL_PURE { + // MSB set bit plus one; similar to FLS. 0=value is zero + for (int i = words - 1; i >= 0; --i) { + if (VL_UNLIKELY(lwp[i])) { // Shorter worst case if predict not taken + for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) { + if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) return i * VL_EDATASIZE + bit + 1; + } + // Can't get here - one bit must be set + } + } + return 0; +} + +//=================================================================== +// SIMPLE LOGICAL OPERATORS + +// EMIT_RULE: VL_AND: oclean=lclean||rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_AND_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] & rwp[i]); + return owp; +} +// EMIT_RULE: VL_OR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_OR_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] | rwp[i]); + return owp; +} +// EMIT_RULE: VL_CHANGEXOR: oclean=1; obits=32; lbits==rbits; +static inline IData VL_CHANGEXOR_W(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE { + IData od = 0; + for (int i = 0; (i < words); ++i) od |= (lwp[i] ^ rwp[i]); + return od; +} +// EMIT_RULE: VL_XOR: oclean=lclean&&rclean; obits=lbits; lbits==rbits; +static inline WDataOutP VL_XOR_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] ^ rwp[i]); + return owp; +} +// EMIT_RULE: VL_NOT: oclean=dirty; obits=lbits; +static inline WDataOutP VL_NOT_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + for (int i = 0; i < words; ++i) owp[i] = ~(lwp[i]); + return owp; +} + +//========================================================================= +// Logical comparisons + +// EMIT_RULE: VL_EQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_NEQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_LT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_GT: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_GTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +// EMIT_RULE: VL_LTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits; +#define VL_NEQ_W(words, lwp, rwp) (!VL_EQ_W(words, lwp, rwp)) +#define VL_LT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) < 0) +#define VL_LTE_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) <= 0) +#define VL_GT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) > 0) +#define VL_GTE_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) >= 0) + +// Output clean, AND MUST BE CLEAN +static inline IData VL_EQ_W(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE { + EData nequal = 0; + for (int i = 0; (i < words); ++i) nequal |= (lwp[i] ^ rwp[i]); + return (nequal == 0); +} + +// Internal usage +static inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE { + for (int i = words - 1; i >= 0; --i) { + if (lwp[i] > rwp[i]) return 1; + if (lwp[i] < rwp[i]) return -1; + } + return 0; // == +} + +#define VL_LTS_IWW(lbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) < 0) +#define VL_LTES_IWW(lbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) <= 0) +#define VL_GTS_IWW(lbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) > 0) +#define VL_GTES_IWW(lbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) >= 0) + +static inline IData VL_GTS_III(int lbits, IData lhs, IData rhs) VL_PURE { + // For lbits==32, this becomes just a single instruction, otherwise ~5. + // GCC 3.3.4 sign extension bugs on AMD64 architecture force us to use quad logic + const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed > rhs_signed; +} +static inline IData VL_GTS_IQQ(int lbits, QData lhs, QData rhs) VL_PURE { + const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed > rhs_signed; +} + +static inline IData VL_GTES_III(int lbits, IData lhs, IData rhs) VL_PURE { + const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed >= rhs_signed; +} +static inline IData VL_GTES_IQQ(int lbits, QData lhs, QData rhs) VL_PURE { + const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed >= rhs_signed; +} + +static inline IData VL_LTS_III(int lbits, IData lhs, IData rhs) VL_PURE { + const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed < rhs_signed; +} +static inline IData VL_LTS_IQQ(int lbits, QData lhs, QData rhs) VL_PURE { + const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed < rhs_signed; +} + +static inline IData VL_LTES_III(int lbits, IData lhs, IData rhs) VL_PURE { + const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); // Q for gcc + const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); // Q for gcc + return lhs_signed <= rhs_signed; +} +static inline IData VL_LTES_IQQ(int lbits, QData lhs, QData rhs) VL_PURE { + const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed <= rhs_signed; +} + +static inline int _vl_cmps_w(int lbits, WDataInP const lwp, WDataInP const rwp) VL_PURE { + const int words = VL_WORDS_I(lbits); + int i = words - 1; + // We need to flip sense if negative comparison + const EData lsign = VL_SIGN_E(lbits, lwp[i]); + const EData rsign = VL_SIGN_E(lbits, rwp[i]); + if (!lsign && rsign) return 1; // + > - + if (lsign && !rsign) return -1; // - < + + for (; i >= 0; --i) { + if (lwp[i] > rwp[i]) return 1; + if (lwp[i] < rwp[i]) return -1; + } + return 0; // == +} + +//========================================================================= +// Expressions + +// Output NOT clean +static inline WDataOutP VL_NEGATE_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE { + EData carry = 1; + for (int i = 0; i < words; ++i) { + owp[i] = ~lwp[i] + carry; + carry = (owp[i] < ~lwp[i]); + } + return owp; +} +static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE { + EData carry = 1; + for (int i = 0; i < words; ++i) { + const EData word = ~owp_lwp[i] + carry; + carry = (word < ~owp_lwp[i]); + owp_lwp[i] = word; + } +} + +// EMIT_RULE: VL_MUL: oclean=dirty; lclean==clean; rclean==clean; +// EMIT_RULE: VL_DIV: oclean=dirty; lclean==clean; rclean==clean; +// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean; +static inline IData VL_DIV_III(int lbits, IData lhs, IData rhs) { + return (rhs == 0) ? 0 : lhs / rhs; +} +static inline QData VL_DIV_QQQ(int lbits, QData lhs, QData rhs) { + return (rhs == 0) ? 0 : lhs / rhs; +} +#define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0)) +static inline IData VL_MODDIV_III(int lbits, IData lhs, IData rhs) { + return (rhs == 0) ? 0 : lhs % rhs; +} +static inline QData VL_MODDIV_QQQ(int lbits, QData lhs, QData rhs) { + return (rhs == 0) ? 0 : lhs % rhs; +} +#define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1)) + +static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + QData carry = 0; + for (int i = 0; i < words; ++i) { + carry = carry + static_cast(lwp[i]) + static_cast(rwp[i]); + owp[i] = (carry & 0xffffffffULL); + carry = (carry >> 32ULL) & 0xffffffffULL; + } + // Last output word is dirty + return owp; +} + +static inline WDataOutP VL_SUB_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + QData carry = 0; + for (int i = 0; i < words; ++i) { + carry = (carry + static_cast(lwp[i]) + + static_cast(static_cast(~rwp[i]))); + if (i == 0) ++carry; // Negation of rwp + owp[i] = (carry & 0xffffffffULL); + carry = (carry >> 32ULL) & 0xffffffffULL; + } + // Last output word is dirty + return owp; +} + +static inline WDataOutP VL_MUL_W(int words, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 0; i < words; ++i) owp[i] = 0; + for (int lword = 0; lword < words; ++lword) { + for (int rword = 0; rword < words; ++rword) { + QData mul = static_cast(lwp[lword]) * static_cast(rwp[rword]); + for (int qword = lword + rword; qword < words; ++qword) { + mul += static_cast(owp[qword]); + owp[qword] = (mul & 0xffffffffULL); + mul = (mul >> 32ULL) & 0xffffffffULL; + } + } + } + // Last output word is dirty + return owp; +} + +static inline IData VL_MULS_III(int lbits, IData lhs, IData rhs) VL_PURE { + const int32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs); + const int32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs); + return lhs_signed * rhs_signed; +} +static inline QData VL_MULS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE { + const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs); + const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs); + return lhs_signed * rhs_signed; +} + +static inline WDataOutP VL_MULS_WWW(int lbits, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE { + const int words = VL_WORDS_I(lbits); + VL_DEBUG_IFDEF(assert(words <= VL_MULS_MAX_WORDS);); + // cppcheck-suppress variableScope + WData lwstore[VL_MULS_MAX_WORDS]; // Fixed size, as MSVC++ doesn't allow [words] here + // cppcheck-suppress variableScope + WData rwstore[VL_MULS_MAX_WORDS]; + WDataInP lwusp = lwp; + WDataInP rwusp = rwp; + const EData lneg = VL_SIGN_E(lbits, lwp[words - 1]); + if (lneg) { // Negate lhs + lwusp = lwstore; + VL_NEGATE_W(words, lwstore, lwp); + lwstore[words - 1] &= VL_MASK_E(lbits); // Clean it + } + const EData rneg = VL_SIGN_E(lbits, rwp[words - 1]); + if (rneg) { // Negate rhs + rwusp = rwstore; + VL_NEGATE_W(words, rwstore, rwp); + rwstore[words - 1] &= VL_MASK_E(lbits); // Clean it + } + VL_MUL_W(words, owp, lwusp, rwusp); + owp[words - 1] &= VL_MASK_E( + lbits); // Clean. Note it's ok for the multiply to overflow into the sign bit + if ((lneg ^ rneg) & 1) { // Negate output (not using NEGATE, as owp==lwp) + QData carry = 0; + for (int i = 0; i < words; ++i) { + carry = carry + static_cast(static_cast(~owp[i])); + if (i == 0) ++carry; // Negation of temp2 + owp[i] = (carry & 0xffffffffULL); + carry = (carry >> 32ULL) & 0xffffffffULL; + } + // Not needed: owp[words-1] |= 1< 0) power = power * power; + if (rhs & (1ULL << i)) out *= power; + } + return out; +} +static inline QData VL_POW_QQQ(int, int, int rbits, QData lhs, QData rhs) VL_PURE { + if (VL_UNLIKELY(rhs == 0)) return 1; + if (VL_UNLIKELY(lhs == 0)) return 0; + QData power = lhs; + QData out = 1ULL; + for (int i = 0; i < rbits; ++i) { + if (i > 0) power = power * power; + if (rhs & (1ULL << i)) out *= power; + } + return out; +} +WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp) VL_MT_SAFE; +WDataOutP VL_POW_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, + QData rhs) VL_MT_SAFE; +QData VL_POW_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp) VL_MT_SAFE; + +#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ + VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) +#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ + VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) +#define VL_POWSS_IIW(obits, lbits, rbits, lhs, rwp, lsign, rsign) \ + VL_POWSS_QQW(obits, lbits, rbits, lhs, rwp, lsign, rsign) +#define VL_POWSS_QQI(obits, lbits, rbits, lhs, rhs, lsign, rsign) \ + VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) +#define VL_POWSS_WWI(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign) \ + VL_POWSS_WWQ(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign) + +static inline IData VL_POWSS_III(int obits, int, int rbits, IData lhs, IData rhs, bool lsign, + bool rsign) VL_MT_SAFE { + if (VL_UNLIKELY(rhs == 0)) return 1; + if (rsign && VL_SIGN_I(rbits, rhs)) { + if (lhs == 0) { + return 0; // "X" + } else if (lhs == 1) { + return 1; + } else if (lsign && lhs == VL_MASK_I(obits)) { // -1 + if (rhs & 1) { + return VL_MASK_I(obits); // -1^odd=-1 + } else { + return 1; // -1^even=1 + } + } + return 0; + } + return VL_POW_III(obits, rbits, rbits, lhs, rhs); +} +static inline QData VL_POWSS_QQQ(int obits, int, int rbits, QData lhs, QData rhs, bool lsign, + bool rsign) VL_MT_SAFE { + if (VL_UNLIKELY(rhs == 0)) return 1; + if (rsign && VL_SIGN_Q(rbits, rhs)) { + if (lhs == 0) { + return 0; // "X" + } else if (lhs == 1) { + return 1; + } else if (lsign && lhs == VL_MASK_Q(obits)) { // -1 + if (rhs & 1) { + return VL_MASK_Q(obits); // -1^odd=-1 + } else { + return 1; // -1^even=1 + } + } + return 0; + } + return VL_POW_QQQ(obits, rbits, rbits, lhs, rhs); +} +WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, + WDataInP const rwp, bool lsign, bool rsign) VL_MT_SAFE; +WDataOutP VL_POWSS_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, QData rhs, + bool lsign, bool rsign) VL_MT_SAFE; +QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp, bool lsign, + bool rsign) VL_MT_SAFE; + +//=================================================================== +// Concat/replication + +// INTERNAL: Stuff LHS bit 0++ into OUTPUT at specified offset +// ld may be "dirty", output is clean +static inline void _vl_insert_II(CData& lhsr, IData ld, int hbit, int lbit, int rbits) VL_PURE { + const IData cleanmask = VL_MASK_I(rbits); + const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_II(SData& lhsr, IData ld, int hbit, int lbit, int rbits) VL_PURE { + const IData cleanmask = VL_MASK_I(rbits); + const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_II(IData& lhsr, IData ld, int hbit, int lbit, int rbits) VL_PURE { + const IData cleanmask = VL_MASK_I(rbits); + const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_QQ(QData& lhsr, QData ld, int hbit, int lbit, int rbits) VL_PURE { + const QData cleanmask = VL_MASK_Q(rbits); + const QData insmask = (VL_MASK_Q(hbit - lbit + 1)) << lbit; + lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask)); +} +static inline void _vl_insert_WI(WDataOutP iowp, IData ld, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { + // Insert value ld into iowp at bit slice [hbit:lbit]. iowp is rbits wide. + const int hoffset = VL_BITBIT_E(hbit); + const int loffset = VL_BITBIT_E(lbit); + const int roffset = VL_BITBIT_E(rbits); + const int hword = VL_BITWORD_E(hbit); + const int lword = VL_BITWORD_E(lbit); + const int rword = VL_BITWORD_E(rbits); + const EData cleanmask = hword == rword ? VL_MASK_E(roffset) : VL_MASK_E(0); + + if (hoffset == VL_SIZEBITS_E && loffset == 0) { + // Fast and common case, word based insertion + iowp[lword] = ld & cleanmask; + } else { + const EData lde = static_cast(ld); + if (hword == lword) { // know < EData bits because above checks it + // Assignment is contained within one word of destination + const EData insmask = (VL_MASK_E(hoffset - loffset + 1)) << loffset; + iowp[lword] = (iowp[lword] & ~insmask) | ((lde << loffset) & (insmask & cleanmask)); + } else { + // Assignment crosses a word boundary in destination + const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; + const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; + const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword + iowp[lword] = (iowp[lword] & ~linsmask) | ((lde << loffset) & linsmask); + // Prevent unsafe write where lword was final writable location and hword is + // out-of-bounds. + if (VL_LIKELY(!(hword == rword && roffset == 0))) { + iowp[hword] + = (iowp[hword] & ~hinsmask) | ((lde >> nbitsonright) & (hinsmask & cleanmask)); + } + } + } +} + +// Copy bits from lwp[hbit:lbit] to low bits of lhsr. rbits is real width of lshr +static inline void _vl_insert_IW(IData& lhsr, WDataInP const lwp, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { + const int hoffset = VL_BITBIT_E(hbit); + const int loffset = VL_BITBIT_E(lbit); + const int hword = VL_BITWORD_E(hbit); + const int lword = VL_BITWORD_E(lbit); + const IData cleanmask = VL_MASK_I(rbits); + if (hword == lword) { + const IData insmask = (VL_MASK_I(hoffset - loffset + 1)); + lhsr = (lhsr & ~insmask) | ((lwp[lword] >> loffset) & (insmask & cleanmask)); + } else { + const int nbitsonright = VL_IDATASIZE - loffset; // bits that filled by lword + const IData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << nbitsonright; + const IData linsmask = VL_MASK_E(VL_EDATASIZE - loffset); + lhsr = (lhsr & ~linsmask) | ((lwp[lword] >> loffset) & (linsmask & cleanmask)); + lhsr = (lhsr & ~hinsmask) | ((lwp[hword] << nbitsonright) & (hinsmask & cleanmask)); + } +} + +// INTERNAL: Stuff large LHS bit 0++ into OUTPUT at specified offset +// lwp may be "dirty" +static inline void _vl_insert_WW(WDataOutP iowp, WDataInP const lwp, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { + const int hoffset = VL_BITBIT_E(hbit); + const int loffset = VL_BITBIT_E(lbit); + const int roffset = VL_BITBIT_E(rbits); + const int lword = VL_BITWORD_E(lbit); + const int hword = VL_BITWORD_E(hbit); + const int rword = VL_BITWORD_E(rbits); + const int words = VL_WORDS_I(hbit - lbit + 1); + // Cleaning mask, only applied to top word of the assignment. Is a no-op + // if we don't assign to the top word of the destination. + const EData cleanmask = hword == rword ? VL_MASK_E(roffset) : VL_MASK_E(0); + + if (hoffset == VL_SIZEBITS_E && loffset == 0) { + // Fast and common case, word based insertion + for (int i = 0; i < (words - 1); ++i) iowp[lword + i] = lwp[i]; + iowp[hword] = lwp[words - 1] & cleanmask; + } else if (loffset == 0) { + // Non-32bit, but nicely aligned, so stuff all but the last word + for (int i = 0; i < (words - 1); ++i) iowp[lword + i] = lwp[i]; + // Know it's not a full word as above fast case handled it + const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)); + iowp[hword] = (iowp[hword] & ~hinsmask) | (lwp[words - 1] & (hinsmask & cleanmask)); + } else { + const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0; + const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset; + const int nbitsonright + = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) + // Middle words + for (int i = 0; i < words; ++i) { + { // Lower word + const int oword = lword + i; + const EData d = lwp[i] << loffset; + const EData od = (iowp[oword] & ~linsmask) | (d & linsmask); + if (oword == hword) { + iowp[oword] = (iowp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); + } else { + iowp[oword] = od; + } + } + { // Upper word + const int oword = lword + i + 1; + if (oword <= hword) { + const EData d = lwp[i] >> nbitsonright; + const EData od = (d & ~linsmask) | (iowp[oword] & linsmask); + if (oword == hword) { + iowp[oword] = (iowp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask)); + } else { + iowp[oword] = od; + } + } + } + } + } +} + +static inline void _vl_insert_WQ(WDataOutP iowp, QData ld, int hbit, int lbit, + int rbits = 0) VL_MT_SAFE { + VlWide lwp; + VL_SET_WQ(lwp, ld); + _vl_insert_WW(iowp, lwp, hbit, lbit, rbits); +} + +// EMIT_RULE: VL_REPLICATE: oclean=clean>width32, dirty<=width32; lclean=clean; rclean==clean; +// RHS MUST BE CLEAN CONSTANT. +#define VL_REPLICATE_IOI(lbits, ld, rep) (-(ld)) // Iff lbits==1 +#define VL_REPLICATE_QOI(lbits, ld, rep) (-(static_cast(ld))) // Iff lbits==1 + +static inline IData VL_REPLICATE_III(int lbits, IData ld, IData rep) VL_PURE { + IData returndata = ld; + for (unsigned i = 1; i < rep; ++i) { + returndata = returndata << lbits; + returndata |= ld; + } + return returndata; +} +static inline QData VL_REPLICATE_QII(int lbits, IData ld, IData rep) VL_PURE { + QData returndata = ld; + for (unsigned i = 1; i < rep; ++i) { + returndata = returndata << lbits; + returndata |= static_cast(ld); + } + return returndata; +} +static inline WDataOutP VL_REPLICATE_WII(int lbits, WDataOutP owp, IData ld, + IData rep) VL_MT_SAFE { + owp[0] = ld; + // Zeroing all words isn't strictly needed but allows compiler to know + // it does not need to preserve data in word(s) not being written + for (unsigned i = 1; i < VL_WORDS_I(static_cast(lbits) * rep); ++i) owp[i] = 0; + for (unsigned i = 1; i < rep; ++i) { + _vl_insert_WI(owp, ld, i * lbits + lbits - 1, i * lbits); + } + return owp; +} +static inline WDataOutP VL_REPLICATE_WQI(int lbits, WDataOutP owp, QData ld, + IData rep) VL_MT_SAFE { + VL_SET_WQ(owp, ld); + // Zeroing all words isn't strictly needed but allows compiler to know + // it does not need to preserve data in word(s) not being written + for (unsigned i = 2; i < VL_WORDS_I(static_cast(lbits) * rep); ++i) owp[i] = 0; + for (unsigned i = 1; i < rep; ++i) { + _vl_insert_WQ(owp, ld, i * lbits + lbits - 1, i * lbits); + } + return owp; +} +static inline WDataOutP VL_REPLICATE_WWI(int lbits, WDataOutP owp, WDataInP const lwp, + IData rep) VL_MT_SAFE { + for (unsigned i = 0; i < VL_WORDS_I(static_cast(lbits)); ++i) owp[i] = lwp[i]; + // Zeroing all words isn't strictly needed but allows compiler to know + // it does not need to preserve data in word(s) not being written + for (unsigned i = VL_WORDS_I(static_cast(lbits)); + i < VL_WORDS_I(static_cast(lbits * rep)); ++i) + owp[i] = 0; + for (unsigned i = 1; i < rep; ++i) { + _vl_insert_WW(owp, lwp, i * lbits + lbits - 1, i * lbits); + } + return owp; +} + +// Left stream operator. Output will always be clean. LHS and RHS must be clean. +// Special "fast" versions for slice sizes that are a power of 2. These use +// shifts and masks to execute faster than the slower for-loop approach where a +// subset of bits is copied in during each iteration. +static inline IData VL_STREAML_FAST_III(int lbits, IData ld, IData rd_log2) VL_PURE { + // Pre-shift bits in most-significant slice: + // + // If lbits is not a multiple of the slice size (i.e., lbits % rd != 0), + // then we end up with a "gap" in our reversed result. For example, if we + // have a 5-bit Verilog signal (lbits=5) in an 8-bit C data type: + // + // ld = ---43210 + // + // (where numbers are the Verilog signal bit numbers and '-' is an unused bit). + // Executing the switch statement below with a slice size of two (rd=2, + // rd_log2=1) produces: + // + // ret = 1032-400 + // + // Pre-shifting the bits in the most-significant slice allows us to avoid + // this gap in the shuffled data: + // + // ld_adjusted = --4-3210 + // ret = 10324--- + IData ret = ld; + if (rd_log2) { + const uint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); // max multiple of rd <= lbits + const uint32_t lbitsRem = lbits - lbitsFloor; // number of bits in most-sig slice (MSS) + const IData msbMask = lbitsFloor == 32 ? 0UL : VL_MASK_I(lbitsRem) << lbitsFloor; + ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem)); + } + switch (rd_log2) { + case 0: ret = ((ret >> 1) & VL_UL(0x55555555)) | ((ret & VL_UL(0x55555555)) << 1); // FALLTHRU + case 1: ret = ((ret >> 2) & VL_UL(0x33333333)) | ((ret & VL_UL(0x33333333)) << 2); // FALLTHRU + case 2: ret = ((ret >> 4) & VL_UL(0x0f0f0f0f)) | ((ret & VL_UL(0x0f0f0f0f)) << 4); // FALLTHRU + case 3: ret = ((ret >> 8) & VL_UL(0x00ff00ff)) | ((ret & VL_UL(0x00ff00ff)) << 8); // FALLTHRU + case 4: ret = ((ret >> 16) | (ret << 16)); // FALLTHRU + default:; + } + return ret >> (VL_IDATASIZE - lbits); +} + +static inline QData VL_STREAML_FAST_QQI(int lbits, QData ld, IData rd_log2) VL_PURE { + // Pre-shift bits in most-significant slice (see comment in VL_STREAML_FAST_III) + QData ret = ld; + if (rd_log2) { + const uint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2); + const uint32_t lbitsRem = lbits - lbitsFloor; + const QData msbMask = lbitsFloor == 64 ? 0ULL : VL_MASK_Q(lbitsRem) << lbitsFloor; + ret = (ret & ~msbMask) | ((ret & msbMask) << ((1ULL << rd_log2) - lbitsRem)); + } + switch (rd_log2) { + case 0: + ret = (((ret >> 1) & 0x5555555555555555ULL) + | ((ret & 0x5555555555555555ULL) << 1)); // FALLTHRU + case 1: + ret = (((ret >> 2) & 0x3333333333333333ULL) + | ((ret & 0x3333333333333333ULL) << 2)); // FALLTHRU + case 2: + ret = (((ret >> 4) & 0x0f0f0f0f0f0f0f0fULL) + | ((ret & 0x0f0f0f0f0f0f0f0fULL) << 4)); // FALLTHRU + case 3: + ret = (((ret >> 8) & 0x00ff00ff00ff00ffULL) + | ((ret & 0x00ff00ff00ff00ffULL) << 8)); // FALLTHRU + case 4: + ret = (((ret >> 16) & 0x0000ffff0000ffffULL) + | ((ret & 0x0000ffff0000ffffULL) << 16)); // FALLTHRU + case 5: ret = ((ret >> 32) | (ret << 32)); // FALLTHRU + default:; + } + return ret >> (VL_QUADSIZE - lbits); +} + +// Regular "slow" streaming operators +static inline IData VL_STREAML_III(int lbits, IData ld, IData rd) VL_PURE { + IData ret = 0; + // Slice size should never exceed the lhs width + const IData mask = VL_MASK_I(rd); + for (int istart = 0; istart < lbits; istart += rd) { + int ostart = lbits - rd - istart; + ostart = ostart > 0 ? ostart : 0; + ret |= ((ld >> istart) & mask) << ostart; + } + return ret; +} + +static inline QData VL_STREAML_QQI(int lbits, QData ld, IData rd) VL_PURE { + QData ret = 0; + // Slice size should never exceed the lhs width + const QData mask = VL_MASK_Q(rd); + for (int istart = 0; istart < lbits; istart += rd) { + int ostart = lbits - rd - istart; + ostart = ostart > 0 ? ostart : 0; + ret |= ((ld >> istart) & mask) << ostart; + } + return ret; +} + +static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const lwp, + IData rd) VL_MT_SAFE { + VL_ZERO_W(lbits, owp); + // Slice size should never exceed the lhs width + const int ssize = (rd < static_cast(lbits)) ? rd : (static_cast(lbits)); + for (int istart = 0; istart < lbits; istart += rd) { + int ostart = lbits - rd - istart; + ostart = ostart > 0 ? ostart : 0; + for (int sbit = 0; sbit < ssize && sbit < lbits - istart; ++sbit) { + // Extract a single bit from lwp and shift it to the correct + // location for owp. + const EData bit = (VL_BITRSHIFT_W(lwp, (istart + sbit)) & 1) + << VL_BITBIT_E(ostart + sbit); + owp[VL_BITWORD_E(ostart + sbit)] |= bit; + } + } + return owp; +} + +static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue& q) { + IData ret = 0; + for (size_t i = 0; i < q.size(); ++i) + ret |= static_cast(q.at(q.size() - 1 - i)) << (i * lbits); + return ret; +} + +static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue& q) { + IData ret = 0; + for (size_t i = 0; i < q.size(); ++i) + ret |= static_cast(q.at(q.size() - 1 - i)) << (i * lbits); + return ret; +} + +static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue& q) { + IData ret = 0; + for (size_t i = 0; i < q.size(); ++i) ret |= q.at(q.size() - 1 - i) << (i * lbits); + return ret; +} + +template +static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked& q) { + IData ret = 0; + for (size_t i = 0; i < N_Depth; ++i) + ret |= static_cast(q[N_Depth - 1 - i]) << (i * lbits); + return ret; +} + +template +static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked& q) { + IData ret = 0; + for (size_t i = 0; i < N_Depth; ++i) + ret |= static_cast(q[N_Depth - 1 - i]) << (i * lbits); + return ret; +} + +template +static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked& q) { + IData ret = 0; + for (size_t i = 0; i < N_Depth; ++i) ret |= q[N_Depth - 1 - i] << (i * lbits); + return ret; +} + +static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue& q) { + QData ret = 0; + for (size_t i = 0; i < q.size(); ++i) + ret |= static_cast(q.at(q.size() - 1 - i)) << (i * lbits); + return ret; +} + +static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue& q) { + QData ret = 0; + for (size_t i = 0; i < q.size(); ++i) + ret |= static_cast(q.at(q.size() - 1 - i)) << (i * lbits); + return ret; +} + +static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue& q) { + QData ret = 0; + for (size_t i = 0; i < q.size(); ++i) + ret |= static_cast(q.at(q.size() - 1 - i)) << (i * lbits); + return ret; +} + +template +static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked& q) { + QData ret = 0; + for (size_t i = 0; i < N_Depth; ++i) + ret |= static_cast(q[N_Depth - 1 - i]) << (i * lbits); + return ret; +} + +template +static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked& q) { + QData ret = 0; + for (size_t i = 0; i < N_Depth; ++i) + ret |= static_cast(q[N_Depth - 1 - i]) << (i * lbits); + return ret; +} + +template +static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked& q) { + QData ret = 0; + for (size_t i = 0; i < N_Depth; ++i) + ret |= static_cast(q[N_Depth - 1 - i]) << (i * lbits); + return ret; +} + +static inline QData VL_PACK_Q_RQ(int obits, int lbits, const VlQueue& q) { + QData ret = 0; + for (size_t i = 0; i < q.size(); ++i) ret |= q.at(q.size() - 1 - i) << (i * lbits); + return ret; +} + +template +static inline QData VL_PACK_Q_UQ(int obits, int lbits, const VlUnpacked& q) { + QData ret = 0; + for (size_t i = 0; i < N_Depth; ++i) ret |= q[N_Depth - 1 - i] << (i * lbits); + return ret; +} + +static inline WDataOutP VL_PACK_W_RI(int obits, int lbits, WDataOutP owp, + const VlQueue& q) { + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + if (VL_UNLIKELY(obits < q.size() * lbits)) return owp; // Though is illegal for q to be larger + const int offset = obits - q.size() * lbits; + for (size_t i = 0; i < q.size(); ++i) + _vl_insert_WI(owp, q.at(q.size() - i - 1), i * lbits + lbits - 1 + offset, + i * lbits + offset); + return owp; +} + +static inline WDataOutP VL_PACK_W_RI(int obits, int lbits, WDataOutP owp, + const VlQueue& q) { + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + if (VL_UNLIKELY(obits < q.size() * lbits)) return owp; // Though is illegal for q to be larger + const int offset = obits - q.size() * lbits; + for (size_t i = 0; i < q.size(); ++i) + _vl_insert_WI(owp, q.at(q.size() - i - 1), i * lbits + lbits - 1 + offset, + i * lbits + offset); + return owp; +} + +static inline WDataOutP VL_PACK_W_RI(int obits, int lbits, WDataOutP owp, + const VlQueue& q) { + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + if (VL_UNLIKELY(obits < q.size() * lbits)) return owp; // Though is illegal for q to be larger + const int offset = obits - q.size() * lbits; + for (size_t i = 0; i < q.size(); ++i) + _vl_insert_WI(owp, q.at(q.size() - 1 - i), i * lbits + lbits - 1 + offset, + i * lbits + offset); + return owp; +} + +template +static inline WDataOutP VL_PACK_W_UI(int obits, int lbits, WDataOutP owp, + const VlUnpacked& q) { + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + for (size_t i = 0; i < N_Depth; ++i) + _vl_insert_WI(owp, q[N_Depth - 1 - i], i * lbits + lbits - 1, i * lbits); + return owp; +} + +template +static inline WDataOutP VL_PACK_W_UI(int obits, int lbits, WDataOutP owp, + const VlUnpacked& q) { + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + for (size_t i = 0; i < N_Depth; ++i) + _vl_insert_WI(owp, q[N_Depth - 1 - i], i * lbits + lbits - 1, i * lbits); + return owp; +} + +template +static inline WDataOutP VL_PACK_W_UI(int obits, int lbits, WDataOutP owp, + const VlUnpacked& q) { + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + for (size_t i = 0; i < N_Depth; ++i) + _vl_insert_WI(owp, q[N_Depth - 1 - i], i * lbits + lbits - 1, i * lbits); + return owp; +} + +static inline WDataOutP VL_PACK_W_RQ(int obits, int lbits, WDataOutP owp, + const VlQueue& q) { + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + if (VL_UNLIKELY(obits < q.size() * lbits)) return owp; // Though is illegal for q to be larger + const int offset = obits - q.size() * lbits; + for (size_t i = 0; i < q.size(); ++i) + _vl_insert_WQ(owp, q.at(q.size() - 1 - i), i * lbits + lbits - 1 + offset, + i * lbits + offset); + return owp; +} + +template +static inline WDataOutP VL_PACK_W_UQ(int obits, int lbits, WDataOutP owp, + const VlUnpacked& q) { + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + for (size_t i = 0; i < N_Depth; ++i) + _vl_insert_WQ(owp, q[N_Depth - 1 - i], i * lbits + lbits - 1, i * lbits); + return owp; +} + +template +static inline WDataOutP VL_PACK_W_RW(int obits, int lbits, WDataOutP owp, + const VlQueue>& q) { + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + if (VL_UNLIKELY(obits < q.size() * lbits)) return owp; // Though is illegal for q to be larger + const int offset = obits - q.size() * lbits; + for (size_t i = 0; i < q.size(); ++i) + _vl_insert_WW(owp, q.at(q.size() - 1 - i), i * lbits + lbits - 1 + offset, + i * lbits + offset); + return owp; +} + +template +static inline WDataOutP VL_PACK_W_UW(int obits, int lbits, WDataOutP owp, + const VlUnpacked, N_Depth>& q) { + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + if (VL_UNLIKELY(obits < q.size() * lbits)) return owp; // Though is illegal for q to be larger + const int offset = obits - q.size() * lbits; + for (size_t i = 0; i < N_Depth; ++i) + _vl_insert_WW(owp, q[N_Depth - 1 - i], i * lbits + lbits - 1 + offset, i * lbits + offset); + return owp; +} + +// Because concats are common and wide, it's valuable to always have a clean output. +// Thus we specify inputs must be clean, so we don't need to clean the output. +// Note the bit shifts are always constants, so the adds in these constify out. +// Casts required, as args may be 8 bit entities, and need to shift to appropriate output size +#define VL_CONCAT_III(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QII(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QIQ(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QQI(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) +#define VL_CONCAT_QQQ(obits, lbits, rbits, ld, rd) \ + (static_cast(ld) << (rbits) | static_cast(rd)) + +static inline WDataOutP VL_CONCAT_WII(int obits, int lbits, int rbits, WDataOutP owp, IData ld, + IData rd) VL_MT_SAFE { + owp[0] = rd; + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + _vl_insert_WI(owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WWI(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, IData rd) VL_MT_SAFE { + owp[0] = rd; + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + _vl_insert_WW(owp, lwp, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WIW(int obits, int lbits, int rbits, WDataOutP owp, IData ld, + WDataInP const rwp) VL_MT_SAFE { + const int rwords = VL_WORDS_I(rbits); + VL_MEMCPY_W(owp, rwp, rwords); + VL_MEMSET_ZERO_W(owp + rwords, VL_WORDS_I(obits) - rwords); + _vl_insert_WI(owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WIQ(int obits, int lbits, int rbits, WDataOutP owp, IData ld, + QData rd) VL_MT_SAFE { + VL_SET_WQ(owp, rd); + VL_MEMSET_ZERO_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E); + _vl_insert_WI(owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WQI(int obits, int lbits, int rbits, WDataOutP owp, QData ld, + IData rd) VL_MT_SAFE { + owp[0] = rd; + VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1); + _vl_insert_WQ(owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WQQ(int obits, int lbits, int rbits, WDataOutP owp, QData ld, + QData rd) VL_MT_SAFE { + VL_SET_WQ(owp, rd); + VL_MEMSET_ZERO_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E); + _vl_insert_WQ(owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, QData rd) VL_MT_SAFE { + VL_SET_WQ(owp, rd); + VL_MEMSET_ZERO_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E); + _vl_insert_WW(owp, lwp, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WQW(int obits, int lbits, int rbits, WDataOutP owp, QData ld, + WDataInP const rwp) VL_MT_SAFE { + const int rwords = VL_WORDS_I(rbits); + VL_MEMCPY_W(owp, rwp, rwords); + VL_MEMSET_ZERO_W(owp + rwords, VL_WORDS_I(obits) - rwords); + _vl_insert_WQ(owp, ld, rbits + lbits - 1, rbits); + return owp; +} +static inline WDataOutP VL_CONCAT_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + const int rwords = VL_WORDS_I(rbits); + VL_MEMCPY_W(owp, rwp, rwords); + VL_MEMSET_ZERO_W(owp + rwords, VL_WORDS_I(obits) - rwords); + _vl_insert_WW(owp, lwp, rbits + lbits - 1, rbits); + return owp; +} + +//=================================================================== +// Shifts + +// Static shift, used by internal functions +// The output is the same as the input - it overlaps! +static inline void _vl_shiftl_inplace_w(int obits, WDataOutP iowp, + IData rd /*1 or 4*/) VL_MT_SAFE { + const int words = VL_WORDS_I(obits); + const EData linsmask = VL_MASK_E(rd); + for (int i = words - 1; i >= 1; --i) { + iowp[i] + = ((iowp[i] << rd) & ~linsmask) | ((iowp[i - 1] >> (VL_EDATASIZE - rd)) & linsmask); + } + iowp[0] = ((iowp[0] << rd) & ~linsmask); + iowp[VL_WORDS_I(obits) - 1] &= VL_MASK_E(obits); +} + +// EMIT_RULE: VL_SHIFTL: oclean=lclean; rclean==clean; +// Important: Unlike most other funcs, the shift might well be a computed +// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) +// If RHS (rd/rwp) is larger than the output, zeros (or all ones for >>>) must be returned +// (This corresponds to AstShift*Ovr Ast nodes) +static inline IData VL_SHIFTL_III(int obits, int, int, IData lhs, IData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; + return lhs << rhs; // Small is common so not clean return +} +static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; + return VL_CLEAN_II(obits, obits, lhs << rhs); +} +static inline QData VL_SHIFTL_QQI(int obits, int, int, QData lhs, IData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; + return lhs << rhs; // Small is common so not clean return +} +static inline QData VL_SHIFTL_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE { + if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; + return VL_CLEAN_QQ(obits, obits, lhs << rhs); +} +static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp, + IData rd) VL_MT_SAFE { + const int word_shift = VL_BITWORD_E(rd); + const int bit_shift = VL_BITBIT_E(rd); + if (rd >= static_cast(obits)) { // rd may be huge with MSB set + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } else if (bit_shift == 0) { // Aligned word shift (<<0,<<32,<<64 etc) + for (int i = 0; i < word_shift; ++i) owp[i] = 0; + for (int i = word_shift; i < VL_WORDS_I(obits); ++i) owp[i] = lwp[i - word_shift]; + } else { + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + _vl_insert_WW(owp, lwp, obits - 1, rd); + } + return owp; +} +static inline WDataOutP VL_SHIFTL_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return VL_ZERO_W(obits, owp); + } + } + return VL_SHIFTL_WWI(obits, lbits, 32, owp, lwp, rwp[0]); +} +static inline WDataOutP VL_SHIFTL_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, QData rd) VL_MT_SAFE { + VlWide rwp; + VL_SET_WQ(rwp, rd); + return VL_SHIFTL_WWW(obits, lbits, rbits, owp, lwp, rwp); +} +static inline IData VL_SHIFTL_IIW(int obits, int, int rbits, IData lhs, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return 0; + } + } + return VL_SHIFTL_III(obits, obits, 32, lhs, rwp[0]); +} +static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs, + WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return 0; + } + } + // Above checks rwp[1]==0 so not needed in below shift + return VL_SHIFTL_QQI(obits, obits, 32, lhs, rwp[0]); +} + +// EMIT_RULE: VL_SHIFTR: oclean=lclean; rclean==clean; +// Important: Unlike most other funcs, the shift might well be a computed +// expression. Thus consider this when optimizing. (And perhaps have 2 funcs?) +static inline IData VL_SHIFTR_III(int obits, int, int, IData lhs, IData rhs) VL_PURE { + if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; + return lhs >> rhs; +} +static inline IData VL_SHIFTR_IIQ(int obits, int, int, IData lhs, QData rhs) VL_PURE { + if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0; + return lhs >> rhs; +} +static inline QData VL_SHIFTR_QQI(int obits, int, int, QData lhs, IData rhs) VL_PURE { + if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; + return lhs >> rhs; +} +static inline QData VL_SHIFTR_QQQ(int obits, int, int, QData lhs, QData rhs) VL_PURE { + if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0; + return lhs >> rhs; +} +static inline WDataOutP VL_SHIFTR_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp, + IData rd) VL_MT_SAFE { + const int word_shift = VL_BITWORD_E(rd); // Maybe 0 + const int bit_shift = VL_BITBIT_E(rd); + if (rd >= static_cast(obits)) { // rd may be huge with MSB set + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } else if (bit_shift == 0) { // Aligned word shift (>>0,>>32,>>64 etc) + const int copy_words = (VL_WORDS_I(obits) - word_shift); + for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift]; + for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } else { + const int loffset = rd & VL_SIZEBITS_E; + const int nbitsonright = VL_EDATASIZE - loffset; // bits that end up in lword (know + // loffset!=0) Middle words + const int words = VL_WORDS_I(obits - rd); + for (int i = 0; i < words; ++i) { + owp[i] = lwp[i + word_shift] >> loffset; + const int upperword = i + word_shift + 1; + if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright; + } + for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } + return owp; +} +static inline WDataOutP VL_SHIFTR_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) { // Huge shift 1>>32 or more + return VL_ZERO_W(obits, owp); + } + } + return VL_SHIFTR_WWI(obits, lbits, 32, owp, lwp, rwp[0]); +} +static inline WDataOutP VL_SHIFTR_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, QData rd) VL_MT_SAFE { + VlWide rwp; + VL_SET_WQ(rwp, rd); + return VL_SHIFTR_WWW(obits, lbits, rbits, owp, lwp, rwp); +} + +static inline IData VL_SHIFTR_IIW(int obits, int, int rbits, IData lhs, + WDataInP const rwp) VL_PURE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) return 0; // Huge shift 1>>32 or more + } + return VL_SHIFTR_III(obits, obits, 32, lhs, rwp[0]); +} +static inline QData VL_SHIFTR_QQW(int obits, int, int rbits, QData lhs, + WDataInP const rwp) VL_PURE { + for (int i = 1; i < VL_WORDS_I(rbits); ++i) { + if (VL_UNLIKELY(rwp[i])) return 0; // Huge shift 1>>32 or more + } + return VL_SHIFTR_QQI(obits, obits, 32, lhs, rwp[0]); +} + +// EMIT_RULE: VL_SHIFTRS: oclean=false; lclean=clean, rclean==clean; +static inline IData VL_SHIFTRS_III(int obits, int lbits, int, IData lhs, IData rhs) VL_PURE { + // Note the C standard does not specify the >> operator as a arithmetic shift! + // IEEE says signed if output signed, but bit position from lbits; + // must use lbits for sign; lbits might != obits, + // an EXTEND(SHIFTRS(...)) can became a SHIFTRS(...) within same 32/64 bit word length + const IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative + if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return sign & VL_MASK_I(obits); + const IData signext = ~(VL_MASK_I(lbits) >> rhs); // One with bits where we've shifted "past" + return (lhs >> rhs) | (sign & VL_CLEAN_II(obits, obits, signext)); +} +static inline QData VL_SHIFTRS_QQI(int obits, int lbits, int, QData lhs, IData rhs) VL_PURE { + const QData sign = -(lhs >> (lbits - 1)); + if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return sign & VL_MASK_Q(obits); + const QData signext = ~(VL_MASK_Q(lbits) >> rhs); + return (lhs >> rhs) | (sign & VL_CLEAN_QQ(obits, obits, signext)); +} +static inline IData VL_SHIFTRS_IQI(int obits, int lbits, int rbits, QData lhs, IData rhs) VL_PURE { + return static_cast(VL_SHIFTRS_QQI(obits, lbits, rbits, lhs, rhs)); +} +static inline WDataOutP VL_SHIFTRS_WWI(int obits, int lbits, int, WDataOutP owp, + WDataInP const lwp, IData rd) VL_MT_SAFE { + const int word_shift = VL_BITWORD_E(rd); + const int bit_shift = VL_BITBIT_E(rd); + const int lmsw = VL_WORDS_I(obits) - 1; + const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]); + if (rd >= static_cast(obits)) { // Shifting past end, sign in all of lbits + for (int i = 0; i <= lmsw; ++i) owp[i] = sign; + owp[lmsw] &= VL_MASK_E(lbits); + } else if (bit_shift == 0) { // Aligned word shift (>>0,>>32,>>64 etc) + const int copy_words = (VL_WORDS_I(obits) - word_shift); + for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift]; + if (copy_words >= 0) owp[copy_words - 1] |= ~VL_MASK_E(obits) & sign; + for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = sign; + owp[lmsw] &= VL_MASK_E(lbits); + } else { + const int loffset = rd & VL_SIZEBITS_E; + const int nbitsonright + = VL_EDATASIZE - loffset; // bits that end up in lword (know loffset!=0) + // Middle words + const int words = VL_WORDS_I(obits - rd); + for (int i = 0; i < words; ++i) { + owp[i] = lwp[i + word_shift] >> loffset; + const int upperword = i + word_shift + 1; + if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright; + } + if (words) owp[words - 1] |= sign & ~VL_MASK_E(obits - loffset); + for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = sign; + owp[lmsw] &= VL_MASK_E(lbits); + } + return owp; +} +static inline WDataOutP VL_SHIFTRS_WWW(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE { + EData overshift = 0; // Huge shift 1>>32 or more + for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; + if (VL_UNLIKELY(overshift || rwp[0] >= static_cast(obits))) { + const int owords = VL_WORDS_I(obits); + if (VL_SIGN_E(lbits, lwp[owords - 1])) { + VL_MEMSET_ONES_W(owp, owords); + owp[owords - 1] &= VL_MASK_E(lbits); + } else { + VL_MEMSET_ZERO_W(owp, owords); + } + return owp; + } + return VL_SHIFTRS_WWI(obits, lbits, 32, owp, lwp, rwp[0]); +} +static inline WDataOutP VL_SHIFTRS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, + WDataInP const lwp, QData rd) VL_MT_SAFE { + VlWide rwp; + VL_SET_WQ(rwp, rd); + return VL_SHIFTRS_WWW(obits, lbits, rbits, owp, lwp, rwp); +} +static inline IData VL_SHIFTRS_IIW(int obits, int lbits, int rbits, IData lhs, + WDataInP const rwp) VL_PURE { + EData overshift = 0; // Huge shift 1>>32 or more + for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; + if (VL_UNLIKELY(overshift || rwp[0] >= static_cast(obits))) { + const IData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative + return VL_CLEAN_II(obits, obits, sign); + } + return VL_SHIFTRS_III(obits, lbits, 32, lhs, rwp[0]); +} +static inline QData VL_SHIFTRS_QQW(int obits, int lbits, int rbits, QData lhs, + WDataInP const rwp) VL_PURE { + EData overshift = 0; // Huge shift 1>>32 or more + for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i]; + if (VL_UNLIKELY(overshift || rwp[0] >= static_cast(obits))) { + const QData sign = -(lhs >> (lbits - 1)); // ffff_ffff if negative + return VL_CLEAN_QQ(obits, obits, sign); + } + return VL_SHIFTRS_QQI(obits, lbits, 32, lhs, rwp[0]); +} +static inline IData VL_SHIFTRS_IIQ(int obits, int lbits, int rbits, IData lhs, QData rhs) VL_PURE { + VlWide rwp; + VL_SET_WQ(rwp, rhs); + return VL_SHIFTRS_IIW(obits, lbits, rbits, lhs, rwp); +} +static inline QData VL_SHIFTRS_QQQ(int obits, int lbits, int rbits, QData lhs, QData rhs) VL_PURE { + VlWide rwp; + VL_SET_WQ(rwp, rhs); + return VL_SHIFTRS_QQW(obits, lbits, rbits, lhs, rwp); +} + +//=================================================================== +// Bit selection + +// EMIT_RULE: VL_BITSEL: oclean=dirty; rclean==clean; +#define VL_BITSEL_IIII(lbits, lhs, rhs) ((lhs) >> (rhs)) +#define VL_BITSEL_QIII(lbits, lhs, rhs) ((lhs) >> (rhs)) +#define VL_BITSEL_QQII(lbits, lhs, rhs) ((lhs) >> (rhs)) +#define VL_BITSEL_IQII(lbits, lhs, rhs) (static_cast((lhs) >> (rhs))) + +static inline IData VL_BITSEL_IWII(int lbits, WDataInP const lwp, IData rd) VL_MT_SAFE { + const int word = VL_BITWORD_E(rd); + if (VL_UNLIKELY(rd > static_cast(lbits))) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + // We return all 1's as that's more likely to find bugs (?) than 0's. + } else { + return (lwp[word] >> VL_BITBIT_E(rd)); + } +} + +// EMIT_RULE: VL_RANGE: oclean=lclean; out=dirty +// & MUST BE CLEAN (currently constant) +#define VL_SEL_IIII(lbits, lhs, lsb, width) ((lhs) >> (lsb)) +#define VL_SEL_QQII(lbits, lhs, lsb, width) ((lhs) >> (lsb)) +#define VL_SEL_IQII(lbits, lhs, lsb, width) (static_cast((lhs) >> (lsb))) + +static inline IData VL_SEL_IWII(int lbits, WDataInP const lwp, IData lsb, IData width) VL_MT_SAFE { + const int msb = lsb + width - 1; + if (VL_UNLIKELY(msb >= lbits)) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) { + return VL_BITRSHIFT_W(lwp, lsb); + } else { + // 32 bit extraction may span two words + const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); // bits that come from low word + return ((lwp[VL_BITWORD_E(msb)] << nbitsfromlow) | VL_BITRSHIFT_W(lwp, lsb)); + } +} + +static inline QData VL_SEL_QWII(int lbits, WDataInP const lwp, IData lsb, IData width) VL_MT_SAFE { + const int msb = lsb + width - 1; + if (VL_UNLIKELY(msb > lbits)) { + return ~0; // Spec says you can go outside the range of a array. Don't coredump if so. + } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) { + return VL_BITRSHIFT_W(lwp, lsb); + } else if (VL_BITWORD_E(msb) == 1 + VL_BITWORD_E(static_cast(lsb))) { + const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); + const QData hi = (lwp[VL_BITWORD_E(msb)]); + const QData lo = VL_BITRSHIFT_W(lwp, lsb); + return (hi << nbitsfromlow) | lo; + } else { + // 64 bit extraction may span three words + const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb); + const QData hi = (lwp[VL_BITWORD_E(msb)]); + const QData mid = (lwp[VL_BITWORD_E(lsb) + 1]); + const QData lo = VL_BITRSHIFT_W(lwp, lsb); + return (hi << (nbitsfromlow + VL_EDATASIZE)) | (mid << nbitsfromlow) | lo; + } +} + +static inline WDataOutP VL_SEL_WWII(int obits, int lbits, WDataOutP owp, WDataInP const lwp, + IData lsb, IData width) VL_MT_SAFE { + const int msb = lsb + width - 1; + const int word_shift = VL_BITWORD_E(lsb); + if (VL_UNLIKELY(msb > lbits)) { // Outside bounds, + for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) owp[i] = ~0; + owp[VL_WORDS_I(obits) - 1] = VL_MASK_E(obits); + } else if (VL_BITBIT_E(lsb) == 0) { + // Just a word extract + for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = lwp[i + word_shift]; + } else { + // Not a _vl_insert because the bits come from any bit number and goto bit 0 + const int loffset = lsb & VL_SIZEBITS_E; + const int nbitsfromlow = VL_EDATASIZE - loffset; // bits that end up in lword (know + // loffset!=0) Middle words + const int words = VL_WORDS_I(msb - lsb + 1); + for (int i = 0; i < words; ++i) { + owp[i] = lwp[i + word_shift] >> loffset; + const int upperword = i + word_shift + 1; + if (upperword <= static_cast(VL_BITWORD_E(msb))) { + owp[i] |= lwp[upperword] << nbitsfromlow; + } + } + for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0; + } + return owp; +} + +template +static inline VlQueue VL_CLONE_Q(const VlQueue& from, int lbits, int srcElementBits, + int dstElementBits) { + VlQueue ret; + VL_COPY_Q(ret, from, lbits, srcElementBits, dstElementBits); + return ret; +} + +template +static inline VlQueue VL_REVCLONE_Q(const VlQueue& from, int lbits, int srcElementBits, + int dstElementBits) { + VlQueue ret; + VL_REVCOPY_Q(ret, from, lbits, srcElementBits, dstElementBits); + return ret; +} + +// Helper function to get a bit from a queue at a specific bit index +template +static inline bool VL_GET_QUEUE_BIT(const VlQueue& queue, int srcElementBits, size_t bitIndex) { + const size_t elemIdx = bitIndex / srcElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return false; + + const T element = queue.at(elemIdx); + if (srcElementBits == 1) { + return element & 1; + } else { + const size_t bitInElem = bitIndex % srcElementBits; + const size_t actualBitPos = srcElementBits - 1 - bitInElem; + return (element >> actualBitPos) & 1; + } +} + +// Helper function to set a bit in the destination queue +template +static inline void VL_SET_QUEUE_BIT(VlQueue& queue, int dstElementBits, size_t bitIndex, + bool value) { + if (dstElementBits == 1) { + if (VL_UNLIKELY(bitIndex >= queue.size())) return; + queue.atWrite(bitIndex) = value ? 1 : 0; + } else { + const size_t elemIdx = bitIndex / dstElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return; + const size_t bitInElem = bitIndex % dstElementBits; + const size_t actualBitPos = dstElementBits - 1 - bitInElem; + if (value) { + queue.atWrite(elemIdx) |= (static_cast(1) << actualBitPos); + } else { + queue.atWrite(elemIdx) &= ~(static_cast(1) << actualBitPos); + } + } +} + +// Helper function to get a bit from a VlWide queue at a specific bit index +template +static inline bool VL_GET_QUEUE_BIT(const VlQueue>& queue, int srcElementBits, + size_t bitIndex) { + const size_t elemIdx = bitIndex / srcElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return false; + + const VlWide& element = queue.at(elemIdx); + const size_t bitInElem = bitIndex % srcElementBits; + const size_t actualBitPos = srcElementBits - 1 - bitInElem; + + return VL_BITISSET_W(element.data(), actualBitPos); +} + +// Helper function to set a bit in a VlWide queue at a specific bit index +template +static inline void VL_SET_QUEUE_BIT(VlQueue>& queue, int dstElementBits, + size_t bitIndex, bool value) { + const size_t elemIdx = bitIndex / dstElementBits; + if (VL_UNLIKELY(elemIdx >= queue.size())) return; + + const size_t bitInElem = bitIndex % dstElementBits; + const size_t actualBitPos = dstElementBits - 1 - bitInElem; + + VlWide& element = queue.atWrite(elemIdx); + if (value) { + VL_ASSIGNBIT_WO(actualBitPos, element.data()); + } else { + VL_ASSIGNBIT_WI(actualBitPos, element.data(), 0); + } +} + +template +static inline void VL_ZERO_INIT_QUEUE_ELEM(T& elem) { + elem = 0; +} + +template +static inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide& elem) { + for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; } +} + +// This specialization works for both VlQueue (and similar) as well +// as VlQueue>. +template +static inline void VL_COPY_Q(VlQueue& q, const VlQueue& from, int lbits, int srcElementBits, + int dstElementBits) { + if (srcElementBits == dstElementBits) { + // Simple case: same element bit width, direct copy of each element + if (VL_UNLIKELY(&q == &from)) return; // Skip self-assignment when it's truly a no-op + q = from; + } else { + // Different element bit widths: use streaming conversion + VlQueue srcCopy = from; + const size_t srcTotalBits = from.size() * srcElementBits; + const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; + q.renew(dstSize); + for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); } + for (size_t bitIndex = 0; bitIndex < srcTotalBits; ++bitIndex) { + VL_SET_QUEUE_BIT(q, dstElementBits, bitIndex, + VL_GET_QUEUE_BIT(srcCopy, srcElementBits, bitIndex)); + } + } +} + +// This specialization works for both VlQueue (and similar) as well +// as VlQueue>. +template +static inline void VL_REVCOPY_Q(VlQueue& q, const VlQueue& from, int lbits, + int srcElementBits, int dstElementBits) { + const size_t srcTotalBits = from.size() * srcElementBits; + const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits; + + // Always make a copy to handle the case where q and from are the same queue + VlQueue srcCopy = from; + + // Initialize all elements to zero using appropriate method + q.renew(dstSize); + for (size_t i = 0; i < dstSize; ++i) VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); + + if (lbits == 1) { + // Simple bit reversal: write directly to destination + for (int i = srcTotalBits - 1; i >= 0; --i) { + VL_SET_QUEUE_BIT(q, dstElementBits, srcTotalBits - 1 - i, + VL_GET_QUEUE_BIT(srcCopy, srcElementBits, i)); + } + } else { + // Generalized block-reversal for lbits > 1: + // 1. Reverse all bits using 1-bit blocks + // 2. Split into lbits-sized blocks and pad incomplete blocks on the left + // 3. Reverse each lbits-sized block using 1-bit blocks + const size_t numCompleteBlocks = srcTotalBits / lbits; + const size_t remainderBits = srcTotalBits % lbits; + const size_t srcBlocks = numCompleteBlocks + (remainderBits > 0 ? 1 : 0); + + size_t dstBitIndex = 0; + + for (size_t block = 0; block < srcBlocks; ++block) { + const size_t blockStart = block * lbits; + const int bitsToProcess = VL_LIKELY(block < numCompleteBlocks) ? lbits : remainderBits; + for (int bit = bitsToProcess - 1; bit >= 0; --bit) { + const size_t reversedBitIndex = blockStart + bit; + const size_t originalBitIndex = srcTotalBits - 1 - reversedBitIndex; + VL_SET_QUEUE_BIT(q, dstElementBits, dstBitIndex++, + VL_GET_QUEUE_BIT(srcCopy, srcElementBits, originalBitIndex)); + } + dstBitIndex += lbits - bitsToProcess; + } + } +} + +//====================================================================== +// Expressions needing insert/select + +static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) { + const size_t size = (rbits + lbits - 1) / lbits; + q.renew(size); + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; +} + +static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) { + const size_t size = (rbits + lbits - 1) / lbits; + q.renew(size); + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; +} + +static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) { + const size_t size = (rbits + lbits - 1) / lbits; + q.renew(size); + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; +} + +static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { + const size_t size = (rbits + lbits - 1) / lbits; + q.renew(size); + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; +} + +static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { + const size_t size = (rbits + lbits - 1) / lbits; + q.renew(size); + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; +} + +static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) { + const size_t size = (rbits + lbits - 1) / lbits; + q.renew(size); + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; +} + +static inline void VL_UNPACK_RQ_Q(int lbits, int rbits, VlQueue& q, QData from) { + const size_t size = (rbits + lbits - 1) / lbits; + q.renew(size); + const QData mask = VL_MASK_Q(lbits); + for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask; +} + +static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WDataInP rwp) { + const int size = (rbits + lbits - 1) / lbits; + q.renew(size); + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < size; ++i) { + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; + } +} + +static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WDataInP rwp) { + const int size = (rbits + lbits - 1) / lbits; + q.renew(size); + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < size; ++i) { + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; + } +} + +static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WDataInP rwp) { + const int size = (rbits + lbits - 1) / lbits; + q.renew(size); + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < size; ++i) { + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask; + } +} + +static inline void VL_UNPACK_RQ_W(int lbits, int rbits, VlQueue& q, WDataInP rwp) { + const int size = (rbits + lbits - 1) / lbits; + q.renew(size); + const QData mask = VL_MASK_Q(lbits); + for (size_t i = 0; i < size; ++i) { + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + q.atWrite(i) = VL_SEL_QWII(rbits, rwp, actualBitPos, actualWidth) & mask; + } +} + +template +static inline void VL_UNPACK_RW_W(int lbits, int rbits, VlQueue>& q, + WDataInP rwp) { + const int size = (rbits + lbits - 1) / lbits; + q.renew(size); + for (size_t i = 0; i < size; ++i) { + // Extract from MSB to LSB: MSB goes to index 0 + const int bitPos = rbits - (i + 1) * lbits; + const int actualBitPos = (bitPos < 0) ? 0 : bitPos; + const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits; + VL_SEL_WWII(actualWidth, rbits, q.atWrite(i), rwp, actualBitPos, actualWidth); + } +} + +template +static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked& q, + IData from) { + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask; +} + +template +static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked& q, + IData from) { + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask; +} + +template +static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked& q, + IData from) { + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask; +} + +template +static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked& q, + QData from) { + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask; +} + +template +static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked& q, + QData from) { + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask; +} + +template +static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked& q, + QData from) { + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask; +} + +template +static inline void VL_UNPACK_UQ_Q(int lbits, int rbits, VlUnpacked& q, + QData from) { + const QData mask = VL_MASK_Q(lbits); + for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask; +} + +template +static inline void VL_UNPACK_UI_W(int lbits, int rbits, VlUnpacked& q, + WDataInP rwp) { + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < N_Depth; ++i) + q[i] = VL_SEL_IWII(rbits, rwp, (N_Depth - 1 - i) * lbits, lbits) & mask; +} + +template +static inline void VL_UNPACK_UI_W(int lbits, int rbits, VlUnpacked& q, + WDataInP rwp) { + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < N_Depth; ++i) + q[i] = VL_SEL_IWII(rbits, rwp, (N_Depth - 1 - i) * lbits, lbits) & mask; +} + +template +static inline void VL_UNPACK_UI_W(int lbits, int rbits, VlUnpacked& q, + WDataInP rwp) { + const IData mask = VL_MASK_I(lbits); + for (size_t i = 0; i < N_Depth; ++i) + q[i] = VL_SEL_IWII(rbits, rwp, (N_Depth - 1 - i) * lbits, lbits) & mask; +} + +template +static inline void VL_UNPACK_UQ_W(int lbits, int rbits, VlUnpacked& q, + WDataInP rwp) { + const QData mask = VL_MASK_Q(lbits); + for (size_t i = 0; i < N_Depth; ++i) + q[i] = VL_SEL_QWII(rbits, rwp, (N_Depth - 1 - i) * lbits, lbits) & mask; +} + +template +static inline void VL_UNPACK_UW_W(int lbits, int rbits, VlUnpacked, N_Depth>& q, + WDataInP rwp) { + for (size_t i = 0; i < N_Depth; ++i) + VL_SEL_WWII(lbits, rbits, q[i], rwp, (N_Depth - 1 - i) * lbits, lbits); +} + +// Return QData from double (numeric) +// EMIT_RULE: VL_RTOIROUND_Q_D: oclean=dirty; lclean==clean/real +static inline QData VL_RTOIROUND_Q_D(double lhs) VL_PURE { + // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa + // This does not need to support subnormals as they are sub-integral + lhs = VL_ROUND(lhs); + if (lhs == 0.0) return 0; + const QData q = VL_CVT_Q_D(lhs); + const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52; + const uint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52); + uint64_t out = 0; + if (lsb < 0) { + out = mantissa >> -lsb; + } else if (lsb < 64) { + out = mantissa << lsb; + } + if (lhs < 0) out = -out; + return out; +} +static inline IData VL_RTOIROUND_I_D(double lhs) VL_PURE { + return static_cast(VL_RTOIROUND_Q_D(lhs)); +} +static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) VL_MT_SAFE { + // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa + // This does not need to support subnormals as they are sub-integral + lhs = VL_ROUND(lhs); + VL_ZERO_W(obits, owp); + if (lhs == 0.0) return owp; + const QData q = VL_CVT_Q_D(lhs); + const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52; + const uint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52); + if (lsb < 0) { + VL_SET_WQ(owp, mantissa >> -lsb); + } else if (lsb < obits) { + _vl_insert_WQ(owp, mantissa, lsb + 52, lsb); + } + if (lhs < 0) VL_NEGATE_INPLACE_W(VL_WORDS_I(obits), owp); + return owp; +} + +//====================================================================== +// Range assignments + +// EMIT_RULE: VL_ASSIGNRANGE: rclean=dirty; +static inline void VL_ASSIGNSEL_II(int rbits, int obits, int lsb, CData& lhsr, IData rhs) VL_PURE { + _vl_insert_II(lhsr, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_II(int rbits, int obits, int lsb, SData& lhsr, IData rhs) VL_PURE { + _vl_insert_II(lhsr, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_II(int rbits, int obits, int lsb, IData& lhsr, IData rhs) VL_PURE { + _vl_insert_II(lhsr, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_QI(int rbits, int obits, int lsb, QData& lhsr, IData rhs) VL_PURE { + _vl_insert_QQ(lhsr, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_QQ(int rbits, int obits, int lsb, QData& lhsr, QData rhs) VL_PURE { + _vl_insert_QQ(lhsr, rhs, lsb + obits - 1, lsb, rbits); +} +// static inline void VL_ASSIGNSEL_IIIW(int obits, int lsb, IData& lhsr, WDataInP const rwp) +// VL_MT_SAFE { Illegal, as lhs width >= rhs width +static inline void VL_ASSIGNSEL_WI(int rbits, int obits, int lsb, WDataOutP iowp, + IData rhs) VL_MT_SAFE { + _vl_insert_WI(iowp, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_WQ(int rbits, int obits, int lsb, WDataOutP iowp, + QData rhs) VL_MT_SAFE { + _vl_insert_WQ(iowp, rhs, lsb + obits - 1, lsb, rbits); +} +static inline void VL_ASSIGNSEL_WW(int rbits, int obits, int lsb, WDataOutP iowp, + WDataInP const rwp) VL_MT_SAFE { + _vl_insert_WW(iowp, rwp, lsb + obits - 1, lsb, rbits); +} + +//==================================================== +// Range assignments + +// These additional functions copy bits range [obis+roffset-1:roffset] from rhs to lower bits +// of lhs(select before assigning). Rhs should always be wider than lhs. +static inline void VL_SELASSIGN_II(int rbits, int obits, CData& lhsr, IData rhs, + int roffset) VL_PURE { + _vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits); +} +static inline void VL_SELASSIGN_II(int rbits, int obits, SData& lhsr, IData rhs, + int roffset) VL_PURE { + _vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits); +} +static inline void VL_SELASSIGN_II(int rbits, int obits, IData& lhsr, IData rhs, + int roffset) VL_PURE { + _vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits); +} +static inline void VL_SELASSIGN_IQ(int rbits, int obits, CData& lhsr, QData rhs, + int roffset) VL_PURE { + // it will be truncated to right CData mask + const CData cleanmask = VL_MASK_I(rbits); + const CData insmask = VL_MASK_I(obits); + lhsr = (lhsr & ~insmask) | (static_cast(rhs >> roffset) & (insmask & cleanmask)); +} +static inline void VL_SELASSIGN_IQ(int rbits, int obits, SData& lhsr, QData rhs, + int roffset) VL_PURE { + // it will be truncated to right CData mask + const SData cleanmask = VL_MASK_I(rbits); + const SData insmask = VL_MASK_I(obits); + lhsr = (lhsr & ~insmask) | (static_cast(rhs >> roffset) & (insmask & cleanmask)); +} +static inline void VL_SELASSIGN_IQ(int rbits, int obits, IData& lhsr, QData rhs, + int roffset) VL_PURE { + const IData cleanmask = VL_MASK_I(rbits); + const IData insmask = VL_MASK_I(obits); + lhsr = (lhsr & ~insmask) | (static_cast(rhs >> roffset) & (insmask & cleanmask)); +} + +static inline void VL_SELASSIGN_QQ(int rbits, int obits, QData& lhsr, QData rhs, + int roffset) VL_PURE { + _vl_insert_QQ(lhsr, rhs >> roffset, obits - 1, 0, rbits); +} + +static inline void VL_SELASSIGN_IW(int rbits, int obits, CData& lhsr, WDataInP const rhs, + int roffset) VL_MT_SAFE { + IData l = static_cast(lhsr); + _vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits); + lhsr = static_cast(l); +} +static inline void VL_SELASSIGN_IW(int rbits, int obits, SData& lhsr, WDataInP const rhs, + int roffset) VL_MT_SAFE { + IData l = static_cast(lhsr); + _vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits); + lhsr = static_cast(l); +} +static inline void VL_SELASSIGN_IW(int rbits, int obits, IData& lhsr, WDataInP const rhs, + int roffset) VL_MT_SAFE { + _vl_insert_IW(lhsr, rhs, roffset + obits - 1, roffset, rbits); +} +static inline void VL_SELASSIGN_QW(int rbits, int obits, QData& lhsr, WDataInP const rhs, + int roffset) VL_MT_SAFE { + // assert VL_QDATASIZE >= rbits > VL_IDATASIZE; + IData low = static_cast(lhsr); + IData high = static_cast(lhsr >> VL_IDATASIZE); + if (obits <= VL_IDATASIZE) { + _vl_insert_IW(low, rhs, obits + roffset - 1, roffset, VL_IDATASIZE); + } else { + _vl_insert_IW(low, rhs, roffset + VL_IDATASIZE - 1, roffset, VL_IDATASIZE); + _vl_insert_IW(high, rhs, roffset + obits - 1, roffset + VL_IDATASIZE, + rbits - VL_IDATASIZE); + } + lhsr = (static_cast(high) << VL_IDATASIZE) | low; +} + +static inline void VL_SELASSIGN_WW(int rbits, int obits, WDataOutP iowp, WDataInP const rwp, + int roffset) VL_MT_SAFE { + // assert rbits > VL_QDATASIZE + const int wordoff = roffset / VL_EDATASIZE; + const int lsb = roffset & VL_SIZEBITS_E; + const int upperbits = lsb == 0 ? 0 : VL_EDATASIZE - lsb; + // If roffset is not aligned, we copy some bits to align it. + if (lsb != 0) { + const int w = obits < upperbits ? obits : upperbits; + const int insmask = VL_MASK_E(w); + iowp[0] = (iowp[0] & ~insmask) | ((rwp[wordoff] >> lsb) & insmask); + // cppcheck-suppress knownConditionTrueFalse + if (w == obits) return; + obits -= w; + } + _vl_insert_WW(iowp, rwp + wordoff + (lsb != 0), upperbits + obits - 1, upperbits, rbits); +} + +//====================================================================== +// Triops + +static inline WDataOutP VL_COND_WIWW(int obits, WDataOutP owp, int cond, WDataInP const w1p, + WDataInP const w2p) VL_MT_SAFE { + return VL_MEMCPY_W(owp, cond ? w1p : w2p, VL_WORDS_I(obits)); +} + +//====================================================================== +// Constification + +// VL_CONST_W_#X(int obits, WDataOutP owp, IData data0, .... IData data(#-1)) +// Sets wide vector words to specified constant words. +// These macros are used when o might represent more words then are given as constants, +// hence all upper words must be zeroed. +// If changing the number of functions here, also change EMITCINLINES_NUM_CONSTW + +#define VL_C_END_(obits, wordsSet) \ + VL_MEMSET_ZERO_W(o + (wordsSet), VL_WORDS_I(obits) - (wordsSet)); \ + return o + +// clang-format off +static inline WDataOutP VL_CONST_W_1X(int obits, WDataOutP o, EData d0) VL_MT_SAFE { + o[0] = d0; + VL_C_END_(obits, 1); +} +static inline WDataOutP VL_CONST_W_2X(int obits, WDataOutP o, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; + VL_C_END_(obits, 2); +} +static inline WDataOutP VL_CONST_W_3X(int obits, WDataOutP o, EData d2, EData d1, + EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; + VL_C_END_(obits, 3); +} +static inline WDataOutP VL_CONST_W_4X(int obits, WDataOutP o, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + VL_C_END_(obits, 4); +} +static inline WDataOutP VL_CONST_W_5X(int obits, WDataOutP o, + EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; + VL_C_END_(obits, 5); +} +static inline WDataOutP VL_CONST_W_6X(int obits, WDataOutP o, + EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; o[5] = d5; + VL_C_END_(obits, 6); +} +static inline WDataOutP VL_CONST_W_7X(int obits, WDataOutP o, + EData d6, EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; o[5] = d5; o[6] = d6; + VL_C_END_(obits, 7); +} +static inline WDataOutP VL_CONST_W_8X(int obits, WDataOutP o, + EData d7, EData d6, EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; + o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; + VL_C_END_(obits, 8); +} +// +static inline WDataOutP VL_CONSTHI_W_1X(int obits, int lsb, WDataOutP o, + EData d0) VL_MT_SAFE { + WDataOutP ohi = o + VL_WORDS_I(lsb); + ohi[0] = d0; + VL_C_END_(obits, VL_WORDS_I(lsb) + 1); +} +static inline WDataOutP VL_CONSTHI_W_2X(int obits, int lsb, WDataOutP o, + EData d1, EData d0) VL_MT_SAFE { + WDataOutP ohi = o + VL_WORDS_I(lsb); + ohi[0] = d0; ohi[1] = d1; + VL_C_END_(obits, VL_WORDS_I(lsb) + 2); +} +static inline WDataOutP VL_CONSTHI_W_3X(int obits, int lsb, WDataOutP o, + EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP ohi = o + VL_WORDS_I(lsb); + ohi[0] = d0; ohi[1] = d1; ohi[2] = d2; + VL_C_END_(obits, VL_WORDS_I(lsb) + 3); +} +static inline WDataOutP VL_CONSTHI_W_4X(int obits, int lsb, WDataOutP o, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP ohi = o + VL_WORDS_I(lsb); + ohi[0] = d0; ohi[1] = d1; ohi[2] = d2; ohi[3] = d3; + VL_C_END_(obits, VL_WORDS_I(lsb) + 4); +} +static inline WDataOutP VL_CONSTHI_W_5X(int obits, int lsb, WDataOutP o, + EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP ohi = o + VL_WORDS_I(lsb); + ohi[0] = d0; ohi[1] = d1; ohi[2] = d2; ohi[3] = d3; + ohi[4] = d4; + VL_C_END_(obits, VL_WORDS_I(lsb) + 5); +} +static inline WDataOutP VL_CONSTHI_W_6X(int obits, int lsb, WDataOutP o, + EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP ohi = o + VL_WORDS_I(lsb); + ohi[0] = d0; ohi[1] = d1; ohi[2] = d2; ohi[3] = d3; + ohi[4] = d4; ohi[5] = d5; + VL_C_END_(obits, VL_WORDS_I(lsb) + 6); +} +static inline WDataOutP VL_CONSTHI_W_7X(int obits, int lsb, WDataOutP o, + EData d6, EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP ohi = o + VL_WORDS_I(lsb); + ohi[0] = d0; ohi[1] = d1; ohi[2] = d2; ohi[3] = d3; + ohi[4] = d4; ohi[5] = d5; ohi[6] = d6; + VL_C_END_(obits, VL_WORDS_I(lsb) + 7); +} +static inline WDataOutP VL_CONSTHI_W_8X(int obits, int lsb, WDataOutP o, + EData d7, EData d6, EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP ohi = o + VL_WORDS_I(lsb); + ohi[0] = d0; ohi[1] = d1; ohi[2] = d2; ohi[3] = d3; + ohi[4] = d4; ohi[5] = d5; ohi[6] = d6; ohi[7] = d7; + VL_C_END_(obits, VL_WORDS_I(lsb) + 8); +} + +#undef VL_C_END_ + +// Partial constant, lower words of vector wider than 8*32, starting at bit number lsb +static inline void VL_CONSTLO_W_8X(int lsb, WDataOutP obase, + EData d7, EData d6, EData d5, EData d4, + EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE { + WDataOutP o = obase + VL_WORDS_I(lsb); + o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7; +} +// clang-format on + +//====================================================================== +// Strings + +extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE; +extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE; +extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE; + +inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE { + // SystemVerilog does not allow a string variable to contain '\0'. + // So C functions such as strcmp() can correctly compare strings. + if (ignoreCase) { + return VL_STRCASECMP(lhs.c_str(), rhs.c_str()); + } else { + return std::strcmp(lhs.c_str(), rhs.c_str()); + } +} + +extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE; +extern IData VL_NTOI_I(int obits, const std::string& str) VL_PURE; +extern QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE; +extern void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str) VL_PURE; + +extern IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE; + +//====================================================================== +// Dist functions + +extern IData VL_DIST_CHI_SQUARE(IData& seedr, IData udeg_of_free) VL_MT_SAFE; +extern IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE; +extern IData VL_DIST_EXPONENTIAL(IData& seedr, IData umean) VL_MT_SAFE; +extern IData VL_DIST_NORMAL(IData& seedr, IData umean, IData udeviation) VL_MT_SAFE; +extern IData VL_DIST_POISSON(IData& seedr, IData umean) VL_MT_SAFE; +extern IData VL_DIST_T(IData& seedr, IData udeg_of_free) VL_MT_SAFE; +extern IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE; + +//====================================================================== +// Conversion functions + +extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE; +extern std::string VL_CVT_PACK_STR_ND(const VlQueue& q) VL_PURE; +inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE { + VlWide lw; + VL_SET_WQ(lw, lhs); + return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw); +} +inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; } +inline std::string& VL_CVT_PACK_STR_NN(std::string& lhs) VL_PURE { return lhs; } +inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE { + VlWide lw; + VL_SET_WI(lw, lhs); + return VL_CVT_PACK_STR_NW(1, lw); +} +inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE { + return lhs + rhs; +} +inline std::string VL_REPLICATEN_NNQ(const std::string& lhs, IData rep) VL_PURE { + std::string result; + result.reserve(lhs.length() * rep); + for (unsigned times = 0; times < rep; ++times) result += lhs; + return result; +} +inline std::string VL_REPLICATEN_NNI(const std::string& lhs, IData rep) VL_PURE { + return VL_REPLICATEN_NNQ(lhs, rep); +} + +inline IData VL_LEN_IN(const std::string& ld) { return static_cast(ld.length()); } +extern std::string VL_TOLOWER_NN(const std::string& ld) VL_PURE; +extern std::string VL_TOUPPER_NN(const std::string& ld) VL_PURE; + +extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE; +extern IData VL_FERROR_IW(IData fpi, int obits, WDataOutP outwp) VL_MT_SAFE; +extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE; +extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE; +extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb, + const std::string& filename, void* memp, QData start, + QData end) VL_MT_SAFE; +extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb, + const std::string& filename, const void* memp, QData start, + QData end) VL_MT_SAFE; +extern IData VL_SSCANF_INNX(int lbits, const std::string& ld, const std::string& format, int argc, + ...) VL_MT_SAFE; +extern void VL_SFORMAT_NX(int obits_ignored, std::string& output, const std::string& format, + int argc, ...) VL_MT_SAFE; +extern std::string VL_SFORMATF_N_NX(const std::string& format, int argc, ...) VL_MT_SAFE; +extern void VL_TIMEFORMAT_IINI(bool hasUnits, int units, bool hasPrecision, int precision, + bool hasSuffix, const std::string& suffix, bool hasWidth, int width, + VerilatedContext* contextp) VL_MT_SAFE; +extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE; +inline IData VL_VALUEPLUSARGS_IND(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE { + VlWide<2> rwp; + const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp)); + return got; +} +inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE { + VlWide<2> rwp; + const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = rwp[0]; + return got; +} +inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE { + VlWide<2> rwp; + const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = rwp[0]; + return got; +} +inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE { + VlWide<2> rwp; + const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = rwp[0]; + return got; +} +inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE { + VlWide<2> rwp; + const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = VL_SET_QW(rwp); + return got; +} +inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE { + VlWide<2> rwp; + const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp); + if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp)); + return got; +} +extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE; + +uint64_t VL_MURMUR64_HASH(const char* key) VL_PURE; + +//====================================================================== + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_imp.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_imp.h new file mode 100644 index 00000000000..6ee4e580e37 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_imp.h @@ -0,0 +1,616 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2009-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//========================================================================= +/// +/// \file +/// \brief Verilated implementation Header, only for verilated.cpp internals. +/// +/// This file is not part of the Verilated public-facing API. +/// It is only for internal use by the Verilated libraries. +/// +//========================================================================= + +#ifndef VERILATOR_VERILATED_IMP_H_ +#define VERILATOR_VERILATED_IMP_H_ + +// clang-format off +#if !defined(VERILATOR_VERILATED_CPP_) && !defined(VERILATOR_VERILATED_DPI_CPP_) \ + && !defined(VERILATOR_VERILATED_VPI_CPP_) && !defined(VERILATOR_VERILATED_SAVE_CPP_) +# error "verilated_imp.h only to be included by verilated*.cpp internals" +#endif + +#include "verilatedos.h" +#include "verilated.h" +#include "verilated_syms.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// clang-format on + +class VerilatedScope; + +//====================================================================== +// Threaded message passing + +// Message, enqueued on an mtask, and consumed on the main eval thread +class VerilatedMsg final { +public: + // TYPES + struct Cmp final { + bool operator()(const VerilatedMsg& a, const VerilatedMsg& b) const { + return a.mtaskId() < b.mtaskId(); + } + }; + +private: + // MEMBERS + uint32_t m_mtaskId; // MTask that did enqueue + std::function m_cb; // Lambda to execute when message received +public: + // CONSTRUCTORS + explicit VerilatedMsg(const std::function& cb) + : m_mtaskId{Verilated::mtaskId()} + , m_cb{cb} {} + ~VerilatedMsg() = default; + VerilatedMsg(const VerilatedMsg&) = default; + VerilatedMsg(VerilatedMsg&&) = default; + VerilatedMsg& operator=(const VerilatedMsg&) = default; + VerilatedMsg& operator=(VerilatedMsg&&) = default; + // METHODS + uint32_t mtaskId() const { return m_mtaskId; } + // Execute the lambda function + void run() const { m_cb(); } +}; + +// Each thread has a queue it pushes to +// This assumes no thread starts pushing the next tick until the previous has drained. +// If more aggressiveness is needed, a double-buffered scheme might work well. +class VerilatedEvalMsgQueue final { + using VerilatedThreadQueue = std::multiset; + + std::atomic m_depth; // Current depth of queue (see comments below) + + mutable VerilatedMutex m_mutex; // Mutex protecting queue + VerilatedThreadQueue m_queue VL_GUARDED_BY(m_mutex); // Message queue +public: + // CONSTRUCTORS + VerilatedEvalMsgQueue() + : m_depth{0} { + assert(atomic_is_lock_free(&m_depth)); + } + ~VerilatedEvalMsgQueue() = default; + +private: + VL_UNCOPYABLE(VerilatedEvalMsgQueue); + +public: + // METHODS + // Add message to queue (called by producer) + void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + m_queue.insert(msg); // Pass by value to copy the message into queue + ++m_depth; + } + // Service queue until completion (called by consumer) + void process() VL_MT_SAFE_EXCLUDES(m_mutex) { + // Tracking m_depth is redundant to e.g. getting the mutex and looking at queue size, + // but on the reader side it's 4x faster to test an atomic then getting a mutex + while (m_depth) { + // Wait for a message to be added to the queue + // We don't use unique_lock as want to unlock with the message copy still in scope + m_mutex.lock(); + assert(!m_queue.empty()); // Otherwise m_depth is wrong + // Unfortunately to release the lock we need to copy the message + // (Or have the message be a pointer, but then new/delete cost on each message) + // We assume messages are small, so copy + const auto it = m_queue.begin(); + const VerilatedMsg msg = *(it); + m_queue.erase(it); + m_mutex.unlock(); + --m_depth; // Ok if outside critical section as only this code checks the value + { + VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from mtaskId=%d\n", msg.mtaskId());); + msg.run(); + } + } + } +}; + +// Each thread has a local queue to build up messages until the end of the eval() call +class VerilatedThreadMsgQueue final { + std::queue m_queue; + +public: + // CONSTRUCTORS + VerilatedThreadMsgQueue() = default; + ~VerilatedThreadMsgQueue() = default; + // The only call of destructor with a non-empty queue is a fatal error. + // So this does not flush the queue, as the destination queue is not known to this class. + +private: + VL_UNCOPYABLE(VerilatedThreadMsgQueue); + // METHODS + static VerilatedThreadMsgQueue& threadton() VL_MT_SAFE { + static thread_local VerilatedThreadMsgQueue t_s; + return t_s; + } + +public: + // Add message to queue, called by producer + static void post(const VerilatedMsg& msg) VL_MT_SAFE { + // Handle calls to threaded routines outside + // of any mtask -- if an initial block calls $finish, say. + if (Verilated::mtaskId() == 0) { + // No queueing, just do the action immediately + msg.run(); + } else { + Verilated::endOfEvalReqdInc(); + threadton().m_queue.push(msg); // Pass by value to copy the message into queue + } + } + // Push all messages to the eval's queue + static void flush(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE { + while (!threadton().m_queue.empty()) { + evalMsgQp->post(threadton().m_queue.front()); + threadton().m_queue.pop(); + Verilated::endOfEvalReqdDec(); + } + } +}; + +// FILE* list constructed from a file-descriptor +class VerilatedFpList final { + FILE* m_fp[31] = {}; + std::size_t m_sz = 0; + +public: + using const_iterator = FILE* const*; + explicit VerilatedFpList() = default; + const_iterator begin() const { return m_fp; } + const_iterator end() const { return m_fp + m_sz; } + std::size_t size() const { return m_sz; } + static std::size_t capacity() { return 31; } + void push_back(FILE* fd) { + if (VL_LIKELY(size() < capacity())) m_fp[m_sz++] = fd; + } +}; + +//====================================================================== +// VerilatedContextImpData + +// Class for hidden implementation members inside VerilatedContext +// Avoids needing std::unordered_map inside verilated.h +class VerilatedContextImpData final { + friend class VerilatedContext; + friend class VerilatedContextImp; + +protected: + // Map of + // Used by scopeInsert, scopeFind, scopeErase, scopeNameMap + mutable VerilatedMutex m_nameMutex; // Protect m_nameMap + VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex); +}; + +//====================================================================== +// VerilatedContextImp +// Class to "add" implementation-only methods to VerilatedContext + +class VerilatedContextImp final : VerilatedContext { + friend class VerilatedContext; + + // MEMBERS - non-static not allowed, use only VerilatedContext + // Select initial value of otherwise uninitialized signals. + // Internal note: Globals may multi-construct, see verilated.cpp top. + + // Medium speed, so uses singleton accessing + struct Statics final { + VerilatedMutex s_randMutex; // Mutex protecting s_randSeedEpoch + // Number incrementing on each reseed, 0=illegal + int s_randSeedEpoch = 1; // Reads ok, wish had a VL_WRITE_GUARDED_BY(s_randMutex) + }; + static Statics& s() VL_MT_SAFE { + static Statics s_s; + return s_s; + } + +public: // But only for verilated*.cpp + // CONSTRUCTORS - no data can live here, use only VerilatedContext + VerilatedContextImp() = delete; + ~VerilatedContextImp() = delete; + + // METHODS - extending into VerilatedContext, call via impp()-> + + // Random seed handling + uint64_t randSeedDefault64() const VL_MT_SAFE; + static uint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; } + + // METHODS - timeformat + int timeFormatUnits() const VL_MT_SAFE { + if (m_s.m_timeFormatUnits == VerilatedContext::Serialized::UNITS_NONE) + return timeprecision(); + return m_s.m_timeFormatUnits; + } + void timeFormatUnits(int value) VL_MT_SAFE { m_s.m_timeFormatUnits = value; } + int timeFormatPrecision() const VL_MT_SAFE { return m_s.m_timeFormatPrecision; } + void timeFormatPrecision(int value) VL_MT_SAFE { m_s.m_timeFormatPrecision = value; } + int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; } + void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; } + std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { + const VerilatedLockGuard lock{m_timeDumpMutex}; + return m_timeFormatSuffix; + } + void timeFormatSuffix(const std::string& value) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) { + const VerilatedLockGuard lock{m_timeDumpMutex}; + m_timeFormatSuffix = value; + } + + // METHODS - arguments + std::string argPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex); + std::pair argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex); + + // METHODS - scope name - INTERNAL only for verilated*.cpp + void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE; + void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE; + + // METHODS - file IO - INTERNAL only for verilated*.cpp + + IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock{m_fdMutex}; + if (m_fdFreeMct.empty()) return 0; + const IData idx = m_fdFreeMct.back(); + m_fdFreeMct.pop_back(); + m_fdps[idx] = std::fopen(filenamep, "w"); + if (VL_UNLIKELY(!m_fdps[idx])) return 0; + return (1 << idx); + } + IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + FILE* const fp = std::fopen(filenamep, modep); + if (VL_UNLIKELY(!fp)) return 0; + // Bit 31 indicates it's a descriptor not a MCD + const VerilatedLockGuard lock{m_fdMutex}; + if (m_fdFree.empty()) { + // Need to create more space in m_fdps and m_fdFree + const std::size_t start = std::max(31UL + 1UL + 3UL, m_fdps.size()); + const std::size_t excess = 10; + m_fdps.resize(start + excess); + std::fill(m_fdps.begin() + start, m_fdps.end(), static_cast(nullptr)); + m_fdFree.resize(excess); + for (std::size_t i = 0, id = start; i < m_fdFree.size(); ++i, ++id) { + m_fdFree[i] = static_cast(id); + } + } + const IData idx = m_fdFree.back(); + m_fdFree.pop_back(); + m_fdps[idx] = fp; + return (idx | (1UL << 31)); // bit 31 indicates not MCD + } + void fdFlush(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock{m_fdMutex}; + const VerilatedFpList fdlist = fdToFpList(fdi); + for (const auto& i : fdlist) std::fflush(i); + } + IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock{m_fdMutex}; + const VerilatedFpList fdlist = fdToFpList(fdi); + if (VL_UNLIKELY(fdlist.size() != 1)) return ~0U; // -1 + return static_cast( + std::fseek(*fdlist.begin(), static_cast(offset), static_cast(origin))); + } + IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock{m_fdMutex}; + const VerilatedFpList fdlist = fdToFpList(fdi); + if (VL_UNLIKELY(fdlist.size() != 1)) return ~0U; // -1 + return static_cast(std::ftell(*fdlist.begin())); + } + void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock{m_fdMutex}; + const VerilatedFpList fdlist = fdToFpList(fdi); + for (const auto& i : fdlist) { + if (VL_UNLIKELY(!i)) continue; + (void)fwrite(output.c_str(), 1, output.size(), i); + } + } + void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock{m_fdMutex}; + if (VL_BITISSET_I(fdi, 31)) { + // Non-MCD case + const IData idx = VL_MASK_I(31) & fdi; + if (VL_UNLIKELY(idx >= m_fdps.size())) return; + if (VL_UNLIKELY(idx <= 2)) return; // stdout/stdin/stderr + if (VL_UNLIKELY(!m_fdps[idx])) return; // Already free + std::fclose(m_fdps[idx]); + m_fdps[idx] = nullptr; + m_fdFree.push_back(idx); + } else { + // MCD case + // Starts at 1 to skip stdout + fdi >>= 1; + for (int i = 1; (fdi != 0) && (i < 31); i++, fdi >>= 1) { + if (fdi & VL_MASK_I(1)) { + std::fclose(m_fdps[i]); + m_fdps[i] = nullptr; + m_fdFreeMct.push_back(i); + } + } + } + } + FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) { + const VerilatedLockGuard lock{m_fdMutex}; + const VerilatedFpList fdlist = fdToFpList(fdi); + if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr; + return *fdlist.begin(); + } + +private: + VerilatedFpList fdToFpList(IData fdi) VL_REQUIRES(m_fdMutex) { + VerilatedFpList fp; + // cppverilator-suppress integerOverflow shiftTooManyBitsSigned + if (VL_BITISSET_I(fdi, 31)) { + // Non-MCD case + const IData idx = fdi & VL_MASK_I(31); + switch (idx) { + case 0: fp.push_back(stdin); break; + case 1: fp.push_back(stdout); break; + case 2: fp.push_back(stderr); break; + default: + if (VL_LIKELY(idx < m_fdps.size())) fp.push_back(m_fdps[idx]); + break; + } + } else { + // MCD Case + if (fdi & 1) fp.push_back(stdout); + fdi >>= 1; + for (size_t i = 1; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) { + if (fdi & VL_MASK_I(1)) fp.push_back(m_fdps[i]); + } + } + return fp; + } + +protected: + // METHODS - protected + void commandArgsGuts(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex); + void commandArgsAddGutsLock(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex); + void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex); + void commandArgVl(const std::string& arg); + bool commandArgVlString(const std::string& arg, const std::string& prefix, + std::string& valuer); + bool commandArgVlUint64(const std::string& arg, const std::string& prefix, uint64_t& valuer, + uint64_t min = std::numeric_limits::min(), + uint64_t max = std::numeric_limits::max()); + void commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex); +}; + +//====================================================================== +// VerilatedImp + +class VerilatedImpData final { + // Whole class is internal use only - Global information shared between verilated*.cpp files. + // All only medium-speed, so use singleton function +protected: + friend class Verilated; + friend class VerilatedImp; + + // TYPES + using UserMap = std::map, void*>; + using ExportNameMap = std::map; + + // MEMBERS + // Nothing below here is save-restored; users expected to re-register appropriately + + VerilatedMutex m_userMapMutex; // Protect m_userMap + // For userInsert, userFind. As indexed by pointer is common across contexts. + UserMap m_userMap VL_GUARDED_BY(m_userMapMutex); // Map of <(scope,userkey), userData> + + VerilatedMutex m_hierMapMutex; // Protect m_hierMap + // Map that represents scope hierarchy + // Used by hierarchyAdd, hierarchyRemove, hierarchyMap + VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex); + + // Slow - somewhat static: + VerilatedMutex m_exportMutex; // Protect m_nameMap + // Map of + // Used by exportInsert, exportFind, exportName. + // Export numbers same across all contexts as just a string-to-number conversion + ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex); + int m_exportNext VL_GUARDED_BY(m_exportMutex) = 0; // Next export funcnum + + // No guard, as init-time loaded + std::vector m_exportFlatCbs; // Exports when only single scope registered + std::vector m_exportFlatMulti; // Multiple scopes registerd; cannot use m_exportScopes + + // CONSTRUCTORS + VerilatedImpData() = default; +}; + +class VerilatedImp final { + // Whole class is internal use only - Global information shared between verilated*.cpp files. +protected: + friend class Verilated; + + // MEMBERS + static VerilatedImpData& s() VL_MT_SAFE { // Singleton + static VerilatedImpData s_s; + return s_s; + } + +public: // But only for verilated*.cpp + // CONSTRUCTORS + VerilatedImp() = default; + ~VerilatedImp() = default; + +private: + VL_UNCOPYABLE(VerilatedImp); + +public: + // METHODS - debug + static void versionDump() VL_MT_SAFE; + + // METHODS - user scope tracking + // We implement this as a single large map instead of one map per scope. + // There's often many more scopes than userdata's and thus having a ~48byte + // per map overhead * N scopes would take much more space and cache thrashing. + // As scopep's are pointers, this implicitly handles multiple Context's + static void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE { + const VerilatedLockGuard lock{s().m_userMapMutex}; + const auto it = s().m_userMap.find(std::make_pair(scopep, userKey)); + if (it != s().m_userMap.end()) { + it->second = userData; + } else { + s().m_userMap.emplace(std::make_pair(scopep, userKey), userData); + } + } + static void* userFind(const void* scopep, void* userKey) VL_MT_SAFE { + const VerilatedLockGuard lock{s().m_userMapMutex}; + const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey)); + if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr; + return it->second; + } + + // METHODS - But only for verilated.cpp + + // Symbol table destruction cleans up the entries for each scope. + static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE { + // Slow ok - called once/scope on destruction, so we only iterate. + const VerilatedLockGuard lock{s().m_userMapMutex}; + for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) { + if (it->first.first == scopep) { + s().m_userMap.erase(it++); + } else { + ++it; + } + } + } + static void userDump() VL_MT_SAFE { + const VerilatedLockGuard lock{s().m_userMapMutex}; // Avoid it changing in middle of dump + bool first = true; + for (const auto& i : s().m_userMap) { + if (first) { + VL_PRINTF_MT(" userDump:\n"); + first = false; + } + VL_PRINTF_MT(" DPI_USER_DATA scope %p key %p: %p\n", i.first.first, i.first.second, + i.second); + } + } + + // METHODS - hierarchy - only for verilated*.cpp + static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE { + // Slow ok - called at construction for VPI accessible elements + const VerilatedLockGuard lock{s().m_hierMapMutex}; + s().m_hierMap[fromp].push_back(top); + } + static void hierarchyRemove(const VerilatedScope* fromp, + const VerilatedScope* top) VL_MT_SAFE { + // Slow ok - called at destruction for VPI accessible elements + const VerilatedLockGuard lock{s().m_hierMapMutex}; + VerilatedHierarchyMap& map = s().m_hierMap; + if (map.find(fromp) == map.end()) return; + auto& scopes = map[fromp]; + const auto it = find(scopes.begin(), scopes.end(), top); + if (it != scopes.end()) scopes.erase(it); + } + static void hierarchyClear() VL_MT_SAFE { + const VerilatedLockGuard lock{s().m_hierMapMutex}; + VerilatedHierarchyMap& map = s().m_hierMap; + map.clear(); + } + static const VerilatedHierarchyMap* hierarchyMap() VL_MT_SAFE_POSTINIT { + // Thread save only assuming this is called only after model construction completed + return &s().m_hierMap; + } + + // METHODS - export names - only for verilated*.cpp + + // Each function prototype is converted to a function number which we + // then use to index a 2D table also indexed by scope number, because we + // can't know at Verilation time what scopes will exist in other modules + // in the design that also happen to have our same callback function. + // Rather than a 2D map, the integer scheme saves 500ish ns on a likely + // miss at the cost of a multiply, and all lookups move to slowpath. +private: + static int exportInsertName(const char* namep) VL_MT_SAFE { + const VerilatedLockGuard lock{s().m_exportMutex}; + const auto it = s().m_exportMap.find(namep); + if (it == s().m_exportMap.end()) { + s().m_exportMap.emplace(namep, s().m_exportNext++); + return s().m_exportNext++; + } else { + return it->second; + } + } + +public: + static int exportInsert(const char* namep, void* cb) VL_MT_SAFE { + const int funcnum = VerilatedImp::exportInsertName(namep); + const VerilatedLockGuard lock{s().m_exportMutex}; + // Slow ok - called once/function at creation + if (funcnum >= s().m_exportFlatCbs.size()) { + s().m_exportFlatCbs.resize(funcnum + 1); + s().m_exportFlatMulti.resize(funcnum + 1); + } + if (!s().m_exportFlatMulti[funcnum]) { + if (s().m_exportFlatCbs[funcnum] == cb) { // Duplicate + } else if (!s().m_exportFlatCbs[funcnum]) { // First + s().m_exportFlatCbs[funcnum] = cb; + } else { // Multiple registrants + s().m_exportFlatCbs[funcnum] = nullptr; + s().m_exportFlatMulti[funcnum] = true; + } + } + return funcnum; + } + static int exportFindNum(const char* namep) VL_MT_SAFE { + const VerilatedLockGuard lock{s().m_exportMutex}; + const auto& it = s().m_exportMap.find(namep); + if (VL_LIKELY(it != s().m_exportMap.end())) return it->second; + const std::string msg = "%Error: Testbench C called "s + namep + + " but no such DPI export function name exists in ANY model"; + VL_FATAL_MT("unknown", 0, "", msg.c_str()); + return -1; + } + static const std::vector& exportFlatCbs() VL_MT_SAFE { return s().m_exportFlatCbs; } + static const char* exportName(int funcnum) VL_MT_SAFE { + // Slowpath; find name for given export; errors only so no map to reverse-map it + const VerilatedLockGuard lock{s().m_exportMutex}; + for (const auto& i : s().m_exportMap) { + if (i.second == funcnum) return i.first; + } + return "*UNKNOWN*"; + } + static void exportsDump() VL_MT_SAFE { + const VerilatedLockGuard lock{s().m_exportMutex}; + bool first = true; + for (const auto& i : s().m_exportMap) { + if (first) { + VL_PRINTF_MT(" exportDump:\n"); + first = false; + } + VL_PRINTF_MT(" DPI_EXPORT_NAME %05d: %s\n", i.second, i.first); + } + } + // We don't free up m_exportMap until the end, because we can't be sure + // what other models are using the assigned funcnum's. +}; + +//====================================================================== + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_intrinsics.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_intrinsics.h new file mode 100644 index 00000000000..6e6f568f319 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_intrinsics.h @@ -0,0 +1,45 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +/// +/// \file +/// \brief Verilator common target-specific intrinsics header +/// +/// This file is not part of the Verilated public-facing API. +/// +/// It is only for internal use; code using machine-specific intrinsics for +/// optimization should include this header rather than directly including +/// the target-specific headers. We provide macros to check for availability +/// of instruction sets, and a common mechanism to disable them. +/// +//************************************************************************* + +#ifndef VERILATOR_VERILATED_INTRINSICS_H_ +#define VERILATOR_VERILATED_INTRINSICS_H_ + +// clang-format off + +// Use VL_PORTABLE_ONLY to disable all intrinsics based optimization +#ifndef VL_PORTABLE_ONLY +# if defined(__SSE2__) && !defined(VL_DISABLE_SSE2) +# define VL_HAVE_SSE2 1 +# include +# endif +# if defined(__AVX2__) && defined(VL_HAVE_SSE2) && !defined(VL_DISABLE_AVX2) +# define VL_HAVE_AVX2 1 +# include +# endif +#endif + +// clang-format on + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_probdist.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_probdist.cpp new file mode 100644 index 00000000000..e2b75d3c74a --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_probdist.cpp @@ -0,0 +1,240 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//========================================================================= +/// +/// \file +/// \brief Verilated probability distribution implementation code +/// +/// Verilator always adds this file to the Makefile for the linker. +/// +/// Those macro/function/variable starting or ending in _ are internal, +/// however many of the other function/macros here are also internal. +/// +//========================================================================= + +#include "verilated_config.h" +#include "verilatedos.h" + +#include "verilated.h" + +//=========================================================================== +// Dist + +static double _vl_dbase_uniform(IData& seedr, int32_t start, int32_t end) VL_MT_SAFE { + union u_s { + float s; + unsigned stemp; + } u; + + const double d = 0.00000011920928955078125; + if (VL_UNLIKELY(seedr == 0)) seedr = 259341593; + + double a; + double b; + if (VL_UNCOVERABLE(start >= end)) { // With current usage shound't occur + a = 0.0; // LCOV_EXCL_LINE + b = 2147483647.0; // LCOV_EXCL_LINE + } else { + a = static_cast(start); + b = static_cast(end); + } + seedr = 69069 * seedr + 1; + u.stemp = seedr; + u.stemp = (u.stemp >> 9) | 0x3f800000; + + double c = static_cast(u.s); + c = c + (c * d); + c = ((b - a) * (c - 1.0)) + a; + return c; +} + +static double _vl_dbase_normal(IData& seedr, int32_t mean, int32_t deviation) VL_MT_SAFE { + double v1 = 0.0; + double v2 = 0.0; + double s = 1.0; + while ((s >= 1.0) || (s == 0.0)) { + v1 = _vl_dbase_uniform(seedr, -1, 1); + v2 = _vl_dbase_uniform(seedr, -1, 1); + s = v1 * v1 + v2 * v2; + } + s = v1 * std::sqrt(-2.0 * log(s) / s); + v1 = static_cast(deviation); + v2 = static_cast(mean); + return (s * v1 + v2); +} + +static double _vl_dbase_exponential(IData& seedr, int32_t mean) VL_MT_SAFE { + double n = _vl_dbase_uniform(seedr, 0, 1); + if (n != 0) n = -log(n) * mean; + return n; +} + +static double _vl_dbase_chi_square(IData& seedr, int32_t deg_of_free) VL_MT_SAFE { + double x; + if (deg_of_free % 2) { + x = _vl_dbase_normal(seedr, 0, 1); + x = x * x; + } else { + x = 0.0; + } + for (int32_t k = 2; k <= deg_of_free; k += 2) x = x + 2 * _vl_dbase_exponential(seedr, 1); + return x; +} + +IData VL_DIST_CHI_SQUARE(IData& seedr, IData udf) VL_MT_SAFE { + const int32_t df = static_cast(udf); + if (VL_UNLIKELY(df <= 0)) { + // Chi_square distribution must have positive degree of freedom + return 0; + } + double r = _vl_dbase_chi_square(seedr, df); + int32_t i; + if (r >= 0) { + i = static_cast(r + 0.5); + } else { + r = -r; // LCOV_EXCL_LINE + i = static_cast(r + 0.5); // LCOV_EXCL_LINE + i = -i; // LCOV_EXCL_LINE + } + return static_cast(i); +} + +IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE { + const int32_t k = static_cast(uk); + const int32_t mean = static_cast(umean); + if (VL_UNLIKELY(k <= 0)) { + // k-stage erlangian distribution must have positive k + return 0; + } + double x = 1.0; + for (int32_t i = 1; i <= k; ++i) x = x * _vl_dbase_uniform(seedr, 0, 1); + const double a = static_cast(mean); + const double b = static_cast(k); + double r = -a * log(x) / b; + int32_t i; + if (r >= 0) { + i = static_cast(r + 0.5); + } else { + r = -r; + i = static_cast(r + 0.5); + i = -i; + } + return static_cast(i); +} + +IData VL_DIST_EXPONENTIAL(IData& seedr, IData umean) VL_MT_SAFE { + const int32_t mean = static_cast(umean); + if (VL_UNLIKELY(mean <= 0)) { + // Exponential distribution must have a positive mean + return 0; + } + int32_t i; + double r = _vl_dbase_exponential(seedr, mean); + if (r >= 0) { + i = static_cast(r + 0.5); + } else { + r = -r; // LCOV_EXCL_LINE + i = static_cast(r + 0.5); // LCOV_EXCL_LINE + i = -i; // LCOV_EXCL_LINE + } + return static_cast(i); +} + +IData VL_DIST_NORMAL(IData& seedr, IData umean, IData usd) VL_MT_SAFE { + const int32_t mean = static_cast(umean); + const int32_t sd = static_cast(usd); + double r = _vl_dbase_normal(seedr, mean, sd); + int32_t i; + if (r >= 0) { + i = static_cast(r + 0.5); + } else { + r = -r; + i = static_cast(r + 0.5); + i = -i; + } + return static_cast(i); +} + +IData VL_DIST_POISSON(IData& seedr, IData umean) VL_MT_SAFE { + const int32_t mean = static_cast(umean); + if (VL_UNLIKELY(mean <= 0)) { + // Poisson distribution must have a positive mean + return 0; + } + int32_t i = 0; + double q = -static_cast(mean); + double p = exp(q); + q = _vl_dbase_uniform(seedr, 0, 1); + while (p < q) { + ++i; + q = _vl_dbase_uniform(seedr, 0, 1) * q; + } + return static_cast(i); +} + +IData VL_DIST_T(IData& seedr, IData udf) VL_MT_SAFE { + const int32_t df = static_cast(udf); + if (VL_UNLIKELY(df <= 0)) { + // t distribution must have positive degree of freedom + return 0; + } + const double chi2 = _vl_dbase_chi_square(seedr, df); + const double div = chi2 / static_cast(df); + const double root = std::sqrt(div); + double r = _vl_dbase_normal(seedr, 0, 1) / root; + int32_t i; + if (r >= 0) { + i = static_cast(r + 0.5); + } else { + r = -r; + i = static_cast(r + 0.5); + i = -i; + } + return static_cast(i); +} + +IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE { + int32_t start = static_cast(ustart); + int32_t end = static_cast(uend); + if (VL_UNLIKELY(start >= end)) return start; + int32_t i; + if (end != std::numeric_limits::max()) { + ++end; + const double r = _vl_dbase_uniform(seedr, start, end); + if (r >= 0) { + i = static_cast(r); + } else { + i = static_cast(r - 1); + } + if (i < start) i = start; + if (i >= end) i = end - 1; + } else if (start != std::numeric_limits::min()) { + --start; + const double r = _vl_dbase_uniform(seedr, start, end) + 1.0; + if (r >= 0) { + i = static_cast(r); + } else { + i = static_cast(r - 1); // LCOV_EXCL_LINE + } + if (i <= start) i = start + 1; + if (i > end) i = end; + } else { + double r = (_vl_dbase_uniform(seedr, start, end) + 2147483648.0) / 4294967295.0; + r = r * 4294967296.0 - 2147483648.0; + if (r >= 0) { + i = static_cast(r); + } else { + i = static_cast(r - 1); + } + } + return static_cast(i); +} diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.cpp new file mode 100644 index 00000000000..621482bf05f --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.cpp @@ -0,0 +1,225 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2012-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated run-time profiling implementation code +/// +//============================================================================= + +#include "verilatedos.h" + +#include "verilated_profiler.h" + +#include "verilated_threads.h" + +#include +#include + +//============================================================================= +// Globals + +// Internal note: Globals may multi-construct, see verilated.cpp top. + +thread_local VlExecutionProfiler::ExecutionTrace VlExecutionProfiler::t_trace; + +constexpr const char* const VlExecutionRecord::s_ascii[]; + +//============================================================================= +// VlExecutionProfiler implementation + +template +static size_t roundUptoMultipleOf(size_t value) { + static_assert((N & (N - 1)) == 0, "'N' must be a power of 2"); + size_t mask = N - 1; + return (value + mask) & ~mask; +} + +VlExecutionProfiler::VlExecutionProfiler(VerilatedContext& context) + : m_context{context} { + // Setup profiling on main thread + setupThread(0); +} + +void VlExecutionProfiler::configure() { + + if (VL_UNLIKELY(m_enabled)) { + --m_windowCount; + if (VL_UNLIKELY(m_windowCount == m_context.profExecWindow())) { + VL_DEBUG_IF(VL_DBG_MSGF("+ profile start collection\n");); + clear(); // Clear the profile after the cache warm-up cycles. + m_tickBegin = VL_CPU_TICK(); + } else if (VL_UNLIKELY(m_windowCount == 0)) { + const uint64_t tickEnd = VL_CPU_TICK(); + VL_DEBUG_IF(VL_DBG_MSGF("+ profile end\n");); + const std::string& fileName = m_context.profExecFilename(); + dump(fileName.c_str(), tickEnd); + m_enabled = false; + } + return; + } + + const uint64_t startReq = m_context.profExecStart() + 1; // + 1, so we can start at time 0 + + if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= m_context.profExecStart())) { + VL_DEBUG_IF(VL_DBG_MSGF("+ profile start warmup\n");); + VL_DEBUG_IF(assert(m_windowCount == 0);); + m_enabled = true; + m_windowCount = m_context.profExecWindow() * 2; + m_lastStartReq = startReq; + } +} + +VerilatedVirtualBase* VlExecutionProfiler::construct(VerilatedContext& context) { + VlExecutionProfiler* const selfp = new VlExecutionProfiler{context}; + if (VlThreadPool* const threadPoolp = static_cast(context.threadPoolp())) { + for (int i = 0; i < threadPoolp->numThreads(); ++i) { + // Data to pass to worker thread initialization + struct Data final { + VlExecutionProfiler* const selfp; + const uint32_t threadId; + } data{selfp, static_cast(i + 1)}; + + // Initialize worker thread + threadPoolp->workerp(i)->addTask( + [](void* userp, bool) { + Data* const datap = static_cast(userp); + datap->selfp->setupThread(datap->threadId); + }, + &data); + + // Wait until initialization is complete + threadPoolp->workerp(i)->wait(); + } + } + return selfp; +} + +void VlExecutionProfiler::setupThread(uint32_t threadId) { + // Reserve some space in the thread-local profiling buffer, in order to try to avoid malloc + // while profiling. + t_trace.reserve(RESERVED_TRACE_CAPACITY); + // Register thread-local buffer in list of all buffers + bool exists; + { + const VerilatedLockGuard lock{m_mutex}; + exists = !m_traceps.emplace(threadId, &t_trace).second; + } + if (VL_UNLIKELY(exists)) { + VL_FATAL_MT(__FILE__, __LINE__, "", "multiple initialization of profiler on some thread"); + } +} + +void VlExecutionProfiler::clear() VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + for (const auto& pair : m_traceps) { + ExecutionTrace* const tracep = pair.second; + const size_t reserve = roundUptoMultipleOf(tracep->size()); + tracep->clear(); + tracep->reserve(reserve); + } +} + +void VlExecutionProfiler::dump(const char* filenamep, uint64_t tickEnd) + VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + VL_DEBUG_IF(VL_DBG_MSGF("+prof+exec writing to '%s'\n", filenamep);); + + FILE* const fp = std::fopen(filenamep, "w"); + if (VL_UNLIKELY(!fp)) VL_FATAL_MT(filenamep, 0, "", "+prof+exec+file file not writable"); + + // TODO Perhaps merge with verilated_coverage output format, so can + // have a common merging and reporting tool, etc. + fprintf(fp, "VLPROFVERSION 2.2 # Verilator execution profile version 2.2\n"); + fprintf(fp, "VLPROF arg +verilator+prof+exec+start+%" PRIu64 "\n", + Verilated::threadContextp()->profExecStart()); + fprintf(fp, "VLPROF arg +verilator+prof+exec+window+%u\n", + Verilated::threadContextp()->profExecWindow()); + std::string numa = "no threads"; + if (const VlThreadPool* const threadPoolp + = static_cast(Verilated::threadContextp()->threadPoolp())) { + numa = threadPoolp->numaStatus(); + } + fprintf(fp, "VLPROF info numa %s\n", numa.c_str()); + // Note that VerilatedContext will by default create as many threads as there are hardware + // processors, but not all of them might be utilized. Report the actual number that has trace + // entries to avoid over-counting. + unsigned threads = 0; + for (const auto& pair : m_traceps) { + if (!pair.second->empty()) ++threads; + } + fprintf(fp, "VLPROF stat threads %u\n", threads); + fprintf(fp, "VLPROF stat yields %" PRIu64 "\n", VlMTaskVertex::yields()); + + // Copy /proc/cpuinfo into this output so verilator_gantt can be run on + // a different machine + { + const std::unique_ptr ifp{new std::ifstream{"/proc/cpuinfo"}}; + if (!ifp->fail()) { + std::string line; + while (std::getline(*ifp, line)) { fprintf(fp, "VLPROFPROC %s\n", line.c_str()); } + } + } + + for (const auto& pair : m_traceps) { + const uint32_t threadId = pair.first; + ExecutionTrace* const tracep = pair.second; + if (tracep->empty()) continue; + fprintf(fp, "VLPROFTHREAD %" PRIu32 "\n", threadId); + + for (const VlExecutionRecord& er : *tracep) { + const char* const name = VlExecutionRecord::s_ascii[static_cast(er.m_type)]; + const uint64_t time = er.m_tick - m_tickBegin; + fprintf(fp, "VLPROFEXEC %s %" PRIu64, name, time); + + switch (er.m_type) { + case VlExecutionRecord::Type::SECTION_POP: + case VlExecutionRecord::Type::EXEC_GRAPH_BEGIN: + case VlExecutionRecord::Type::EXEC_GRAPH_END: + // No payload + fprintf(fp, "\n"); + break; + case VlExecutionRecord::Type::MTASK_BEGIN: { + const auto& payload = er.m_payload.mtaskBegin; + if (payload.m_hierBlock[0] != '\0') { + fprintf(fp, " id %u predictStart %u cpu %u hierBlock %s\n", payload.m_id, + payload.m_predictStart, payload.m_cpu, payload.m_hierBlock); + } else { + fprintf(fp, " id %u predictStart %u cpu %u\n", payload.m_id, + payload.m_predictStart, payload.m_cpu); + } + break; + } + case VlExecutionRecord::Type::MTASK_END: { + const auto& payload = er.m_payload.mtaskEnd; + fprintf(fp, " predictCost %u\n", payload.m_predictCost); + break; + } + case VlExecutionRecord::Type::THREAD_SCHEDULE_WAIT_BEGIN: + case VlExecutionRecord::Type::THREAD_SCHEDULE_WAIT_END: { + const auto& payload = er.m_payload.threadScheduleWait; + fprintf(fp, " cpu %u\n", payload.m_cpu); + break; + } + case VlExecutionRecord::Type::SECTION_PUSH: { + const auto& payload = er.m_payload.sectionPush; + fprintf(fp, " %s\n", payload.m_name); + break; + } + default: abort(); // LCOV_EXCL_LINE + } + } + } + fprintf(fp, "VLPROF stat ticks %" PRIu64 "\n", tickEnd - m_tickBegin); + + std::fclose(fp); +} diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.h new file mode 100644 index 00000000000..b5fb7844526 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.h @@ -0,0 +1,305 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2012-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated run-time profiling header +/// +/// This file is not part of the Verilated public-facing API. +/// It is only for internal use by Verilated library routines. +/// +//============================================================================= + +#ifndef VERILATOR_VERILATED_PROFILER_H_ +#define VERILATOR_VERILATED_PROFILER_H_ + +#include "verilatedos.h" + +#include "verilated.h" + +#include +#include +#include +#include +#include +#include + +class VlExecutionProfiler; +class VlThreadPool; + +//============================================================================= +// Macros to simplify generated code + +#define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \ + if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfilerp->enabled())) \ + (vlSymsp)->__Vm_executionProfilerp->addRecord() + +//============================================================================= +// Return high-precision counter for profiling, or 0x0 if not available +VL_ATTR_ALWINLINE QData VL_CPU_TICK() { + uint64_t val; + VL_GET_CPU_TICK(val); + return val; +} + +//============================================================================= +// Private class used by VlExecutionProfiler + +#define _VL_FOREACH_APPLY(macro, arg) macro(arg, #arg) + +// clang-format off +#define FOREACH_VlExecutionRecord_TYPE(macro) \ + _VL_FOREACH_APPLY(macro, SECTION_PUSH) \ + _VL_FOREACH_APPLY(macro, SECTION_POP) \ + _VL_FOREACH_APPLY(macro, MTASK_BEGIN) \ + _VL_FOREACH_APPLY(macro, MTASK_END) \ + _VL_FOREACH_APPLY(macro, THREAD_SCHEDULE_WAIT_BEGIN) \ + _VL_FOREACH_APPLY(macro, THREAD_SCHEDULE_WAIT_END) \ + _VL_FOREACH_APPLY(macro, EXEC_GRAPH_BEGIN) \ + _VL_FOREACH_APPLY(macro, EXEC_GRAPH_END) +// clang-format on + +class VlExecutionRecord final { + friend class VlExecutionProfiler; + + // TYPES + enum class Type : uint8_t { +#define VL_FOREACH_MACRO(id, name) id, + FOREACH_VlExecutionRecord_TYPE(VL_FOREACH_MACRO) +#undef VL_FOREACH_MACRO + }; + + static constexpr const char* const s_ascii[] = { +#define VL_FOREACH_MACRO(id, name) name, + FOREACH_VlExecutionRecord_TYPE(VL_FOREACH_MACRO) +#undef VL_FOREACH_MACRO + }; + + union Payload { + struct { + const char* m_name; // Name of section being entered + } sectionPush; + struct { + uint32_t m_id; // MTask id + uint32_t m_predictStart; // Time scheduler predicted would start + uint32_t m_cpu; // Executing CPU id + const char* m_hierBlock; // Name of a hier block with this mtask + } mtaskBegin; + struct { + uint32_t m_predictCost; // How long scheduler predicted would take + } mtaskEnd; + struct { + uint32_t m_cpu; // Executing CPU id + } threadScheduleWait; + }; + + // STATE + // Layout below allows efficient packing. + const uint64_t m_tick = VL_CPU_TICK(); // Tick at construction + Payload m_payload; // The record payload + Type m_type; // The record type + static_assert(alignof(uint64_t) >= alignof(Payload), "Padding not allowed"); + static_assert(alignof(Payload) >= alignof(Type), "Padding not allowed"); + +public: + // CONSTRUCTOR + VlExecutionRecord() = default; + + // METHODS + void sectionPush(const char* name) { + m_payload.sectionPush.m_name = name; + m_type = Type::SECTION_PUSH; + } + void sectionPop() { m_type = Type::SECTION_POP; } + void mtaskBegin(uint32_t id, uint32_t predictStart, const char* hierBlock) { + m_payload.mtaskBegin.m_id = id; + m_payload.mtaskBegin.m_predictStart = predictStart; + m_payload.mtaskBegin.m_cpu = VlOs::getcpu(); + m_payload.mtaskBegin.m_hierBlock = hierBlock; + m_type = Type::MTASK_BEGIN; + } + void mtaskEnd(uint32_t predictCost) { + m_payload.mtaskEnd.m_predictCost = predictCost; + m_type = Type::MTASK_END; + } + void threadScheduleWaitBegin() { + m_payload.threadScheduleWait.m_cpu = VlOs::getcpu(); + m_type = Type::THREAD_SCHEDULE_WAIT_BEGIN; + } + void threadScheduleWaitEnd() { + m_payload.threadScheduleWait.m_cpu = VlOs::getcpu(); + m_type = Type::THREAD_SCHEDULE_WAIT_END; + } + void execGraphBegin() { m_type = Type::EXEC_GRAPH_BEGIN; } + void execGraphEnd() { m_type = Type::EXEC_GRAPH_END; } +}; + +static_assert(std::is_trivially_destructible::value, + "VlExecutionRecord should be trivially destructible for fast buffer clearing"); + +//============================================================================= +// VlExecutionProfiler is for collecting profiling data about model execution + +class VlExecutionProfiler final : public VerilatedVirtualBase { + // CONSTANTS + + // In order to try to avoid dynamic memory allocations during the actual profiling phase, + // trace buffers are pre-allocated to be able to hold [a multiple] of this many records. + static constexpr size_t RESERVED_TRACE_CAPACITY = 4096; + + // TYPES + + // Execution traces are recorded into thread local vectors. We can append records of profiling + // events to this vector with very low overhead, and then dump them out later. This prevents + // the overhead of printf/malloc/IO from corrupting the profiling data. It's super cheap to + // append a VlProfileRec struct on the end of a pre-allocated vector; this is the only cost we + // pay in real-time during a profiling cycle. Internal note: Globals may multi-construct, see + // verilated.cpp top. + using ExecutionTrace = std::vector; + + // STATE + VerilatedContext& m_context; // The context this profiler is under + static thread_local ExecutionTrace t_trace; // thread-local trace buffers + mutable VerilatedMutex m_mutex; + // Map from thread id to &t_trace of given thread + std::map m_traceps VL_GUARDED_BY(m_mutex); + + bool m_enabled = false; // Is profiling currently enabled + + uint64_t m_tickBegin = 0; // Sample time (rdtsc() on x86) at beginning of collection + uint64_t m_lastStartReq = 0; // Last requested profiling start (in simulation time) + uint32_t m_windowCount = 0; // Track our position in the cache warmup and profile window + +public: + // CONSTRUCTOR + explicit VlExecutionProfiler(VerilatedContext& context); + ~VlExecutionProfiler() override = default; + + // METHODS + + // Is profiling enabled + bool enabled() const { return m_enabled; } + // Append a trace record to the trace buffer of the current thread + static VlExecutionRecord& addRecord() { + t_trace.emplace_back(); + return t_trace.back(); + } + // Configure profiler (called in beginning of 'eval') + void configure(); + // Setup profiling on a particular thread; + void setupThread(uint32_t threadId); + // Clear all profiling data + void clear() VL_MT_SAFE_EXCLUDES(m_mutex); + // Write profiling data into file + void dump(const char* filenamep, uint64_t tickEnd) VL_MT_SAFE_EXCLUDES(m_mutex); + + // Passed to VerilatedContext to create the VlExecutionProfiler profiler instance + static VerilatedVirtualBase* construct(VerilatedContext& context); +}; + +//============================================================================= +// VlPgoProfiler is for collecting profiling data for PGO + +template +class VlPgoProfiler final { + // TYPES + struct Record final { + const std::string m_name; // Hashed name of mtask/etc + const size_t m_counterNumber = 0; // Which counter has data + }; + + // Counters are stored packed, all together to reduce cache effects + std::array m_counters{}; // Time spent on this record + std::vector m_records; // Record information + // An original cost of a profiled hier block. During Verilation with + // collected profiling data, costs of hier blocks change thus hashes of + // original mtasks does not match those from the previous, + // instrumented, run. We shall not assume that a single top-level mtask + // will correspond to the hier block as multiple hier block DPIs can be + // contracted into a single mtask. Therefore, the old cost, from + // previous instrumented run, is used to stabilize profiled scheduling. + const uint64_t m_currentHierBlockCost; + +public: + // METHODS + explicit VlPgoProfiler(uint64_t currentHierBlockCost = 0) + : m_currentHierBlockCost{currentHierBlockCost} {} + ~VlPgoProfiler() = default; + VL_UNMOVABLE(VlPgoProfiler); + VL_UNCOPYABLE(VlPgoProfiler); + void writeHeader(const std::string& filename) VL_MT_SAFE; + void write(const char* modelp, const std::string& filename) VL_MT_SAFE; + void addCounter(size_t counter, const std::string& name) { + VL_DEBUG_IF(assert(counter < N_Entries);); + m_records.emplace_back(Record{name, counter}); + } + void startCounter(size_t counter) { + // -= so when we add end time in stopCounter, the net effect is adding the difference, + // without needing to hold onto a temporary + m_counters[counter] -= VL_CPU_TICK(); + } + void stopCounter(size_t counter) { m_counters[counter] += VL_CPU_TICK(); } +}; + +template +void VlPgoProfiler::writeHeader(const std::string& filename) VL_MT_SAFE { + static VerilatedMutex s_mutex; + const VerilatedLockGuard lock{s_mutex}; + + // On the first call we create the file. On later calls we append. + // So when we have multiple models in an executable, possibly even + // running on different threads, each will have a different symtab so + // each will collect is own data correctly. However when each is + // destroyed we need to get all the data, not keep overwriting and only + // get the last model's data. + + FILE* const fp = std::fopen(filename.c_str(), "w"); + if (VL_UNLIKELY(!fp)) { + VL_FATAL_MT(filename.c_str(), 0, "", "+prof+vlt+file file not writable"); + } + + VL_DEBUG_IF(VL_DBG_MSGF("+prof+vlt+file initializing '%s'\n", filename.c_str());); + + // TODO Perhaps merge with verilated_coverage output format, so can + // have a common merging and reporting tool, etc. + fprintf(fp, "// Verilated model profile-guided optimization data dump file\n"); + fprintf(fp, "`verilator_config\n"); + + std::fclose(fp); +} + +template +void VlPgoProfiler::write(const char* modelp, const std::string& filename) VL_MT_SAFE { + static VerilatedMutex s_mutex; + const VerilatedLockGuard lock{s_mutex}; + + FILE* const fp = std::fopen(filename.c_str(), "a"); + if (VL_UNLIKELY(!fp)) { + VL_FATAL_MT(filename.c_str(), 0, "", "+prof+vlt+file file not writable"); + } + + VL_DEBUG_IF(VL_DBG_MSGF("+prof+vlt+file writing to '%s'\n", filename.c_str());); + + if (m_currentHierBlockCost) { + fprintf(fp, "profile_data -hier-dpi \"%s\" -cost 64'd%" PRIu64 "\n", modelp, + m_currentHierBlockCost); + } + + for (const Record& rec : m_records) { + fprintf(fp, "profile_data -model \"%s\" -mtask \"%s\" -cost 64'd%" PRIu64 "\n", modelp, + rec.m_name.c_str(), m_counters[rec.m_counterNumber]); + } + + std::fclose(fp); +} + +#endif diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.cpp new file mode 100644 index 00000000000..2a1f3d562c2 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.cpp @@ -0,0 +1,966 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//========================================================================= +/// +/// \file +/// \brief Verilated randomization implementation code +/// +/// This file must be compiled and linked against all Verilated objects +/// that use randomization features. +/// +/// See the internals documentation docs/internals.rst for details. +/// +//========================================================================= + +#include "verilated_random.h" + +#include +#include +#include +#include +#include + +#define _VL_SOLVER_HASH_LEN 1 +#define _VL_SOLVER_HASH_LEN_TOTAL 4 + +// clang-format off +#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__)) +# define _VL_SOLVER_PIPE // Allow pipe SMT solving. Needs fork() +#endif + +#ifdef _VL_SOLVER_PIPE +# include +# include +#endif + +#if defined(_WIN32) || defined(__MINGW32__) +# include // open, read, write, close +#endif +// clang-format on + +class VlRProcess final : private std::streambuf, public std::iostream { + static constexpr int BUFFER_SIZE = 4096; + const char* const* m_cmd = nullptr; // fork() process argv +#ifdef _VL_SOLVER_PIPE + pid_t m_pid = 0; // fork() process id +#else + int m_pid = 0; // fork() process id - always zero as disabled +#endif + bool m_pidExited = true; // If subprocess has exited and can be opened + int m_pidStatus = 0; // fork() process exit status, valid if m_pidExited + int m_writeFd = -1; // File descriptor TO subprocess + int m_readFd = -1; // File descriptor FROM subprocess + char m_readBuf[BUFFER_SIZE]; + char m_writeBuf[BUFFER_SIZE]; + +public: + typedef std::streambuf::traits_type traits_type; + +protected: + int overflow(int c = traits_type::eof()) override { + char c2 = static_cast(c); + if (pbase() == pptr()) return 0; + size_t size = pptr() - pbase(); + ssize_t n = ::write(m_writeFd, pbase(), size); + // VL_PRINTF_MT("solver-write '%s'\n", std::string(pbase(), size).c_str()); + if (n == -1) perror("write"); + if (n <= 0) { + wait_report(); + return traits_type::eof(); + } + if (n == size) + setp(m_writeBuf, m_writeBuf + sizeof(m_writeBuf)); + else + setp(m_writeBuf + n, m_writeBuf + sizeof(m_writeBuf)); + if (c != traits_type::eof()) sputc(c2); + return 0; + } + int underflow() override { + sync(); + ssize_t n = ::read(m_readFd, m_readBuf, sizeof(m_readBuf)); + if (n == -1) perror("read"); + if (n <= 0) { + wait_report(); + return traits_type::eof(); + } + setg(m_readBuf, m_readBuf, m_readBuf + n); + return traits_type::to_int_type(m_readBuf[0]); + } + int sync() override { + overflow(); + return 0; + } + +public: + explicit VlRProcess(const char* const* const cmd = nullptr) + : std::streambuf{} + , std::iostream{this} + , m_cmd{cmd} { + open(cmd); + } + + void wait_report() { + if (m_pidExited) return; +#ifdef _VL_SOLVER_PIPE + if (waitpid(m_pid, &m_pidStatus, 0) != m_pid) return; + if (m_pidStatus) { + std::stringstream msg; + msg << "Subprocess command `" << m_cmd[0]; + for (const char* const* arg = m_cmd + 1; *arg; ++arg) msg << ' ' << *arg; + msg << "' failed: "; + if (WIFSIGNALED(m_pidStatus)) + msg << strsignal(WTERMSIG(m_pidStatus)) + << (WCOREDUMP(m_pidStatus) ? " (core dumped)" : ""); + else if (WIFEXITED(m_pidStatus)) + msg << "exit status " << WEXITSTATUS(m_pidStatus); + const std::string str = msg.str(); + VL_WARN_MT("", 0, "VlRProcess", str.c_str()); + } +#endif + m_pidExited = true; + m_pid = 0; + closeFds(); + } + + void closeFds() { + if (m_writeFd != -1) { + close(m_writeFd); + m_writeFd = -1; + } + if (m_readFd != -1) { + close(m_readFd); + m_readFd = -1; + } + } + + bool open(const char* const* const cmd) { + setp(std::begin(m_writeBuf), std::end(m_writeBuf)); + setg(m_readBuf, m_readBuf, m_readBuf); +#ifdef _VL_SOLVER_PIPE + if (!cmd || !cmd[0]) return false; + m_cmd = cmd; + int fd_stdin[2]; // Can't use std::array + int fd_stdout[2]; // Can't use std::array + constexpr int P_RD = 0; + constexpr int P_WR = 1; + + if (pipe(fd_stdin) != 0) { + perror("VlRProcess::open: pipe"); + return false; + } + if (pipe(fd_stdout) != 0) { + perror("VlRProcess::open: pipe"); + close(fd_stdin[P_RD]); + close(fd_stdin[P_WR]); + return false; + } + + if (fd_stdin[P_RD] <= 2 || fd_stdin[P_WR] <= 2 || fd_stdout[P_RD] <= 2 + || fd_stdout[P_WR] <= 2) { + // We'd have to rearrange all of the FD usages in this case. + // Too unlikely; verilator isn't a daemon. + fprintf(stderr, "stdin/stdout closed before pipe opened\n"); + close(fd_stdin[P_RD]); + close(fd_stdin[P_WR]); + close(fd_stdout[P_RD]); + close(fd_stdout[P_WR]); + return false; + } + + const pid_t pid = fork(); + if (pid < 0) { + perror("VlRProcess::open: fork"); + close(fd_stdin[P_RD]); + close(fd_stdin[P_WR]); + close(fd_stdout[P_RD]); + close(fd_stdout[P_WR]); + return false; + } + if (pid == 0) { + // Child + close(fd_stdin[P_WR]); + dup2(fd_stdin[P_RD], STDIN_FILENO); + close(fd_stdout[P_RD]); + dup2(fd_stdout[P_WR], STDOUT_FILENO); + execvp(cmd[0], const_cast(cmd)); + std::stringstream msg; + msg << "VlRProcess::open: execvp(" << cmd[0] << ")"; + const std::string str = msg.str(); + perror(str.c_str()); + _exit(127); + } + // Parent + m_pid = pid; + m_pidExited = false; + m_pidStatus = 0; + m_readFd = fd_stdout[P_RD]; + m_writeFd = fd_stdin[P_WR]; + + close(fd_stdin[P_RD]); + close(fd_stdout[P_WR]); + + return true; +#else + return false; +#endif + } +}; + +static VlRProcess& getSolver() { + static VlRProcess s_solver; + static bool s_done = false; + if (s_done) return s_solver; + s_done = true; + + static std::vector s_argv; + static std::string s_program = Verilated::threadContextp()->solverProgram(); + s_argv.emplace_back(&s_program[0]); + for (char* arg = &s_program[0]; *arg; ++arg) { + if (*arg == ' ') { + *arg = '\0'; + s_argv.emplace_back(arg + 1); + } + } + s_argv.emplace_back(nullptr); + + const char* const* const cmd = &s_argv[0]; + s_solver.open(cmd); + s_solver << "(set-logic QF_ABV)\n"; + s_solver << "(check-sat)\n"; + s_solver << "(reset)\n"; + std::string s; + getline(s_solver, s); + if (s == "sat") return s_solver; + + std::stringstream msg; + msg << "Unable to communicate with SAT solver, please check its installation or specify a " + "different one in VERILATOR_SOLVER environment variable.\n"; + msg << " ... Tried: $"; + for (const char* const* arg = cmd; *arg; ++arg) msg << ' ' << *arg; + msg << '\n'; + const std::string str = msg.str(); + VL_WARN_MT("", 0, "randomize", str.c_str()); + + while (getline(s_solver, s)) {} + return s_solver; +} + +static std::string readUntilBalanced(std::istream& stream) { + std::string result; + std::string token; + int parenCount = 1; + while (stream >> token) { + for (const char c : token) { + if (c == '(') { + ++parenCount; + } else if (c == ')') { + --parenCount; + } + } + result += token + " "; + if (parenCount == 0) break; + } + return result; +} + +static std::string parseNestedSelect(const std::string& nested_select_expr, + std::vector& indices) { + std::istringstream nestedStream(nested_select_expr); + std::string name, idx; + nestedStream >> name; + if (name == "(select") { + const std::string further_nested_expr = readUntilBalanced(nestedStream); + name = parseNestedSelect(further_nested_expr, indices); + } + std::getline(nestedStream, idx, ')'); + indices.push_back(idx); + return name; +} + +//====================================================================== +// VlRandomizer:: Methods + +void VlRandomVar::emitGetValue(std::ostream& s) const { s << ' ' << m_name; } +void VlRandomVar::emitExtract(std::ostream& s, int i) const { + s << " ((_ extract " << i << ' ' << i << ") " << m_name << ')'; +} +void VlRandomVar::emitType(std::ostream& s) const { s << "(_ BitVec " << width() << ')'; } +int VlRandomVar::totalWidth() const { return m_width; } +static bool parseSMTNum(int obits, WDataOutP owp, const std::string& val) { + int i; + for (i = 0; val[i] && val[i] != '#'; ++i) {} + if (val[i++] != '#') return false; + switch (val[i++]) { + case 'b': _vl_vsss_based(owp, obits, 1, &val[i], 0, val.size() - i); break; + case 'o': _vl_vsss_based(owp, obits, 3, &val[i], 0, val.size() - i); break; + case 'h': // FALLTHRU + case 'x': _vl_vsss_based(owp, obits, 4, &val[i], 0, val.size() - i); break; + default: + VL_WARN_MT(__FILE__, __LINE__, "randomize", + "Internal: Unable to parse solver's randomized number"); + return false; + } + return true; +} +bool VlRandomVar::set(const std::string& idx, const std::string& val) const { + VlWide qowp; + VL_SET_WQ(qowp, 0ULL); + WDataOutP owp = qowp; + const int obits = width(); + VlWide qiwp; + VL_SET_WQ(qiwp, 0ULL); + if (!idx.empty() && !parseSMTNum(64, qiwp, idx)) return false; + const int nidx = qiwp[0]; + if (obits > VL_QUADSIZE) owp = reinterpret_cast(datap(nidx)); + if (!parseSMTNum(obits, owp, val)) return false; + + if (obits <= VL_BYTESIZE) { + CData* const p = static_cast(datap(nidx)); + *p = VL_CLEAN_II(obits, obits, owp[0]); + } else if (obits <= VL_SHORTSIZE) { + SData* const p = static_cast(datap(nidx)); + *p = VL_CLEAN_II(obits, obits, owp[0]); + } else if (obits <= VL_IDATASIZE) { + IData* const p = static_cast(datap(nidx)); + *p = VL_CLEAN_II(obits, obits, owp[0]); + } else if (obits <= VL_QUADSIZE) { + QData* const p = static_cast(datap(nidx)); + *p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp)); + } else { + _vl_clean_inplace_w(obits, owp); + } + return true; +} + +void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits) { + const IData hash = VL_RANDOM_RNG_I(rngr) & ((1 << bits) - 1); + int varBits = 0; + for (const auto& var : m_vars) varBits += var.second->totalWidth(); + os << "(= #b"; + for (int i = bits - 1; i >= 0; i--) os << (VL_BITISSET_I(hash, i) ? '1' : '0'); + if (bits > 1) os << " (concat"; + for (int i = 0; i < bits; ++i) { + IData varBitsLeft = varBits; + IData varBitsWant = (varBits + 1) / 2; + if (varBits > 2) os << " (bvxor"; + for (const auto& var : m_vars) { + for (int j = 0; j < var.second->totalWidth(); j++, varBitsLeft--) { + const bool doEmit = (VL_RANDOM_RNG_I(rngr) % varBitsLeft) < varBitsWant; + if (doEmit) { + var.second->emitExtract(os, j); + if (--varBitsWant == 0) break; + } + } + if (varBitsWant == 0) break; + } + if (varBits > 2) os << ')'; + } + if (bits > 1) os << ')'; + os << ')'; +} + +size_t VlRandomizer::hashConstraints() const { + size_t h = 0; + for (const auto& c : m_constraints) { + h ^= std::hash{}(c) + 0x9e3779b9 + (h << 6) + (h >> 2); + } + return h; +} + +void VlRandomizer::enumerateRandcValues(const std::string& varName, VlRNG& rngr) { + std::vector values; + const auto varIt = m_vars.find(varName); + if (varIt == m_vars.end()) return; + const int width = varIt->second->width(); + + std::iostream& os = getSolver(); + if (!os) return; + + // Set up a single incremental solver session for enumeration + os << "(set-option :produce-models true)\n"; + os << "(set-logic QF_ABV)\n"; + os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n"; + os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n"; + + // Declare all variables (solver needs full context for cross-var constraints) + for (const auto& var : m_vars) { + if (var.second->dimension() > 0) { + auto arrVarsp = std::make_shared(m_arr_vars); + var.second->setArrayInfo(arrVarsp); + } + os << "(declare-fun " << var.first << " () "; + var.second->emitType(os); + os << ")\n"; + } + + // Assert all user constraints + for (const std::string& constraint : m_constraints) { + os << "(assert (= #b1 " << constraint << "))\n"; + } + + // Incrementally enumerate all valid values for this randc variable + while (true) { + os << "(check-sat)\n"; + std::string sat; + do { std::getline(os, sat); } while (sat.empty()); + if (sat != "sat") break; + + // Read just this variable's value + os << "(get-value (" << varName << "))\n"; + char c; + os >> c; // '(' + os >> c; // '(' + std::string name, value; + os >> name; // Consume variable name token from solver output + (void)name; + std::getline(os, value, ')'); + os >> c; // ')' + + // Parse the SMT value to uint64_t + VlWide qowp; + VL_SET_WQ(qowp, 0ULL); + if (!parseSMTNum(width, qowp, value)) break; + const uint64_t numVal = (width <= 32) ? qowp[0] : VL_SET_QW(qowp); + + values.push_back(numVal); + + // Exclude this value for next iteration (incremental) + os << "(assert (not (= " << varName << " (_ bv" << numVal << " " << width << "))))\n"; + } + + os << "(reset)\n"; + + // Shuffle using Fisher-Yates + for (size_t i = values.size(); i > 1; --i) { + const size_t j = VL_RANDOM_RNG_I(rngr) % i; + std::swap(values[i - 1], values[j]); + } + + m_randcValueQueues[varName] = std::deque(values.begin(), values.end()); +} + +bool VlRandomizer::next(VlRNG& rngr) { + if (m_vars.empty() && m_unique_arrays.empty()) return true; + for (const std::string& baseName : m_unique_arrays) { + const auto it = m_vars.find(baseName); + const uint32_t size = m_unique_array_sizes.at(baseName); + + if (it != m_vars.end()) { + std::string distinctExpr = "(__Vbv (distinct"; + for (uint32_t i = 0; i < size; ++i) { + char hexIdx[12]; + sprintf(hexIdx, "#x%08x", i); + distinctExpr += " (select " + it->first + " " + hexIdx + ")"; + } + distinctExpr += "))"; + m_constraints.push_back(distinctExpr); + } + } + + // Randc queue-based cycling: enumerate valid values once, then pop per call + if (!m_randcVarNames.empty()) { + const size_t currentHash = hashConstraints(); + // Invalidate queues if constraints changed (e.g., constraint_mode toggled) + if (currentHash != m_randcConstraintHash) { + m_randcValueQueues.clear(); + m_randcConstraintHash = currentHash; + } + // Refill empty queues (start of new cycle) + for (const auto& name : m_randcVarNames) { + auto& queue = m_randcValueQueues[name]; + if (queue.empty()) enumerateRandcValues(name, rngr); + } + } + + // Pop randc values from queues (will be pinned in solver) + std::map randcPinned; + for (const auto& name : m_randcVarNames) { + auto& queue = m_randcValueQueues[name]; + if (queue.empty()) return false; // No valid values exist + randcPinned[name] = queue.front(); + queue.pop_front(); + } + + // If solve-before constraints are present, use phased solving + if (!m_solveBefore.empty()) return nextPhased(rngr); + + std::iostream& os = getSolver(); + if (!os) return false; + + os << "(set-option :produce-models true)\n"; + os << "(set-logic QF_ABV)\n"; + os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n"; + os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n"; + for (const auto& var : m_vars) { + if (var.second->dimension() > 0) { + auto arrVarsp = std::make_shared(m_arr_vars); + var.second->setArrayInfo(arrVarsp); + } + os << "(declare-fun " << var.first << " () "; + var.second->emitType(os); + os << ")\n"; + } + + for (const std::string& constraint : m_constraints) { + os << "(assert (= #b1 " << constraint << "))\n"; + } + + // Pin randc values from pre-enumerated queues + for (const auto& pair : randcPinned) { + const int w = m_vars.at(pair.first)->width(); + os << "(assert (= " << pair.first << " (_ bv" << pair.second << " " << w << ")))\n"; + } + + os << "(check-sat)\n"; + + bool sat = parseSolution(os, true); + if (!sat) { + // If unsat, use named assertions to get unsat-core + os << "(reset)\n"; + os << "(set-option :produce-unsat-cores true)\n"; + os << "(set-logic QF_ABV)\n"; + os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n"; + os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n"; + for (const auto& var : m_vars) { + if (var.second->dimension() > 0) { + auto arrVarsp = std::make_shared(m_arr_vars); + var.second->setArrayInfo(arrVarsp); + } + os << "(declare-fun " << var.first << " () "; + var.second->emitType(os); + os << ")\n"; + } + int j = 0; + for (const std::string& constraint : m_constraints) { + os << "(assert (! (= #b1 " << constraint << ") :named cons" << j << "))\n"; + j++; + } + os << "(check-sat)\n"; + sat = parseSolution(os, true); + (void)sat; + os << "(reset)\n"; + return false; + } + for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) { + os << "(assert "; + randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN); + os << ")\n"; + os << "\n(check-sat)\n"; + sat = parseSolution(os, false); + (void)sat; + } + + os << "(reset)\n"; + return true; +} + +bool VlRandomizer::parseSolution(std::iostream& os, bool log) { + std::string sat; + do { std::getline(os, sat); } while (sat == ""); + if (sat == "unsat") { + if (!log) return false; + os << "(get-unsat-core) \n"; + sat.clear(); + std::getline(os, sat); + std::vector numbers; + std::string currentNum; + for (char c : sat) { + if (std::isdigit(c)) { + currentNum += c; + numbers.push_back(std::stoi(currentNum)); + currentNum.clear(); + } + } + if (Verilated::threadContextp()->warnUnsatConstr()) { + for (int n : numbers) { + if (n < m_constraints_line.size()) { + const std::string& constraint_info = m_constraints_line[n]; + // Parse "filename:linenum source" format + size_t colon_pos = constraint_info.find(':'); + if (colon_pos != std::string::npos) { + std::string filename = constraint_info.substr(0, colon_pos); + size_t space_pos = constraint_info.find(" ", colon_pos); + std::string linenum_str; + std::string source; + if (space_pos != std::string::npos) { + linenum_str + = constraint_info.substr(colon_pos + 1, space_pos - colon_pos - 1); + source = constraint_info.substr(space_pos + 3); + } else { + linenum_str = constraint_info.substr(colon_pos + 1); + } + const int linenum = std::stoi(linenum_str); + std::string msg = "UNSATCONSTR: Unsatisfied constraint"; + if (!source.empty()) { + // Trim leading whitespace and add quotes + size_t start = source.find_first_not_of(" \t"); + if (start != std::string::npos) { + msg += ": '" + source.substr(start) + "'"; + } + } + VL_WARN_MT(filename.c_str(), linenum, "", msg.c_str()); + } else { + VL_PRINTF("%%Warning-UNSATCONSTR: Unsatisfied constraint: %s\n", + constraint_info.c_str()); + } + } + } + } + return false; + } + if (sat != "sat") { + std::stringstream msg; + msg << "Internal: Solver error: " << sat; + const std::string str = msg.str(); + VL_WARN_MT(__FILE__, __LINE__, "randomize", str.c_str()); + return false; + } + + os << "(get-value ("; + for (const auto& var : m_vars) { + if (var.second->dimension() > 0) { + auto arrVarsp = std::make_shared(m_arr_vars); + var.second->setArrayInfo(arrVarsp); + } + var.second->emitGetValue(os); + } + os << "))\n"; + // Quasi-parse S-expression of the form ((x #xVALUE) (y #bVALUE) (z #xVALUE)) + char c; + os >> c; + if (c != '(') { + VL_WARN_MT(__FILE__, __LINE__, "randomize", + "Internal: Unable to parse solver's response: invalid S-expression"); + return false; + } + while (true) { + os >> c; + if (c == ')') break; + if (c != '(') { + VL_WARN_MT(__FILE__, __LINE__, "randomize", + "Internal: Unable to parse solver's response: invalid S-expression"); + return false; + } + std::string name, idx, value; + std::vector indices; + os >> name; + indices.clear(); + if (name == "(select") { + const std::string selectExpr = readUntilBalanced(os); + name = parseNestedSelect(selectExpr, indices); + } + std::getline(os, value, ')'); + const auto it = m_vars.find(name); + if (it == m_vars.end()) continue; + const VlRandomVar& varr = *it->second; + if (m_randmodep && !varr.randModeIdxNone()) { + if (!m_randmodep->at(varr.randModeIdx())) continue; + } + if (!indices.empty()) { + std::ostringstream oss; + oss << varr.name(); + for (const auto& hex_index : indices) { + const size_t start = hex_index.find_first_not_of(" "); + if (start == std::string::npos || hex_index.substr(start, 2) != "#x") { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", + "hex_index contains invalid format"); + continue; + } + std::string trimmed_hex = hex_index.substr(start + 2); + + if (trimmed_hex.size() <= 8) { // Small numbers: <= 32 bits + // Convert to decimal and output directly + oss << "[" << std::to_string(std::stoll(trimmed_hex, nullptr, 16)) << "]"; + } else { // Large numbers: > 32 bits + // Trim leading zeros and handle empty case + trimmed_hex.erase(0, trimmed_hex.find_first_not_of('0')); + oss << "[" << (trimmed_hex.empty() ? "0" : trimmed_hex) << "]"; + } + } + const std::string indexed_name = oss.str(); + + const auto iti = std::find_if(m_arr_vars.begin(), m_arr_vars.end(), + [&indexed_name](const auto& entry) { + return entry.second->m_name == indexed_name; + }); + if (iti != m_arr_vars.end()) { + std::ostringstream ss; + ss << "#x" << std::hex << std::setw(8) << std::setfill('0') + << iti->second->m_index; + idx = ss.str(); + } else { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", + "indexed_name not found in m_arr_vars"); + } + } + varr.set(idx, value); + } + return true; +} + +void VlRandomizer::hard(std::string&& constraint, const char* filename, uint32_t linenum, + const char* source) { + m_constraints.emplace_back(std::move(constraint)); + // Format constraint location: "filename:linenum source" + if (filename[0] != '\0' || source[0] != '\0') { + std::string line; + if (filename[0] != '\0') { + line = std::string(filename) + ":" + std::to_string(linenum); + if (source[0] != '\0') line += " " + std::string(source); + } else { + line = source; + } + m_constraints_line.emplace_back(std::move(line)); + } +} + +void VlRandomizer::clearConstraints() { + m_constraints.clear(); + m_constraints_line.clear(); + m_solveBefore.clear(); + // Keep m_vars for class member randomization +} + +void VlRandomizer::clearAll() { + m_constraints.clear(); + m_vars.clear(); + m_randcVarNames.clear(); + m_randcValueQueues.clear(); + m_randcConstraintHash = 0; +} + +void VlRandomizer::markRandc(const char* name) { m_randcVarNames.insert(name); } + +void VlRandomizer::solveBefore(const char* beforeName, const char* afterName) { + m_solveBefore.emplace_back(std::string(beforeName), std::string(afterName)); +} + +bool VlRandomizer::nextPhased(VlRNG& rngr) { + // Phased solving for solve...before constraints. + // Variables are solved in layers determined by topological sort of the + // solve-before dependency graph. Each layer is solved with ALL constraints + // (preserving the solution space) but earlier layers' values are pinned. + + // Step 1: Build dependency graph (before -> {after vars}) + std::map> graph; + std::map inDegree; + std::set solveBeforeVars; + + for (const auto& pair : m_solveBefore) { + const std::string& before = pair.first; + const std::string& after = pair.second; + // Only consider variables that are actually registered + if (m_vars.find(before) == m_vars.end() || m_vars.find(after) == m_vars.end()) continue; + graph[before].insert(after); + solveBeforeVars.insert(before); + solveBeforeVars.insert(after); + if (inDegree.find(before) == inDegree.end()) inDegree[before] = 0; + if (inDegree.find(after) == inDegree.end()) inDegree[after] = 0; + } + + // Compute in-degrees (after depends on before, so edge is before->after, + // but for solving order: before has no incoming edge from after) + // Actually: "solve x before y" means x should be solved first. + // Dependency: y depends on x. Edge: x -> y. in-degree of y increases. + for (const auto& entry : graph) { + for (const auto& to : entry.second) { inDegree[to]++; } + } + + // Step 2: Topological sort into layers (Kahn's algorithm) + std::vector> layers; + std::set remaining = solveBeforeVars; + + while (!remaining.empty()) { + std::vector currentLayer; + for (const auto& var : remaining) { + if (inDegree[var] == 0) currentLayer.push_back(var); + } + if (currentLayer.empty()) { + VL_WARN_MT("", 0, "randomize", "Circular dependency in solve-before constraints"); + return false; + } + std::sort(currentLayer.begin(), currentLayer.end()); + for (const auto& var : currentLayer) { + remaining.erase(var); + if (graph.count(var)) { + for (const auto& to : graph[var]) { inDegree[to]--; } + } + } + layers.push_back(std::move(currentLayer)); + } + + // If only one layer, no phased solving needed -- fall through to normal path + // (all solve_before vars are independent, no actual ordering required) + if (layers.size() <= 1) { + // Clear solve_before temporarily and call normal next() + const auto saved = std::move(m_solveBefore); + m_solveBefore.clear(); + const bool result = next(rngr); + m_solveBefore = std::move(saved); + return result; + } + + // Step 3: Solve phase by phase + std::map solvedValues; // varName -> SMT value literal + + for (size_t phase = 0; phase < layers.size(); phase++) { + const bool isFinalPhase = (phase == layers.size() - 1); + + std::iostream& os = getSolver(); + if (!os) return false; + + // Solver session setup + os << "(set-option :produce-models true)\n"; + os << "(set-logic QF_ABV)\n"; + os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n"; + os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n"; + + // Declare ALL variables + for (const auto& var : m_vars) { + if (var.second->dimension() > 0) { + auto arrVarsp = std::make_shared(m_arr_vars); + var.second->setArrayInfo(arrVarsp); + } + os << "(declare-fun " << var.first << " () "; + var.second->emitType(os); + os << ")\n"; + } + + // Pin all previously solved variables + for (const auto& entry : solvedValues) { + os << "(assert (= " << entry.first << " " << entry.second << "))\n"; + } + + // Assert ALL constraints + for (const std::string& constraint : m_constraints) { + os << "(assert (= #b1 " << constraint << "))\n"; + } + + // Initial check-sat WITHOUT diversity (guaranteed sat if constraints are consistent) + os << "(check-sat)\n"; + + if (isFinalPhase) { + // Final phase: use parseSolution to write ALL values to memory + bool sat = parseSolution(os, true); + if (!sat) { + os << "(reset)\n"; + return false; + } + // Diversity loop (same as normal next()) + for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) { + os << "(assert "; + randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN); + os << ")\n"; + os << "\n(check-sat)\n"; + sat = parseSolution(os, false); + (void)sat; + } + os << "(reset)\n"; + } else { + // Intermediate phase: extract values for current layer variables only + std::string satResponse; + do { std::getline(os, satResponse); } while (satResponse.empty()); + + if (satResponse != "sat") { + os << "(reset)\n"; + return false; + } + + // Build get-value variable list for this layer + const auto& layerVars = layers[phase]; + auto getValueCmd = [&]() { + os << "(get-value ("; + for (const auto& varName : layerVars) { + if (m_vars.count(varName)) os << varName << " "; + } + os << "))\n"; + }; + + // Helper to parse ((name1 value1) (name2 value2) ...) response + auto parseGetValue = [&]() -> bool { + char c; + os >> c; // outer '(' + while (true) { + os >> c; + if (c == ')') break; // outer closing + if (c != '(') return false; + std::string name; + os >> name; + + // Read value handling nested parens for (_ bvN W) format + os >> std::ws; + std::string value; + char firstChar; + os.get(firstChar); + if (firstChar == '(') { + // Compound value like (_ bv5 32) + value = "("; + int depth = 1; + while (depth > 0) { + os.get(c); + value += c; + if (c == '(') + depth++; + else if (c == ')') + depth--; + } + // Read closing ')' of the pair + os >> c; + } else { + // Atom value like #x00000005 or #b101 + value += firstChar; + while (os.get(c) && c != ')') { value += c; } + // Trim trailing whitespace + const size_t end = value.find_last_not_of(" \t\n\r"); + if (end != std::string::npos) value = value.substr(0, end + 1); + } + + solvedValues[name] = value; + } + return true; + }; + + // Get baseline values (deterministic, always valid) + getValueCmd(); + if (!parseGetValue()) { + os << "(reset)\n"; + return false; + } + + // Try diversity: add random constraint, re-check. If sat, get + // updated (more diverse) values. If unsat, keep baseline values. + os << "(assert "; + randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN); + os << ")\n"; + os << "(check-sat)\n"; + satResponse.clear(); + do { std::getline(os, satResponse); } while (satResponse.empty()); + if (satResponse == "sat") { + getValueCmd(); + parseGetValue(); + } + + os << "(reset)\n"; + } + } + + return true; +} + +#ifdef VL_DEBUG +void VlRandomizer::dump() const { + for (const auto& var : m_vars) { + VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name().c_str()); + } + for (const std::string& c : m_constraints) VL_PRINTF("Constraint: %s\n", c.c_str()); +} +#endif diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.h new file mode 100644 index 00000000000..53c09f07113 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.h @@ -0,0 +1,677 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated randomization header +/// +/// This file is included automatically by Verilator in some of the C++ files +/// it generates if randomization features are used. +/// +/// This file is not part of the Verilated public-facing API. +/// It is only for internal use. +/// +/// See the internals documentation docs/internals.rst for details. +/// +//************************************************************************* +#ifndef VERILATOR_VERILATED_RANDOM_H_ +#define VERILATOR_VERILATED_RANDOM_H_ + +#include "verilated.h" + +#include +#include +#include +#include +#include + +//============================================================================= + +// VlRandomExpr and subclasses represent expressions for the constraint solver. +class ArrayInfo final { +public: + const std::string + m_name; // Name of the array variable, including index notation (e.g., arr[2][1]) + void* const m_datap; // Reference to the array variable data + const int m_index; // Flattened (1D) index of the array element + const std::vector m_indices; // Multi-dimensional indices of the array element + const std::vector m_idxWidths; // Multi-dimensional indices' bit widths + + ArrayInfo(const std::string& name, void* datap, int index, const std::vector& indices, + const std::vector& idxWidths) + : m_name{name} + , m_datap{datap} + , m_index{index} + , m_indices{indices} + , m_idxWidths{idxWidths} {} +}; +using ArrayInfoMap = std::map>; + +class VlRandomVar VL_NOT_FINAL { + std::string m_name; // Variable name + void* const m_datap; // Reference to variable data + const int m_width; // Variable width in bits + const int m_dimension; //Variable dimension, default is 0 + const std::uint32_t m_randModeIdx; // rand_mode index + +public: + VlRandomVar(const std::string& name, int width, void* datap, int dimension, + std::uint32_t randModeIdx) + : m_name{name} + , m_datap{datap} + , m_width{width} + , m_dimension{dimension} + , m_randModeIdx{randModeIdx} {} + virtual ~VlRandomVar() = default; + std::string name() const { return m_name; } + int width() const { return m_width; } + int dimension() const { return m_dimension; } + virtual void* datap(int idx) const { return m_datap; } + std::uint32_t randModeIdx() const { return m_randModeIdx; } + bool randModeIdxNone() const { return randModeIdx() == std::numeric_limits::max(); } + bool set(const std::string& idx, const std::string& val) const; + virtual void emitGetValue(std::ostream& s) const; + virtual void emitExtract(std::ostream& s, int i) const; + virtual void emitType(std::ostream& s) const; + virtual int totalWidth() const; + mutable std::shared_ptr m_arrVarsRefp; + void setArrayInfo(const std::shared_ptr& arrVarsRefp) const { + m_arrVarsRefp = arrVarsRefp; + } + mutable std::map count_cache; + int countMatchingElements(const ArrayInfoMap& arr_vars, const std::string& base_name) const { + if (VL_LIKELY(count_cache.find(base_name) != count_cache.end())) + return count_cache[base_name]; + int count = 0; + for (int index = 0; arr_vars.find(base_name + std::to_string(index)) != arr_vars.end(); + ++index) { + ++count; + } + count_cache[base_name] = count; + return count; + } +}; +template +class VlRandomArrayVarTemplate final : public VlRandomVar { +public: + VlRandomArrayVarTemplate(const std::string& name, int width, void* datap, int dimension, + std::uint32_t randModeIdx) + : VlRandomVar{name, width, datap, dimension, randModeIdx} {} + void* datap(int idx) const override { + const std::string indexed_name = name() + std::to_string(idx); + const auto it = m_arrVarsRefp->find(indexed_name); + if (it != m_arrVarsRefp->end()) { + return it->second->m_datap; + } else { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars"); + return nullptr; // LCOV_EXCL_BR_LINE + } + } + void emitHexs(std::ostream& s, const std::vector& indices, const size_t bit_width, + size_t idx) const { + for (int j = bit_width - 4; j >= 0; j -= 4) { + s << "0123456789abcdef"[(indices[idx] >> j) & 0xf]; + } + } + void emitSelect(std::ostream& s, const std::vector& indices, + const std::vector& idxWidths) const { + const size_t num_indices = idxWidths.size(); + size_t wide_size = 0; + + for (size_t idx = 0; idx < num_indices; ++idx) s << "(select "; + s << name(); + + for (size_t idx = 0; idx < num_indices; ++idx) { + const size_t bit_width = idxWidths[idx]; + s << " #x"; + + const size_t emit_count = (bit_width > 32) ? (idxWidths[idx] / 32) : 1; + + for (size_t i = 0; i < emit_count; ++i) { + emitHexs(s, indices, (bit_width > 32) ? 32 : bit_width, wide_size + i); + } + + wide_size += (idxWidths[idx] > 32) ? (idxWidths[idx] / 32) : 1; + s << ")"; + } + } + void emitGetValue(std::ostream& s) const override { + const int elementCounts = countMatchingElements(*m_arrVarsRefp, name()); + for (int i = 0; i < elementCounts; ++i) { + const std::string indexed_name = name() + std::to_string(i); + const auto it = m_arrVarsRefp->find(indexed_name); + if (it != m_arrVarsRefp->end()) { + const std::vector& indices = it->second->m_indices; + const std::vector& idxWidths = it->second->m_idxWidths; + emitSelect(s, indices, idxWidths); + } else { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", + "indexed_name not found in m_arr_vars"); + } + } + } + void emitType(std::ostream& s) const override { + const std::string indexed_name = name() + std::to_string(0); + const auto it = m_arrVarsRefp->find(indexed_name); + if (it != m_arrVarsRefp->end()) { + const std::vector& idxWidths = it->second->m_idxWidths; + if (dimension() > 0) { + for (int i = 0; i < dimension(); ++i) { + s << "(Array (_ BitVec " << idxWidths[i] << ") "; + } + s << "(_ BitVec " << width() << ")"; + for (int i = 0; i < dimension(); ++i) s << ")"; + } + } else { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars"); + } + } + int totalWidth() const override { + const int elementCounts = countMatchingElements(*m_arrVarsRefp, name()); + return width() * elementCounts; + } + void emitExtract(std::ostream& s, int i) const override { + const int j = i / width(); + i = i % width(); + s << " ((_ extract " << i << ' ' << i << ')'; + const std::string indexed_name = name() + std::to_string(j); + const auto it = m_arrVarsRefp->find(indexed_name); + if (it != m_arrVarsRefp->end()) { + const std::vector& indices = it->second->m_indices; + const std::vector& idxWidths = it->second->m_idxWidths; + emitSelect(s, indices, idxWidths); + } else { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars"); + } + s << ')'; + } +}; + +//============================================================================= +// Object holding constraints and variable references. +class VlRandomizer VL_NOT_FINAL { + // MEMBERS + std::vector m_constraints; // Solver-dependent constraints + std::vector + m_constraints_line; // fileline content of the constraint for unsat constraints + std::map> m_vars; // Solver-dependent + // variables + ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration + std::vector m_unique_arrays; + std::map m_unique_array_sizes; + const VlQueue* m_randmodep = nullptr; // rand_mode state; + int m_index = 0; // Internal counter for key generation + std::set m_randcVarNames; // Names of randc variables for cyclic tracking + std::map> + m_randcValueQueues; // Remaining values per randc var (queue-based cycling) + size_t m_randcConstraintHash = 0; // Hash of constraints when queues were built + std::vector> + m_solveBefore; // Solve-before ordering pairs (beforeVar, afterVar) + + // PRIVATE METHODS + void randomConstraint(std::ostream& os, VlRNG& rngr, int bits); + bool parseSolution(std::iostream& file, bool log = false); + void enumerateRandcValues(const std::string& varName, VlRNG& rngr); + size_t hashConstraints() const; + bool nextPhased(VlRNG& rngr); // Phased solving for solve...before + +public: + // CONSTRUCTORS + VlRandomizer() = default; + ~VlRandomizer() = default; + + // METHODS + // Finds the next solution satisfying the constraints + bool next(VlRNG& rngr); + + // --- Process the key for associative array --- + + // process_key: Handle integral keys (<= 32-bit) + template + typename std::enable_if::value && (sizeof(T_Key) <= 4)>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index, + const std::string& base_name, size_t& idx_width) { + integral_index.push_back(static_cast(key)); + indexed_name + = base_name + "[" + std::to_string(integral_index[integral_index.size() - 1]) + "]"; + idx_width = sizeof(T_Key) * 8; + } + + // process_key: Handle integral keys (> 32-bit), split into 2 x 32-bit segments + template + typename std::enable_if::value && (sizeof(T_Key) > 4)>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index, + const std::string& base_name, size_t& idx_width) { + constexpr size_t segment_bits = 32; + constexpr T_Key mask = (static_cast(1) << segment_bits) - 1; + integral_index.push_back(static_cast(key >> segment_bits)); + integral_index.push_back(static_cast(key & mask)); + + std::ostringstream hex_stream; + hex_stream << std::hex << key; + std::string index_string = hex_stream.str(); + index_string.erase(0, index_string.find_first_not_of('0')); + index_string = index_string.empty() ? "0" : index_string; + + indexed_name = base_name + "[" + index_string + "]"; + + idx_width = sizeof(T_Key) * 8; + } + + // process_key: Handle wide keys (VlWide-like), segment is 32-bit per element + template + typename std::enable_if::value>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index, + const std::string& base_name, size_t& idx_width) { + std::ostringstream hex_stream; + for (size_t i = key.size(); i > 0; --i) { + const size_t segment_value = key.at(i - 1); + hex_stream << std::hex << segment_value; + integral_index.push_back(segment_value); + } + std::string index_string = hex_stream.str(); + index_string.erase(0, index_string.find_first_not_of('0')); + index_string = index_string.empty() ? "0" : index_string; + + indexed_name = base_name + "[" + index_string + "]"; + idx_width = key.size() * 32; + } + + // process_key: Handle string key, encoded as 128-bit hex + template + typename std::enable_if::value>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index, + const std::string& base_name, size_t& idx_width) { + // Convert the input string to its ASCII hexadecimal representation + std::ostringstream oss; + for (unsigned char c : key) { + oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(c); + } + std::string hex_str = oss.str(); + // Ensure the hex string is exactly 128 bits (32 hex characters) + hex_str = hex_str.size() > 32 ? hex_str.substr(0, 32) + : std::string(32 - hex_str.size(), '0') + hex_str; + + // Split the hex string into 4 segments (32-bit per segment) + integral_index.clear(); + for (size_t i = 0; i < hex_str.size(); i += 8) { + integral_index.push_back(std::stoul(hex_str.substr(i, 8), nullptr, 16)); + } + + indexed_name = base_name + "[" + + (hex_str.find_first_not_of('0') == std::string::npos + ? "0" + : hex_str.substr(hex_str.find_first_not_of('0'))) + + "]"; + + idx_width = 128; + } + + // process_key: Unsupported key type fallback + template + typename std::enable_if::value + && !std::is_same::value + && !VlIsVlWide::value>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index, + const std::string& base_name, size_t& idx_width) { + VL_FATAL_MT(__FILE__, __LINE__, "randomize", + "Unsupported: Only integral and string index of associative array is " + "supported currently."); + } + + // --- write_var to register variables --- + // Register scalar variable (non-struct, basic type) + template + typename std::enable_if::value && !IsVlUnpacked::value, + void>::type + write_var(T& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + if (m_vars.find(name) != m_vars.end()) return; + // TODO: make_unique once VlRandomizer is per-instance not per-ref + m_vars[name] + = std::make_shared(name, width, &var, dimension, randmodeIdx); + } + + // Register user-defined struct variable by recursively writing members + template + typename std::enable_if::value, void>::type + write_var(T& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + modifyMembers(var, var.memberIndices(), name); + } + + // Register queue of non-struct types + template + typename std::enable_if::value, void>::type + write_var(VlQueue& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + if (m_vars.find(name) != m_vars.end()) return; + m_vars[name] = std::make_shared>>( + name, width, &var, dimension, randmodeIdx); + if (dimension > 0) { + m_index = 0; + record_arr_table(var, name, dimension, {}, {}); + } + } + + // Register queue of structs + template + typename std::enable_if::value, void>::type + write_var(VlQueue& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + if (dimension > 0) record_struct_arr(var, name, dimension, {}, {}); + } + // Register unpacked array of non-struct types + template + typename std::enable_if::value, void>::type + write_var(VlUnpacked& var, uint32_t width, const std::string& name, + uint32_t dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + + if (m_vars.find(name) != m_vars.end()) return; + + m_vars[name] = std::make_shared>>( + name, width, &var, dimension, randmodeIdx); + + if (dimension > 0) { + m_index = 0; + record_arr_table(var, name, dimension, {}, {}); + } + } + // Register unpacked array of structs + template + typename std::enable_if::value, void>::type + write_var(VlUnpacked& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + if (dimension > 0) record_struct_arr(var, name, dimension, {}, {}); + } + + // Register associative array of non-struct types + template + typename std::enable_if::value, void>::type + write_var(VlAssocArray& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + if (m_vars.find(name) != m_vars.end()) return; + m_vars[name] + = std::make_shared>>( + name, width, &var, dimension, randmodeIdx); + if (dimension > 0) { + m_index = 0; + record_arr_table(var, name, dimension, {}, {}); + } + } + + // Register associative array of structs + template + typename std::enable_if::value, void>::type + write_var(VlAssocArray& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + if (dimension > 0) record_struct_arr(var, name, dimension, {}, {}); + } + + // --- Record Arrays: flat and struct --- + + // Record a flat (non-class) element into the array variable table + template + typename std::enable_if::value || VlIsVlWide::value, void>::type + record_arr_table(T& var, const std::string& name, int dimension, std::vector indices, + std::vector idxWidths) { + const std::string key = generateKey(name, m_index); + m_arr_vars[key] = std::make_shared(name, &var, m_index, indices, idxWidths); + ++m_index; + } + + // This is the "Sender" API for the generated code + void rand_unique(const std::string& name, uint32_t size) { + m_unique_arrays.push_back(name); + m_unique_array_sizes[name] = size; + } + + // Recursively record all elements in an unpacked array + template + void record_arr_table(VlUnpacked& var, const std::string& name, int dimension, + std::vector indices, std::vector idxWidths) { + if ((dimension > 0) && (N_Depth != 0)) { + idxWidths.push_back(32); + for (size_t i = 0; i < N_Depth; ++i) { + const std::string indexed_name = name + "[" + std::to_string(i) + "]"; + indices.push_back(i); + record_arr_table(var.operator[](i), indexed_name, dimension - 1, indices, + idxWidths); + indices.pop_back(); + } + } + } + + // Recursively record all elements in a queue + template + void record_arr_table(VlQueue& var, const std::string& name, int dimension, + std::vector indices, std::vector idxWidths) { + if ((dimension > 0) && (var.size() != 0)) { + idxWidths.push_back(32); + for (size_t i = 0; i < var.size(); ++i) { + const std::string indexed_name = name + "[" + std::to_string(i) + "]"; + indices.push_back(i); + record_arr_table(var.atWrite(i), indexed_name, dimension - 1, indices, idxWidths); + indices.pop_back(); + } + } + } + + // Recursively record all elements in an associative array + template + void record_arr_table(VlAssocArray& var, const std::string& name, + int dimension, std::vector indices, + std::vector idxWidths) { + if ((dimension > 0) && (var.size() != 0)) { + for (auto it = var.begin(); it != var.end(); ++it) { + const T_Key& key = it->first; + const T_Value& value = it->second; + + std::string indexed_name; + std::vector integral_index; + size_t idx_width = 0; + + process_key(key, indexed_name, integral_index, name, idx_width); + + // Update indices and widths + idxWidths.push_back(idx_width); + indices.insert(indices.end(), integral_index.begin(), integral_index.end()); + + record_arr_table(var.at(key), indexed_name, dimension - 1, indices, idxWidths); + + // Cleanup indices and widths + idxWidths.pop_back(); + indices.resize(indices.size() - integral_index.size()); + } + } + } + + // Register a single structArray element via write_var + template + typename std::enable_if::value, void>::type + record_struct_arr(T& var, const std::string& name, int dimension, std::vector indices, + std::vector idxWidths) { + std::ostringstream oss; + for (size_t i = 0; i < indices.size(); ++i) { + oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0') + << static_cast(indices[i]); + if (i < indices.size() - 1) oss << "."; + } + write_var(var, 1ULL, + oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL); + } + + // Recursively process VlUnpacked of structs + template + void record_struct_arr(VlUnpacked& var, const std::string& name, int dimension, + std::vector indices, std::vector idxWidths) { + if (dimension > 0 && N_Depth != 0) { + constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1); + idxWidths.push_back(idx_width); + for (size_t i = 0; i < N_Depth; ++i) { + indices.push_back(i); + record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths); + indices.pop_back(); + } + } + } + + // Recursively process VlQueue of structs + template + void record_struct_arr(VlQueue& var, const std::string& name, int dimension, + std::vector indices, std::vector idxWidths) { + if ((dimension > 0) && (var.size() != 0)) { + idxWidths.push_back(32); + for (size_t i = 0; i < var.size(); ++i) { + indices.push_back(i); + record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths); + indices.pop_back(); + } + } + } + + // Recursively process associative arrays of structs + template + void record_struct_arr(VlAssocArray& var, const std::string& name, + int dimension, const std::vector& indices, + const std::vector& idxWidths) { + if ((dimension > 0) && (!var.empty())) { + for (auto it = var.begin(); it != var.end(); ++it) { + const T_Key& key = it->first; + const T_Value& value = it->second; + + std::string indexed_name; + std::vector integral_index; + size_t idx_width = 0; + + process_key(key, indexed_name, integral_index, name, idx_width); + std::ostringstream oss; + for (int i = 0; i < integral_index.size(); ++i) + oss << std::hex << static_cast(integral_index[i]); + + std::string result = oss.str(); + result.insert(result.begin(), int(idx_width / 4) - result.size(), '0'); + record_struct_arr(var.at(key), name + "." + result, dimension - 1, indices, + idxWidths); + } + } + } + + // --- Helper functions --- + + // Helper: Register all members of a user-defined struct + template + void modifyMembers(T& obj, std::index_sequence, const std::string& baseName) { + // Use the indices to access each member via std::get + (void)std::initializer_list{ + (write_var(std::get(obj.getMembers(obj)), obj.memberWidth()[I], + (baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I]), + 0)...}; + } + + // Helper: Generate unique variable key from name and index + std::string generateKey(const std::string& name, int idx) { + if (!name.empty() && name[0] == '\\') { + const size_t space_pos = name.find(' '); + return (space_pos != std::string::npos ? name.substr(0, space_pos) : name) + + std::to_string(idx); + } + const size_t bracket_pos = name.find('['); + return (bracket_pos != std::string::npos ? name.substr(0, bracket_pos) : name) + + std::to_string(idx); + } + + void hard(std::string&& constraint, const char* filename = "", uint32_t linenum = 0, + const char* source = ""); + void clearConstraints(); + void clearAll(); // Clear both constraints and variables + void markRandc(const char* name); // Mark variable as randc for cyclic tracking + void solveBefore(const char* beforeName, + const char* afterName); // Register solve-before ordering + void set_randmode(const VlQueue& randmode) { m_randmodep = &randmode; } +#ifdef VL_DEBUG + void dump() const; +#endif +}; + +//============================================================================= + +// Light wrapper for RNG used by std::randomize() to support scope-level randomization. +class VlStdRandomizer final : public VlRandomizer { + // MEMBERS + VlRNG m_rng; // Random number generator + +public: + // CONSTRUCTORS + VlStdRandomizer() = default; + ~VlStdRandomizer() = default; + +private: + // Wide type specialization (>64 bits) + template + typename std::enable_if::value, bool>::type + basicStdRandomizationImpl(T& value, size_t width) { + VL_RANDOM_RNG_W(m_rng, width, value); + // Mask off garbage bits in last word + const int words = VL_WORDS_I(width); + const int bitsInLastWord = width & VL_SIZEBITS_I; + if (bitsInLastWord) value.at(words - 1) &= VL_MASK_I(bitsInLastWord); + return true; + } + + // Scalar type specialization (<=64 bits) + template + typename std::enable_if::value, bool>::type + basicStdRandomizationImpl(T& value, size_t width) { + if (width <= 32) { + value = VL_MASK_I(width) & VL_RANDOM_RNG_I(m_rng); + } else { + value = VL_MASK_Q(width) & VL_RANDOM_RNG_Q(m_rng); + } + return true; + } + +public: + // Scalar/wide randomization + template + bool basicStdRandomization(T& value, size_t width) { + return basicStdRandomizationImpl(value, width); + } + + // Unpacked array randomization + template + bool basicStdRandomization(VlUnpacked& value, size_t width) { + for (size_t i = 0; i < N_Depth; ++i) { basicStdRandomization(value.operator[](i), width); } + return true; + } + + // Queue/dynamic array randomization + template + bool basicStdRandomization(VlQueue& value, size_t width) { + for (int i = 0; i < value.size(); ++i) { basicStdRandomization(value.atWrite(i), width); } + return true; + } + + // Associative array randomization + template + bool basicStdRandomization(VlAssocArray& value, size_t width) { + T_Key key; + for (int exists = value.first(key); exists; exists = value.next(key)) { + basicStdRandomization(value.at(key), width); + } + return true; + } + bool next() { return VlRandomizer::next(m_rng); } +}; + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.cpp new file mode 100644 index 00000000000..83237bb2f92 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.cpp @@ -0,0 +1,669 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated C++ tracing in SAIF format implementation code +/// +/// This file must be compiled and linked against all Verilated objects +/// that use --trace-saif. +/// +/// Use "verilator --trace-saif" to add this to the Makefile for the linker. +/// +//============================================================================= + +// clang-format off + +#include "verilatedos.h" +#include "verilated.h" +#include "verilated_saif_c.h" + +#include +#include +#include +#include + +#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) +# include +#else +# include +#endif + +#ifndef O_LARGEFILE // WIN32 headers omit this +# define O_LARGEFILE 0 +#endif +#ifndef O_NONBLOCK // WIN32 headers omit this +# define O_NONBLOCK 0 +#endif +#ifndef O_CLOEXEC // WIN32 headers omit this +# define O_CLOEXEC 0 +#endif + +// clang-format on + +//============================================================================= +// Specialization of the generics for this trace format + +#define VL_SUB_T VerilatedSaif +#define VL_BUF_T VerilatedSaifBuffer +#include "verilated_trace_imp.h" +#undef VL_SUB_T +#undef VL_BUF_T + +//============================================================================= +// VerilatedSaifActivityBit + +class VerilatedSaifActivityBit final { + // MEMBERS + bool m_lastVal = false; // Last emitted activity bit value + uint64_t m_highTime = 0; // Total time when bit was high + size_t m_transitions = 0; // Total number of bit transitions + +public: + // METHODS + VL_ATTR_ALWINLINE + void aggregateVal(uint64_t dt, bool newVal) { + m_transitions += newVal != m_lastVal ? 1 : 0; + m_highTime += m_lastVal ? dt : 0; + m_lastVal = newVal; + } + + // ACCESSORS + VL_ATTR_ALWINLINE bool bitValue() const { return m_lastVal; } + VL_ATTR_ALWINLINE uint64_t highTime() const { return m_highTime; } + VL_ATTR_ALWINLINE uint64_t toggleCount() const { return m_transitions; } +}; + +//============================================================================= +// VerilatedSaifActivityVar + +class VerilatedSaifActivityVar final { + // MEMBERS + uint64_t m_lastTime = 0; // Last time when variable value was updated + VerilatedSaifActivityBit* m_bits; // Pointer to variable bits objects + uint32_t m_width; // Width of variable (in bits) + +public: + // CONSTRUCTORS + VerilatedSaifActivityVar(uint32_t width, VerilatedSaifActivityBit* bits) + : m_bits{bits} + , m_width{width} {} + + VerilatedSaifActivityVar(VerilatedSaifActivityVar&&) = default; + VerilatedSaifActivityVar& operator=(VerilatedSaifActivityVar&&) = default; + + // METHODS + VL_ATTR_ALWINLINE void emitBit(uint64_t time, CData newval); + + template + VL_ATTR_ALWINLINE void emitData(uint64_t time, DataType newval, uint32_t bits) { + static_assert(std::is_integral::value, + "The emitted value must be of integral type"); + + const uint64_t dt = time - m_lastTime; + for (size_t i = 0; i < std::min(m_width, bits); ++i) { + m_bits[i].aggregateVal(dt, (newval >> i) & 1); + } + updateLastTime(time); + } + + VL_ATTR_ALWINLINE void emitWData(uint64_t time, const WData* newvalp, uint32_t bits); + VL_ATTR_ALWINLINE void updateLastTime(uint64_t val) { m_lastTime = val; } + + // ACCESSORS + VL_ATTR_ALWINLINE uint32_t width() const { return m_width; } + VL_ATTR_ALWINLINE VerilatedSaifActivityBit& bit(std::size_t index); + VL_ATTR_ALWINLINE uint64_t lastUpdateTime() const { return m_lastTime; } + +private: + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedSaifActivityVar); +}; + +//============================================================================= +// VerilatedSaifActivityScope + +class VerilatedSaifActivityScope final { + // MEMBERS + // Absolute path to the scope + std::string m_scopePath; + // Name of the activity scope + std::string m_scopeName; + // Array indices of child scopes + std::vector> m_childScopes{}; + // Children signals codes mapped to their names in the current scope + std::vector> m_childActivities{}; + // Parent scope pointer + VerilatedSaifActivityScope* m_parentScope = nullptr; + +public: + // CONSTRUCTORS + VerilatedSaifActivityScope(std::string scopePath, std::string name, + VerilatedSaifActivityScope* parentScope = nullptr) + : m_scopePath{std::move(scopePath)} + , m_scopeName{std::move(name)} + , m_parentScope{parentScope} {} + + VerilatedSaifActivityScope(VerilatedSaifActivityScope&&) = default; + VerilatedSaifActivityScope& operator=(VerilatedSaifActivityScope&&) = default; + + // METHODS + VL_ATTR_ALWINLINE void addChildScope(std::unique_ptr childScope) { + m_childScopes.emplace_back(std::move(childScope)); + } + VL_ATTR_ALWINLINE void addActivityVar(uint32_t code, std::string name) { + m_childActivities.emplace_back(code, std::move(name)); + } + VL_ATTR_ALWINLINE bool hasParent() const { return m_parentScope; } + + // ACCESSORS + VL_ATTR_ALWINLINE const std::string& path() const { return m_scopePath; } + VL_ATTR_ALWINLINE const std::string& name() const { return m_scopeName; } + VL_ATTR_ALWINLINE const std::vector>& + childScopes() const { + return m_childScopes; + } + VL_ATTR_ALWINLINE + const std::vector>& childActivities() const { + return m_childActivities; + } + VL_ATTR_ALWINLINE VerilatedSaifActivityScope* parentScope() const { return m_parentScope; } + +private: + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedSaifActivityScope); +}; + +//============================================================================= +// VerilatedSaifActivityAccumulator + +class VerilatedSaifActivityAccumulator final { + // Give access to the private activities + friend class VerilatedSaifBuffer; + friend class VerilatedSaif; + + // MEMBERS + // Map of scopes paths to codes of activities inside + std::unordered_map>> + m_scopeToActivities; + // Map of variables codes mapped to their activity objects + std::unordered_map m_activity; + // Memory pool for signals bits objects + std::vector> m_activityArena; + +public: + // METHODS + void declare(uint32_t code, const std::string& absoluteScopePath, std::string variableName, + int bits, bool array, int arraynum); + + // CONSTRUCTORS + VerilatedSaifActivityAccumulator() = default; + + VerilatedSaifActivityAccumulator(VerilatedSaifActivityAccumulator&&) = default; + VerilatedSaifActivityAccumulator& operator=(VerilatedSaifActivityAccumulator&&) = default; + +private: + VL_UNCOPYABLE(VerilatedSaifActivityAccumulator); +}; + +//============================================================================= +//============================================================================= +//============================================================================= +// VerilatedSaifActivityVar implementation + +VL_ATTR_ALWINLINE +void VerilatedSaifActivityVar::emitBit(const uint64_t time, const CData newval) { + assert(m_lastTime <= time); + m_bits[0].aggregateVal(time - m_lastTime, newval); + updateLastTime(time); +} + +VL_ATTR_ALWINLINE +void VerilatedSaifActivityVar::emitWData(const uint64_t time, const WData* newvalp, + const uint32_t bits) { + assert(m_lastTime <= time); + const uint64_t dt = time - m_lastTime; + for (std::size_t i = 0; i < std::min(m_width, bits); ++i) { + const size_t wordIndex = i / VL_EDATASIZE; + m_bits[i].aggregateVal(dt, (newvalp[wordIndex] >> VL_BITBIT_E(i)) & 1); + } + + updateLastTime(time); +} + +VerilatedSaifActivityBit& VerilatedSaifActivityVar::bit(const std::size_t index) { + assert(index < m_width); + return m_bits[index]; +} + +//============================================================================= +//============================================================================= +//============================================================================= +// VerilatedSaifActivityAccumulator implementation + +void VerilatedSaifActivityAccumulator::declare(uint32_t code, const std::string& absoluteScopePath, + std::string variableName, int bits, bool array, + int arraynum) { + const size_t block_size = 1024; + if (m_activityArena.empty() + || m_activityArena.back().size() + bits > m_activityArena.back().capacity()) { + m_activityArena.emplace_back(); + m_activityArena.back().reserve(block_size); + } + const size_t bitsIdx = m_activityArena.back().size(); + m_activityArena.back().resize(m_activityArena.back().size() + bits); + + if (array) { + variableName += '['; + variableName += std::to_string(arraynum); + variableName += ']'; + } + m_scopeToActivities[absoluteScopePath].emplace_back(code, variableName); + m_activity.emplace(code, VerilatedSaifActivityVar{static_cast(bits), + m_activityArena.back().data() + bitsIdx}); +} + +//============================================================================= +//============================================================================= +//============================================================================= +// VerilatedSaif implementation + +VerilatedSaif::VerilatedSaif(void* filep) { + m_activityAccumulators.emplace_back(std::make_unique()); +} + +void VerilatedSaif::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + if (isOpen()) return; + + m_filename = filename; // "" is ok, as someone may overload open + m_filep = ::open(m_filename.c_str(), + O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE | O_NONBLOCK | O_CLOEXEC, 0666); + m_isOpen = true; + + initializeSaifFileContents(); + + Super::traceInit(); +} + +void VerilatedSaif::initializeSaifFileContents() { + printStr("// Generated by verilated_saif\n"); + printStr("(SAIFILE\n"); + printStr("(SAIFVERSION \"2.0\")\n"); + printStr("(DIRECTION \"backward\")\n"); + printStr("(PROGRAM_NAME \"Verilator\")\n"); + printStr("(DIVIDER / )\n"); + printStr("(TIMESCALE "); + printStr(timeResStr()); + printStr(")\n"); +} + +void VerilatedSaif::emitTimeChange(uint64_t timeui) { m_time = timeui; } + +VerilatedSaif::~VerilatedSaif() { close(); } + +void VerilatedSaif::close() VL_MT_SAFE_EXCLUDES(m_mutex) { + // This function is on the flush() call path + const VerilatedLockGuard lock{m_mutex}; + if (!isOpen()) return; + + finalizeSaifFileContents(); + clearCurrentlyCollectedData(); + + writeBuffered(true); + ::close(m_filep); + m_isOpen = false; + + Super::closeBase(); +} + +void VerilatedSaif::finalizeSaifFileContents() { + printStr("(DURATION "); + printStr(std::to_string(currentTime())); + printStr(")\n"); + + incrementIndent(); + for (const auto& topScope : m_scopes) recursivelyPrintScopes(*topScope); + decrementIndent(); + + printStr(")\n"); // SAIFILE +} + +void VerilatedSaif::recursivelyPrintScopes(const VerilatedSaifActivityScope& scope) { + openInstanceScope(scope.name()); + printScopeActivities(scope); + for (const auto& childScope : scope.childScopes()) recursivelyPrintScopes(*childScope); + closeInstanceScope(); +} + +void VerilatedSaif::openInstanceScope(const std::string& instanceName) { + printIndent(); + printStr("(INSTANCE "); + printStr(instanceName); + printStr("\n"); + incrementIndent(); +} + +void VerilatedSaif::closeInstanceScope() { + decrementIndent(); + printIndent(); + printStr(")\n"); // INSTANCE +} + +void VerilatedSaif::printScopeActivities(const VerilatedSaifActivityScope& scope) { + bool anyNetWritten = false; + + for (auto& accumulator : m_activityAccumulators) { + anyNetWritten |= printScopeActivitiesFromAccumulatorIfPresent(scope.path(), *accumulator, + anyNetWritten); + } + + if (anyNetWritten) closeNetScope(); +} + +bool VerilatedSaif::printScopeActivitiesFromAccumulatorIfPresent( + const std::string& absoluteScopePath, VerilatedSaifActivityAccumulator& accumulator, + bool anyNetWritten) { + if (accumulator.m_scopeToActivities.count(absoluteScopePath) == 0) return false; + + for (const auto& childSignal : accumulator.m_scopeToActivities.at(absoluteScopePath)) { + VerilatedSaifActivityVar& activityVariable = accumulator.m_activity.at(childSignal.first); + anyNetWritten + = printActivityStats(activityVariable, childSignal.second.c_str(), anyNetWritten); + } + + return anyNetWritten; +} + +void VerilatedSaif::openNetScope() { + printIndent(); + printStr("(NET\n"); + incrementIndent(); +} + +void VerilatedSaif::closeNetScope() { + decrementIndent(); + printIndent(); + printStr(")\n"); // NET +} + +bool VerilatedSaif::printActivityStats(VerilatedSaifActivityVar& activity, + const std::string& activityName, bool anyNetWritten) { + for (size_t i = 0; i < activity.width(); ++i) { + VerilatedSaifActivityBit& bit = activity.bit(i); + + bit.aggregateVal(currentTime() - activity.lastUpdateTime(), bit.bitValue()); + + if (!anyNetWritten) { + openNetScope(); + anyNetWritten = true; + } + + printIndent(); + printStr("("); + printStr(activityName); + if (activity.width() > 1) { + printStr("\\["); + printStr(std::to_string(i)); + printStr("\\]"); + } + + // We only have two-value logic so TZ, TX and TB will always be 0 + printStr(" (T0 "); + printStr(std::to_string(currentTime() - bit.highTime())); + printStr(") (T1 "); + printStr(std::to_string(bit.highTime())); + printStr(") (TZ 0) (TX 0) (TB 0) (TC "); + printStr(std::to_string(bit.toggleCount())); + printStr("))\n"); + } + + activity.updateLastTime(currentTime()); + + return anyNetWritten; +} + +void VerilatedSaif::clearCurrentlyCollectedData() { + m_currentScope = nullptr; + m_scopes.clear(); + m_activityAccumulators.clear(); +} + +void VerilatedSaif::printStr(const char* str) { + m_buffer.append(str); + writeBuffered(false); +} + +void VerilatedSaif::printStr(const std::string& str) { + m_buffer.append(str); + writeBuffered(false); +} + +void VerilatedSaif::writeBuffered(bool force) { + if (VL_UNLIKELY(m_buffer.size() >= WRITE_BUFFER_SIZE || force)) { + if (VL_UNLIKELY(!m_buffer.empty())) { + ::write(m_filep, m_buffer.data(), m_buffer.size()); + m_buffer = ""; + m_buffer.reserve(WRITE_BUFFER_SIZE * 2); + } + } +} + +//============================================================================= +// Definitions + +void VerilatedSaif::flush() VL_MT_SAFE_EXCLUDES(m_mutex) { + const VerilatedLockGuard lock{m_mutex}; + Super::flushBase(); +} + +void VerilatedSaif::incrementIndent() { m_indent += 1; } + +void VerilatedSaif::decrementIndent() { m_indent -= 1; } + +void VerilatedSaif::printIndent() { + printStr(std::string(m_indent, ' ')); // Must use () constructor +} + +void VerilatedSaif::pushPrefix(const char* namep, VerilatedTracePrefixType type) { + assert(!m_prefixStack.empty()); // Constructor makes an empty entry + const std::string name{namep}; + // An empty name means this is the root of a model created with + // name()=="". The tools get upset if we try to pass this as empty, so + // we put the signals under a new $rootio scope, but the signals + // further down will be peers, not children (as usual for name()!=""). + const std::string prevPrefix = m_prefixStack.back().first; + if (name == "$rootio" && !prevPrefix.empty()) { + // Upper has name, we can suppress inserting $rootio, but still push so popPrefix works + m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER); + return; + } else if (name.empty()) { + m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER); + return; + } + + if (type != VerilatedTracePrefixType::ARRAY_UNPACKED + && type != VerilatedTracePrefixType::ARRAY_PACKED) { + + std::string scopePath = prevPrefix + name; + std::string scopeName = lastWord(scopePath); + + auto newScope = std::make_unique( + std::move(scopePath), std::move(scopeName), m_currentScope); + VerilatedSaifActivityScope* newScopePtr = newScope.get(); + + if (m_currentScope) { + m_currentScope->addChildScope(std::move(newScope)); + } else { + m_scopes.emplace_back(std::move(newScope)); + } + + m_currentScope = newScopePtr; + } + + const std::string newPrefix = prevPrefix + name; + bool properScope = (type != VerilatedTracePrefixType::ARRAY_UNPACKED + && type != VerilatedTracePrefixType::ARRAY_PACKED + && type != VerilatedTracePrefixType::ROOTIO_WRAPPER); + m_prefixStack.emplace_back(newPrefix + (properScope ? " " : ""), type); +} + +void VerilatedSaif::popPrefix() { + if (m_prefixStack.back().second != VerilatedTracePrefixType::ARRAY_UNPACKED + && m_prefixStack.back().second != VerilatedTracePrefixType::ARRAY_PACKED + && m_prefixStack.back().second != VerilatedTracePrefixType::ROOTIO_WRAPPER + && m_currentScope) { + m_currentScope = m_currentScope->parentScope(); + } + m_prefixStack.pop_back(); + assert(!m_prefixStack.empty()); // Always one left, the constructor's initial one +} + +void VerilatedSaif::declare(const uint32_t code, uint32_t fidx, const char* name, + const char* wirep, const bool array, const int arraynum, + const bool bussed, const int msb, const int lsb) { + assert(m_activityAccumulators.size() > fidx); + VerilatedSaifActivityAccumulator& accumulator = *m_activityAccumulators.at(fidx); + + const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1; + + const std::string hierarchicalName = m_prefixStack.back().first + name; + + if (!Super::declCode(code, hierarchicalName, bits)) return; + + std::string variableName = lastWord(hierarchicalName); + m_currentScope->addActivityVar(code, variableName); + + accumulator.declare(code, m_currentScope->path(), std::move(variableName), bits, array, + arraynum); +} + +void VerilatedSaif::declEvent(const uint32_t code, const uint32_t fidx, const char* name, + const int dtypenum, const VerilatedTraceSigDirection, + const VerilatedTraceSigKind, const VerilatedTraceSigType, + const bool array, const int arraynum) { + declare(code, fidx, name, "event", array, arraynum, false, 0, 0); +} + +void VerilatedSaif::declBit(const uint32_t code, const uint32_t fidx, const char* name, + const int dtypenum, const VerilatedTraceSigDirection, + const VerilatedTraceSigKind, const VerilatedTraceSigType, + const bool array, const int arraynum) { + declare(code, fidx, name, "wire", array, arraynum, false, 0, 0); +} +void VerilatedSaif::declBus(const uint32_t code, const uint32_t fidx, const char* name, + const int dtypenum, const VerilatedTraceSigDirection, + const VerilatedTraceSigKind, const VerilatedTraceSigType, + const bool array, const int arraynum, const int msb, const int lsb) { + declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb); +} +void VerilatedSaif::declQuad(const uint32_t code, const uint32_t fidx, const char* name, + const int dtypenum, const VerilatedTraceSigDirection, + const VerilatedTraceSigKind, const VerilatedTraceSigType, + const bool array, const int arraynum, const int msb, const int lsb) { + declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb); +} +void VerilatedSaif::declArray(const uint32_t code, const uint32_t fidx, const char* name, + const int dtypenum, const VerilatedTraceSigDirection, + const VerilatedTraceSigKind, const VerilatedTraceSigType, + const bool array, const int arraynum, const int msb, const int lsb) { + declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb); +} +void VerilatedSaif::declDouble(const uint32_t code, const uint32_t fidx, const char* name, + const int dtypenum, const VerilatedTraceSigDirection, + const VerilatedTraceSigKind, const VerilatedTraceSigType, + const bool array, const int arraynum) { + declare(code, fidx, name, "real", array, arraynum, false, 63, 0); +} + +//============================================================================= +// Get/commit trace buffer + +VerilatedSaif::Buffer* VerilatedSaif::getTraceBuffer(uint32_t fidx) { return new Buffer{*this}; } + +void VerilatedSaif::commitTraceBuffer(VerilatedSaif::Buffer* bufp) { delete bufp; } + +//============================================================================= +//============================================================================= +//============================================================================= +// VerilatedSaifBuffer implementation + +//============================================================================= +// emit* trace routines + +// Note: emit* are only ever called from one place (full* in +// verilated_trace_imp.h, which is included in this file at the top), +// so always inline them. + +VL_ATTR_ALWINLINE +void VerilatedSaifBuffer::emitEvent(const uint32_t code) { + // NOP +} + +VL_ATTR_ALWINLINE +void VerilatedSaifBuffer::emitBit(const uint32_t code, const CData newval) { + assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code) + && "Activity must be declared earlier"); + VerilatedSaifActivityVar& activity + = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code); + activity.emitBit(m_owner.currentTime(), newval); +} + +VL_ATTR_ALWINLINE +void VerilatedSaifBuffer::emitCData(const uint32_t code, const CData newval, const int bits) { + assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code) + && "Activity must be declared earlier"); + VerilatedSaifActivityVar& activity + = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code); + activity.emitData(m_owner.currentTime(), newval, bits); +} + +VL_ATTR_ALWINLINE +void VerilatedSaifBuffer::emitSData(const uint32_t code, const SData newval, const int bits) { + assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code) + && "Activity must be declared earlier"); + VerilatedSaifActivityVar& activity + = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code); + activity.emitData(m_owner.currentTime(), newval, bits); +} + +VL_ATTR_ALWINLINE +void VerilatedSaifBuffer::emitIData(const uint32_t code, const IData newval, const int bits) { + assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code) + && "Activity must be declared earlier"); + VerilatedSaifActivityVar& activity + = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code); + activity.emitData(m_owner.currentTime(), newval, bits); +} + +VL_ATTR_ALWINLINE +void VerilatedSaifBuffer::emitQData(const uint32_t code, const QData newval, const int bits) { + assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code) + && "Activity must be declared earlier"); + VerilatedSaifActivityVar& activity + = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code); + activity.emitData(m_owner.currentTime(), newval, bits); +} + +VL_ATTR_ALWINLINE +void VerilatedSaifBuffer::emitWData(const uint32_t code, const WData* newvalp, const int bits) { + assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code) + && "Activity must be declared earlier"); + VerilatedSaifActivityVar& activity + = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code); + activity.emitWData(m_owner.currentTime(), newvalp, bits); +} + +VL_ATTR_ALWINLINE +void VerilatedSaifBuffer::emitDouble(const uint32_t code, const double newval) { + // NOP +} diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.h new file mode 100644 index 00000000000..28f35166c93 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.h @@ -0,0 +1,292 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated tracing in SAIF format header +/// +/// User wrapper code should use this header when creating SAIF traces. +/// +//============================================================================= + +#ifndef VERILATOR_VERILATED_SAIF_C_H_ +#define VERILATOR_VERILATED_SAIF_C_H_ + +#include "verilated.h" +#include "verilated_trace.h" + +#include +#include +#include + +class VerilatedSaifBuffer; +class VerilatedSaifActivityAccumulator; +class VerilatedSaifActivityScope; +class VerilatedSaifActivityVar; +class VerilatedSaifActivityBit; + +//============================================================================= +// VerilatedSaif +// Base class to create a Verilator SAIF dump +// This is an internally used class - see VerilatedSaifC for what to call from applications + +class VerilatedSaif VL_NOT_FINAL : public VerilatedTrace { +public: + using Super = VerilatedTrace; + +private: + friend VerilatedSaifBuffer; // Give the buffer access to the private bits + + //========================================================================= + // SAIF-specific internals + + int m_filep = 0; // File we're writing to + bool m_isOpen = false; // True indicates open file + std::string m_filename; // Filename we're writing to (if open) + std::string m_buffer; // Write data buffer + + int m_indent = 0; // Indentation size in spaces + static constexpr size_t WRITE_BUFFER_SIZE = 256 * 1024; // Bytes between write calls + + // Currently active scope + VerilatedSaifActivityScope* m_currentScope = nullptr; + // Array of declared scopes + std::vector> m_scopes{}; + // Activity accumulators used to store variables statistics over simulation time + std::vector> m_activityAccumulators{}; + // Total time of the currently traced simulation + uint64_t m_time = 0; + + // Stack of declared scopes combined names + std::vector> m_prefixStack{ + {"", VerilatedTracePrefixType::SCOPE_MODULE}}; + + // METHODS + VL_ATTR_ALWINLINE uint64_t currentTime() const { return m_time; } + + void initializeSaifFileContents(); + void finalizeSaifFileContents(); + void recursivelyPrintScopes(const VerilatedSaifActivityScope& scope); + void openInstanceScope(const std::string& instanceName); + void closeInstanceScope(); + void printScopeActivities(const VerilatedSaifActivityScope& scope); + bool + printScopeActivitiesFromAccumulatorIfPresent(const std::string& absoluteScopePath, + VerilatedSaifActivityAccumulator& accumulator, + bool anyNetWritten); + void openNetScope(); + void closeNetScope(); + bool printActivityStats(VerilatedSaifActivityVar& activity, const std::string& activityName, + bool anyNetWritten); + + void incrementIndent(); + void decrementIndent(); + void printIndent(); + + void printStr(const char* str); + void printStr(const std::string& str); + void writeBuffered(bool force); + + void clearCurrentlyCollectedData(); + + void declare(uint32_t code, uint32_t fidx, const char* name, const char* wirep, bool array, + int arraynum, bool bussed, int msb, int lsb); + + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedSaif); + +protected: + //========================================================================= + // Implementation of VerilatedTrace interface + + // Called when the trace moves forward to a new time point + void emitTimeChange(uint64_t timeui) override; + + // Hooks called from VerilatedTrace + bool preFullDump() override { return isOpen(); } + bool preChangeDump() override { return isOpen(); } + + // Trace buffer management + Buffer* getTraceBuffer(uint32_t fidx) override; + void commitTraceBuffer(Buffer*) override; + + // Configure sub-class + void configure(const VerilatedTraceConfig&) override {} + +public: + //========================================================================= + // External interface to client code + + // CONSTRUCTOR + explicit VerilatedSaif(void* filep = nullptr); + ~VerilatedSaif(); + + // METHODS - All must be thread safe + // Open the file; call isOpen() to see if errors + void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex); + // Close the file + void close() VL_MT_SAFE_EXCLUDES(m_mutex); + // Flush any remaining data to this file + void flush() VL_MT_SAFE_EXCLUDES(m_mutex); + // Return if file is open + bool isOpen() const VL_MT_SAFE { return m_isOpen; } + + //========================================================================= + // Internal interface to Verilator generated code + + void pushPrefix(const char*, VerilatedTracePrefixType); + void popPrefix(); + + void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum); + void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum); + void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum, int msb, int lsb); + void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum, int msb, int lsb); + void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum, int msb, int lsb); + void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum, + VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType, + bool array, int arraynum); +}; + +#ifndef DOXYGEN +// Declare specialization here as it's used in VerilatedSaifC just below +template <> +void VerilatedSaif::Super::dump(uint64_t time); +template <> +void VerilatedSaif::Super::set_time_unit(const char* unitp); +template <> +void VerilatedSaif::Super::set_time_unit(const std::string& unit); +template <> +void VerilatedSaif::Super::set_time_resolution(const char* unitp); +template <> +void VerilatedSaif::Super::set_time_resolution(const std::string& unit); +template <> +void VerilatedSaif::Super::dumpvars(int level, const std::string& hier); +#endif // DOXYGEN + +//============================================================================= +// VerilatedSaifBuffer + +class VerilatedSaifBuffer VL_NOT_FINAL { + // Give the trace file and sub-classes access to the private bits + friend VerilatedSaif; + friend VerilatedSaif::Super; + friend VerilatedSaif::Buffer; + friend VerilatedSaif::OffloadBuffer; + + VerilatedSaif& m_owner; // Trace file owning this buffer. Required by subclasses. + uint32_t m_fidx; // Index of target activity accumulator + + // CONSTRUCTORS + explicit VerilatedSaifBuffer(VerilatedSaif& owner) + : m_owner{owner} + , m_fidx{0} {} + explicit VerilatedSaifBuffer(VerilatedSaif& owner, uint32_t fidx) + : m_owner{owner} + , m_fidx{fidx} {} + virtual ~VerilatedSaifBuffer() = default; + + //========================================================================= + // Implementation of VerilatedTraceBuffer interface + // Implementations of duck-typed methods for VerilatedTraceBuffer. These are + // called from only one place (the full* methods), so always inline them. + VL_ATTR_ALWINLINE void emitEvent(uint32_t code); + VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval); + VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits); + VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits); + VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits); + VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits); + VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits); + VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval); +}; + +//============================================================================= +// VerilatedSaifC +// Class representing a SAIF dump file in C standalone (no SystemC) +// simulations. Also derived for use in SystemC simulations. + +class VerilatedSaifC VL_NOT_FINAL : public VerilatedTraceBaseC { + VerilatedSaif m_sptrace; // Trace file being created + + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedSaifC); + +public: + // Construct the dump. Optional argument is ignored + explicit VerilatedSaifC(void* filep = nullptr) + : m_sptrace{filep} {} + // Destruct, flush, and close the dump + virtual ~VerilatedSaifC() { close(); } + + // METHODS - User called + + // Return if file is open + bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); } + // Open a new SAIF file + // This includes a complete header dump each time it is called, + // just as if this object was deleted and reconstructed. + virtual void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); } + + void rolloverSize(size_t size) VL_MT_SAFE {} // NOP + + // Close dump + void close() VL_MT_SAFE { + m_sptrace.close(); + modelConnected(false); + } + // Flush dump + void flush() VL_MT_SAFE { m_sptrace.flush(); } + // Write one cycle of dump data + // Call with the current context's time just after eval'ed, + // e.g. ->dump(contextp->time()) + void dump(uint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); } + // Write one cycle of dump data - backward compatible and to reduce + // conversion warnings. It's better to use a uint64_t time instead. + void dump(double timestamp) { dump(static_cast(timestamp)); } + void dump(uint32_t timestamp) { dump(static_cast(timestamp)); } + void dump(int timestamp) { dump(static_cast(timestamp)); } + + // METHODS - Internal/backward compatible + // \protectedsection + + // Set time units (s/ms, defaults to ns) + // Users should not need to call this, as for Verilated models, these + // propagate from the Verilated default timeunit + void set_time_unit(const char* unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); } + void set_time_unit(const std::string& unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); } + // Set time resolution (s/ms, defaults to ns) + // Users should not need to call this, as for Verilated models, these + // propagate from the Verilated default timeprecision + void set_time_resolution(const char* unit) VL_MT_SAFE { m_sptrace.set_time_resolution(unit); } + void set_time_resolution(const std::string& unit) VL_MT_SAFE { + m_sptrace.set_time_resolution(unit); + } + // Set variables to dump, using $dumpvars format + // If level = 0, dump everything and hier is then ignored + void dumpvars(int level, const std::string& hier) VL_MT_SAFE { + m_sptrace.dumpvars(level, hier); + } + + // Internal class access + VerilatedSaif* spTrace() { return &m_sptrace; } +}; + +#endif // guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_sc.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_sc.h new file mode 100644 index 00000000000..e0c5a50fad6 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_sc.h @@ -0,0 +1,56 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated tracing in SAIF format for SystemC header +/// +/// User wrapper code should use this header when creating SAIF SystemC traces. +/// +/// This class is not threadsafe, as the SystemC kernel is not threadsafe. +/// +//============================================================================= + +#ifndef VERILATOR_VERILATED_SAIF_SC_H_ +#define VERILATOR_VERILATED_SAIF_SC_H_ + +#include "verilatedos.h" + +#include "verilated_saif_c.h" +#include "verilated_sc_trace.h" + +//============================================================================= +// VerilatedSaifSc +/// Trace file used to create SAIF dump for SystemC version of Verilated models. It's very similar +/// to its C version (see the class VerilatedSaifC) + +class VerilatedSaifSc final : VerilatedScTraceBase, public VerilatedSaifC { + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedSaifSc); + +public: + VerilatedSaifSc() { + spTrace()->set_time_unit(VerilatedScTraceBase::getScTimeUnit()); + spTrace()->set_time_resolution(VerilatedScTraceBase::getScTimeResolution()); + } + + // METHODS + // Override VerilatedSaifC. Must be called after starting simulation. + void open(const char* filename) override VL_MT_SAFE { + VerilatedScTraceBase::checkScElaborationDone(); + VerilatedSaifC::open(filename); + } + + // METHODS - for SC kernel + // Called from SystemC kernel + void cycle() override { VerilatedSaifC::dump(sc_core::sc_time_stamp().to_double()); } +}; + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.cpp new file mode 100644 index 00000000000..0569ba0aa3c --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.cpp @@ -0,0 +1,270 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated save/restore implementation code +/// +/// This file must be compiled and linked against all Verilated objects +/// that use --savable. +/// +/// Use "verilator --savable" to add this to the Makefile for the linker. +/// +//============================================================================= + +#define VERILATOR_VERILATED_SAVE_CPP_ + +#include "verilatedos.h" + +#include "verilated_save.h" + +#include "verilated.h" +#include "verilated_imp.h" + +#include +#include + +// clang-format off +#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__) +# include +#else +# include +#endif + +#ifndef O_LARGEFILE // WIN32 headers omit this +# define O_LARGEFILE 0 +#endif +#ifndef O_NONBLOCK // WIN32 headers omit this +# define O_NONBLOCK 0 +#endif +#ifndef O_CLOEXEC // WIN32 headers omit this +# define O_CLOEXEC 0 +#endif +// clang-format on + +// CONSTANTS +// Value of first bytes of each file (must be multiple of 8 bytes) +static const char* const VLTSAVE_HEADER_STR = "verilatorsave02\n"; +// Value of last bytes of each file (must be multiple of 8 bytes) +static const char* const VLTSAVE_TRAILER_STR = "vltsaved"; + +//============================================================================= +//============================================================================= +//============================================================================= +// Serialization + +bool VerilatedDeserialize::readDiffers(const void* __restrict datap, + size_t size) VL_MT_UNSAFE_ONE { + bufferCheck(); + const uint8_t* __restrict dp = static_cast(datap); + uint8_t miss = 0; + while (size--) miss |= (*dp++ ^ *m_cp++); + return (miss != 0); +} + +VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap, + size_t size) VL_MT_UNSAFE_ONE { + if (VL_UNLIKELY(readDiffers(datap, size))) { + const std::string fn = filename(); + const std::string msg + = "Can't deserialize save-restore file as was made from different model: " + + filename(); + VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str()); + // Die before we close() as close would infinite loop + } + return *this; // For function chaining +} + +void VerilatedSerialize::header() VL_MT_UNSAFE_ONE { + VerilatedSerialize& os = *this; // So can cut and paste standard << code below + assert((std::strlen(VLTSAVE_HEADER_STR) & 7) == 0); // Keep aligned + os.write(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)); +} + +void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE { + VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below + if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) { + const std::string fn = filename(); + const std::string msg + = "Can't deserialize; file has wrong header signature, or file not found: "s + + filename(); + VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str()); + // Die before we close() as close would infinite loop + } +} + +void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE { + VerilatedSerialize& os = *this; // So can cut and paste standard << code below + assert((std::strlen(VLTSAVE_TRAILER_STR) & 7) == 0); // Keep aligned + os.write(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR)); +} + +void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE { + VerilatedDeserialize& os = *this; // So can cut and paste standard >> code below + if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR)))) { + const std::string fn = filename(); + const std::string msg + = "Can't deserialize; file has wrong end-of-file signature: "s + filename(); + VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str()); + // Die before we close() as close would infinite loop + } +} + +//============================================================================= +//============================================================================= +//============================================================================= +// Opening/Closing + +void VerilatedSave::open(const char* filenamep) VL_MT_UNSAFE_ONE { + m_assertOne.check(); + if (isOpen()) return; + VL_DEBUG_IF(VL_DBG_MSGF("- save: opening save file %s\n", filenamep);); + + if (VL_UNCOVERABLE(filenamep[0] == '|')) { + assert(0); // LCOV_EXCL_LINE // Not supported yet. + } else { + // cppcheck-suppress duplicateExpression + m_fd = ::open(filenamep, + O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE | O_NONBLOCK | O_CLOEXEC, 0666); + if (VL_UNLIKELY(m_fd < 0)) { + // User code can check isOpen() + m_isOpen = false; + return; + } + } + m_isOpen = true; + m_filename = filenamep; + m_cp = m_bufp; + header(); +} + +void VerilatedRestore::open(const char* filenamep) VL_MT_UNSAFE_ONE { + m_assertOne.check(); + if (isOpen()) return; + VL_DEBUG_IF(VL_DBG_MSGF("- restore: opening restore file %s\n", filenamep);); + + if (VL_UNCOVERABLE(filenamep[0] == '|')) { + assert(0); // LCOV_EXCL_LINE // Not supported yet. + } else { + // cppcheck-suppress duplicateExpression + m_fd = ::open(filenamep, O_CREAT | O_RDONLY | O_LARGEFILE | O_CLOEXEC, 0666); + if (VL_UNLIKELY(m_fd < 0)) { + // User code can check isOpen() + m_isOpen = false; + return; + } + } + m_isOpen = true; + m_filename = filenamep; + m_cp = m_bufp; + m_endp = m_bufp; + header(); +} + +void VerilatedSave::closeImp() VL_MT_UNSAFE_ONE { + if (!isOpen()) return; + trailer(); + flushImp(); + m_isOpen = false; + ::close(m_fd); // May get error, just ignore it +} + +void VerilatedRestore::closeImp() VL_MT_UNSAFE_ONE { + if (!isOpen()) return; + trailer(); + flushImp(); + m_isOpen = false; + ::close(m_fd); // May get error, just ignore it +} + +//============================================================================= +// Buffer management + +void VerilatedSave::flushImp() VL_MT_UNSAFE_ONE { + m_assertOne.check(); + if (VL_UNLIKELY(!isOpen())) return; + const uint8_t* wp = m_bufp; + while (true) { + const ssize_t remaining = (m_cp - wp); + if (remaining == 0) break; + errno = 0; + const ssize_t got = ::write(m_fd, wp, remaining); + if (got > 0) { + wp += got; + } else if (VL_UNCOVERABLE(got < 0)) { + if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) { + // LCOV_EXCL_START + // write failed, presume error (perhaps out of disk space) + const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno); + VL_FATAL_MT("", 0, "", msg.c_str()); + close(); + break; + // LCOV_EXCL_STOP + } + } + } + m_cp = m_bufp; // Reset buffer +} + +void VerilatedRestore::fill() VL_MT_UNSAFE_ONE { + m_assertOne.check(); + if (VL_UNLIKELY(!isOpen())) return; + // Move remaining characters down to start of buffer. (No memcpy, overlaps allowed) + uint8_t* rp = m_bufp; + for (const uint8_t* sp = m_cp; sp < m_endp; *rp++ = *sp++) {} // Overlaps + m_endp = m_bufp + (m_endp - m_cp); + m_cp = m_bufp; // Reset buffer + // Read into buffer starting at m_endp + while (true) { + const ssize_t remaining = (m_bufp + bufferSize() - m_endp); + if (remaining == 0) break; + errno = 0; + const ssize_t got = ::read(m_fd, m_endp, remaining); + if (got > 0) { + m_endp += got; + } else if (VL_UNCOVERABLE(got < 0)) { + if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) { + // LCOV_EXCL_START + // write failed, presume error (perhaps out of disk space) + const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno); + VL_FATAL_MT("", 0, "", msg.c_str()); + close(); + break; + // LCOV_EXCL_STOP + } + } else { // got==0, EOF + // Fill buffer from here to end with NULLs so reader's don't + // need to check eof each character. + while (m_endp < m_bufp + bufferSize()) *m_endp++ = '\0'; + break; + } + } +} + +//============================================================================= +// Serialization of types + +VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp) { + os.write(rhsp->serialized1Ptr(), rhsp->serialized1Size()); + os << rhsp->impp()->timeFormatSuffix(); + os << rhsp->dumpfile(); + return os; +} +VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp) { + os.read(rhsp->serialized1Ptr(), rhsp->serialized1Size()); + std::string s; + os >> s; + rhsp->impp()->timeFormatSuffix(s); + os >> s; + rhsp->dumpfile(s); + return os; +} diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.h new file mode 100644 index 00000000000..aa547af68c6 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.h @@ -0,0 +1,335 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2000-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated save-restore serialization header +/// +/// This must be included in user wrapper code that wants to use +/// save/restore. +/// +//============================================================================= + +#ifndef VERILATOR_VERILATED_SAVE_C_H_ +#define VERILATOR_VERILATED_SAVE_C_H_ + +#include "verilatedos.h" + +#include "verilated.h" + +#include + +//============================================================================= +// VerilatedSerialize +/// Class for writing serialization of structures to a stream representation. +/// +/// User wrapper code will more typically use VerilatedSave which uses this +/// as a subclass to write a file. +/// +/// This class is not thread safe, it must be called by a single thread + +class VerilatedSerialize VL_NOT_FINAL { +protected: + // MEMBERS + // For speed, keep m_cp as the first member of this structure + uint8_t* m_cp; // Current pointer into m_bufp buffer + uint8_t* m_bufp; // Output buffer + bool m_isOpen = false; // True indicates open file/stream + std::string m_filename; // Filename, for error messages + VerilatedAssertOneThread m_assertOne; // Assert only called from single thread + + static constexpr size_t bufferSize() { + return 256 * 1024L; + } // See below for slack calculation + static constexpr size_t bufferInsertSize() { return 16 * 1024L; } + + void header() VL_MT_UNSAFE_ONE; + void trailer() VL_MT_UNSAFE_ONE; + + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedSerialize); + +public: + /// Construct + VerilatedSerialize() { + m_bufp = new uint8_t[bufferSize()]; + m_cp = m_bufp; + } + /// Flush, close, and destruct + virtual ~VerilatedSerialize() { + // Child classes will need to typically call closeImp() in destructors + if (m_bufp) VL_DO_CLEAR(delete[] m_bufp, m_bufp = nullptr); + } + // METHODS + /// Return true if file is open + bool isOpen() const { return m_isOpen; } + /// Return current filename + std::string filename() const { return m_filename; } + /// Close the stream + virtual void close() VL_MT_UNSAFE_ONE { flush(); } + /// Flush pending data to stream + virtual void flush() VL_MT_UNSAFE_ONE {} + /// Write data to stream + VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE { + const uint8_t* __restrict dp = static_cast(datap); + while (size) { + bufferCheck(); + size_t blk = size; + if (blk > bufferInsertSize()) blk = bufferInsertSize(); + const uint8_t* __restrict maxp = dp + blk; + for (; dp < maxp; *m_cp++ = *dp++) {} + size -= blk; + } + return *this; // For function chaining + } + +private: + VerilatedSerialize& bufferCheck() VL_MT_UNSAFE_ONE { + // Flush the write buffer if there's not enough space left for new information + // We only call this once per vector, so we need enough slop for a very wide "b###" line + if (VL_UNLIKELY(m_cp > (m_bufp + (bufferSize() - bufferInsertSize())))) flush(); + return *this; // For function chaining + } +}; + +//============================================================================= +// VerilatedDeserialize +/// Class for loading structures from a stream representation. +/// +/// User wrapper code will more typically use VerilatedRestore which uses +/// this as a subclass to a read from a file. +/// +/// This class is not thread safe, it must be called by a single thread + +class VerilatedDeserialize VL_NOT_FINAL { +protected: + // MEMBERS + // For speed, keep m_cp as the first member of this structure + uint8_t* m_cp; // Current pointer into m_bufp buffer + uint8_t* m_bufp; // Output buffer + uint8_t* m_endp = nullptr; // Last valid byte in m_bufp buffer + bool m_isOpen = false; // True indicates open file/stream + std::string m_filename; // Filename, for error messages + VerilatedAssertOneThread m_assertOne; // Assert only called from single thread + + static constexpr size_t bufferSize() { + return 256 * 1024L; + } // See below for slack calculation + static constexpr size_t bufferInsertSize() { return 16 * 1024L; } + + virtual void fill() = 0; + void header() VL_MT_UNSAFE_ONE; + void trailer() VL_MT_UNSAFE_ONE; + + // CONSTRUCTORS + VL_UNCOPYABLE(VerilatedDeserialize); + +public: + /// Construct + VerilatedDeserialize() { + m_bufp = new uint8_t[bufferSize()]; + m_cp = m_bufp; + } + /// Destruct + virtual ~VerilatedDeserialize() { + // Child classes will need to typically call closeImp() in destructors + if (m_bufp) VL_DO_CLEAR(delete[] m_bufp, m_bufp = nullptr); + } + // METHODS + /// Return true if file is open + bool isOpen() const { return m_isOpen; } + /// Return current filename + std::string filename() const { return m_filename; } + /// Close the stream + virtual void close() VL_MT_UNSAFE_ONE { flush(); } + /// Flush pending data to stream + virtual void flush() VL_MT_UNSAFE_ONE {} + /// Read data from stream + VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE { + uint8_t* __restrict dp = static_cast(datap); + while (size) { + bufferCheck(); + size_t blk = size; + if (blk > bufferInsertSize()) blk = bufferInsertSize(); + const uint8_t* __restrict maxp = dp + blk; + for (; dp < maxp; *dp++ = *m_cp++) {} + size -= blk; + } + return *this; // For function chaining + } + + // Internal use: + // Read a datum and compare with expected value + VerilatedDeserialize& readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE; + VerilatedDeserialize& readAssert(uint64_t data) VL_MT_UNSAFE_ONE { + return readAssert(&data, sizeof(data)); + } + +private: + bool readDiffers(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE; + VerilatedDeserialize& bufferCheck() VL_MT_UNSAFE_ONE { + // Flush the write buffer if there's not enough space left for new information + // We only call this once per vector, so we need enough slop for a very wide "b###" line + if (VL_UNLIKELY((m_cp + bufferInsertSize()) > m_endp)) fill(); + return *this; // For function chaining + } +}; + +//============================================================================= +// VerilatedSave +/// Stream-like object that serializes Verilated model to a file. +/// +/// This class is not thread safe, it must be called by a single thread + +class VerilatedSave final : public VerilatedSerialize { +private: + int m_fd = -1; // File descriptor we're writing to + + void closeImp() VL_MT_UNSAFE_ONE; + void flushImp() VL_MT_UNSAFE_ONE; + +public: + // CONSTRUCTORS + /// Construct new object + VerilatedSave() = default; + /// Flush, close and destruct + ~VerilatedSave() override { closeImp(); } + // METHODS + /// Open the file; call isOpen() to see if errors + void open(const char* filenamep) VL_MT_UNSAFE_ONE; + /// Open the file; call isOpen() to see if errors + void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); } + /// Flush and close the file + void close() override VL_MT_UNSAFE_ONE { closeImp(); } + /// Flush data to file + void flush() override VL_MT_UNSAFE_ONE { flushImp(); } +}; + +//============================================================================= +// VerilatedRestore +/// Stream-like object that serializes Verilated model from a file. +/// +/// This class is not thread safe, it must be called by a single thread + +class VerilatedRestore final : public VerilatedDeserialize { +private: + int m_fd = -1; // File descriptor we're writing to + + void closeImp() VL_MT_UNSAFE_ONE; + void flushImp() VL_MT_UNSAFE_ONE {} + +public: + // CONSTRUCTORS + /// Construct new object + VerilatedRestore() = default; + /// Flush, close and destruct + ~VerilatedRestore() override { closeImp(); } + + // METHODS + /// Open the file; call isOpen() to see if errors + void open(const char* filenamep) VL_MT_UNSAFE_ONE; + /// Open the file; call isOpen() to see if errors + void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); } + /// Close the file + void close() override VL_MT_UNSAFE_ONE { closeImp(); } + void flush() override VL_MT_UNSAFE_ONE { flushImp(); } + void fill() override VL_MT_UNSAFE_ONE; +}; + +//============================================================================= + +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint64_t& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint64_t& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint32_t& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint32_t& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint16_t& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint16_t& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint8_t& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint8_t& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const bool& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, bool& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const double& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, double& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const float& rhs) { + return os.write(&rhs, sizeof(rhs)); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) { + return os.read(&rhs, sizeof(rhs)); +} +inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const std::string& rhs) { + const uint32_t len = rhs.length(); + os << len; + return os.write(rhs.data(), len); +} +inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& rhs) { + uint32_t len = 0; + os >> len; + rhs.resize(len); + // cppcheck-suppress cstyleCast // NOLINTNEXTLINE(google-readability-casting) + return os.read((void*)(rhs.data()), len); +} +VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp); +VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp); + +template +VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray& rhs) { + os << rhs.atDefault(); + const uint32_t len = rhs.size(); + os << len; + for (const auto& i : rhs) { + const T_Key index = i.first; // Copy to get around const_iterator + const T_Value value = i.second; + os << index << value; + } + return os; +} +template +VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VlAssocArray& rhs) { + os >> rhs.atDefault(); + uint32_t len = 0; + os >> len; + rhs.clear(); + for (uint32_t i = 0; i < len; ++i) { + T_Key index; + T_Value value; + os >> index; + os >> value; + rhs.at(index) = value; + } + return os; +} + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc.h new file mode 100644 index 00000000000..d59a9f5c92a --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc.h @@ -0,0 +1,52 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2009-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated SystemC header for all Verilated SystemC files +/// +/// This file is included automatically by Verilator at the top of all +/// SystemC files it generates. It contains functions Verilated code uses +/// internally to connect the Verilated model into SystemC. +/// +/// User wrapper code is not required to include nor use anything in this +/// header, but may prefer to include it in place of "verilated.h" when +/// using Verilator with SystemC. +/// +//************************************************************************* + +#ifndef VERILATOR_VERILATED_SC_H_ +#define VERILATOR_VERILATED_SC_H_ + +#include "verilatedos.h" + +#include + +//============================================================================= +// For \internal use, get a pointer to m_data in the sc_bv_base class, +// getting around that it is protected. So make an exposing class, then +// use cast magic to get at it. Saves patching get_datap in SystemC. +#define VL_SC_BV_DATAP(bv) (VlScBvExposer::sp_datap(bv)) +// This class is thread safe (though most of SystemC is not). +class VlScBvExposer final : public sc_dt::sc_bv_base { +public: + static const uint32_t* sp_datap(const sc_dt::sc_bv_base& base) VL_MT_SAFE { + return static_cast(&base)->sp_datatp(); + } + const uint32_t* sp_datatp() const { return reinterpret_cast(m_data); } + // Above reads this protected element in sc_bv_base: + // sc_digit* m_data; // data array +}; + +//========================================================================= + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc_trace.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc_trace.h new file mode 100644 index 00000000000..5f53b103cec --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc_trace.h @@ -0,0 +1,247 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated tracing for SystemC implementation code +/// +/// +/// +//============================================================================= + +#ifndef VERILATOR_VERILATED_SC_TRACE_H_ +#define VERILATOR_VERILATED_SC_TRACE_H_ + +#include "verilatedos.h" + +#include "verilated.h" +#include "verilated_sc.h" + +#if SYSTEMC_VERSION >= 20140417 && SYSTEMC_VERSION < 20231124 +// SystemC's simulation phase callback introduced in 2.3.1, and removed since 3.0.0 (PubRev) +#define _VL_HAVE_SYSTEMC_PHASE_CALLBACK +#endif +#if SYSTEMC_VERSION >= 20231124 +// SystemC's stage callback introduced in 3.0.0 (PubRev) +#define _VL_HAVE_SYSTEMC_STAGE_CALLBACK +#endif + +//============================================================================= +// VerilatedScTraceBase +// Base class for VCD/FST trace format on SystemC +// This is an internally used class - see VerilatedVcdSc and VerilatedFstSc for what to call from +// applications +// +/// This class utilizes SystemC's callbacks, which allows to dump signals inside the Verilated +/// module automatically as time advances. +/// +/// For SystemC prior to 2.3.1, the only approach for being notified after each update is by adding +/// a trace file (sc_trace_file) to the simulation context. And after this version the simulation +/// phase callback approach has been introduced (sc_trace_file also utilizes this), which is +/// presented only if it's enabled with the `--enable-phase-callbacks` option. However, when it's +/// enabled with `--enable-phase-callbacks=tracing`, trace files will be therefore disabled, thus +/// failing to provide its functionality. +/// +/// To provide a universal way for tracing, the class attempts to register a phase callback first. +/// If it fails (proving that the feature has been disabled), it'll use the trace file approach +/// instead. + +class VerilatedScTraceBase VL_NOT_FINAL : private sc_core::sc_object, +#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK + private sc_core::sc_stage_callback_if, +#endif + private sc_core::sc_trace_file { + bool m_enableDeltaCycles = false; + bool m_traceFileAdded = false; + static void stubReportHandler(const sc_core::sc_report&, const sc_core::sc_actions&) {}; + +public: + void enableDeltaCycles(bool flag = true) { + using namespace sc_core; +#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK + // Save old report handler before overriding it + const auto oldHandler = sc_report_handler::get_handler(); + // Override the old handler to hide 'phase callbacks not enabled' message + sc_report_handler::set_handler(&stubReportHandler); + if (flag) { + // Register simulation phase callback for delta cycles + sc_object::register_simulation_phase_callback(SC_END_OF_UPDATE); + } else { + sc_object::unregister_simulation_phase_callback(SC_END_OF_UPDATE); + } + // Restore the old handler + sc_report_handler::set_handler(oldHandler); +#endif +#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK + if (flag) { + sc_register_stage_callback(*this, SC_POST_UPDATE); + } else { + sc_unregister_stage_callback(*this, SC_POST_UPDATE); + } +#endif + m_enableDeltaCycles = flag; + } + +protected: + VerilatedScTraceBase() + : sc_object(sc_core::sc_gen_unique_name("$$$$verilator_sc_trace$$$$")) + , sc_trace_file() { + registerTraceCallback(); + }; + ~VerilatedScTraceBase() override { + using namespace sc_core; +#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK + // remove_trace_file added in 2.3.1 and removed in 3.0.0 + // Phase callback is automatically unregistered in ~sc_object(). Only the trace file is + // needed to be removed here + if (m_traceFileAdded) simcontext()->remove_trace_file(this); +#else + (void)m_traceFileAdded; +#endif +#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK + sc_unregister_stage_callback(*this, SC_PRE_TIMESTEP | SC_POST_UPDATE); +#endif + }; + void registerTraceCallback() { + using namespace sc_core; +#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK + // Save old report handler before overriding it + const auto oldHandler = sc_report_handler::get_handler(); + // Override the old handler to hide 'phase callbacks not enabled' message + sc_report_handler::set_handler(&stubReportHandler); + // Register regular simulation phase (non-delta cycle) callback + phase_cb_mask cb_mask = sc_object::register_simulation_phase_callback(SC_BEFORE_TIMESTEP); + if (cb_mask == SC_UNITIALIZED) { +#endif +#if SYSTEMC_VERSION < 20231124 // add_trace_file removed in 3.0.0 + // Phase callback not enabled, use trace file instead + simcontext()->add_trace_file(this); + m_traceFileAdded = true; +#endif +#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK + } + // Restore the old handler + sc_report_handler::set_handler(oldHandler); +#endif +#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK + sc_register_stage_callback(*this, SC_PRE_TIMESTEP); +#endif + } + static std::string getScTimeUnit() { + // We want to avoid a depreciated warning, but still be back compatible. + // Turning off the message just for this still results in an + // annoying "to turn off" message. + const sc_core::sc_time t1sec{1, sc_core::SC_SEC}; + if (t1sec.to_default_time_units() == 0) { + VL_FATAL_MT(__FILE__, __LINE__, "", // LCOV_EXCL_LINE + "Cannot to get valid SystemC default time unit for trace file"); + } + const sc_core::sc_time tunits{1.0 / t1sec.to_default_time_units(), sc_core::SC_SEC}; + return tunits.to_string(); + } + static std::string getScTimeResolution() { + return sc_core::sc_get_time_resolution().to_string(); + } + static void checkScElaborationDone() { + if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) { + Verilated::scTraceBeforeElaborationError(); + } + } + + // METHODS - for SC kernel +#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK + // Override sc_object. Called if using phase callback + void simulation_phase_callback() final { cycle(); } +#endif +#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK + // Override sc_stage_callback_if. Called if using stage callback + void stage_callback(const sc_core::sc_stage&) final { cycle(); } +#endif + // Override sc_trace_file. Called if using trace file + void cycle(bool delta_cycle) final { + if (!delta_cycle || m_enableDeltaCycles) cycle(); + } + // METHODS - callbacks + // Subclasses should implement this callback method + virtual void cycle() = 0; + +private: + // METHODS - Fake outs for linker + // LCOV_EXCL_START + +#ifdef NC_SYSTEMC + // Cadence Incisive has these as abstract functions so we must create them + void set_time_unit(int exponent10_seconds) override {} // deprecated +#endif + void set_time_unit(double v, sc_core::sc_time_unit tu) override {} + + //-------------------------------------------------- + // SystemC 2.1.v1 + + void write_comment(const std::string&) override {} + void trace(sc_core::sc_trace_file*) const override {} + void trace(const unsigned int&, const std::string&, const char**) override {} + +#define DECL_TRACE_METHOD_A(tp) \ + void trace(const tp& object, const std::string& name) override {} +#define DECL_TRACE_METHOD_B(tp) \ + void trace(const tp& object, const std::string& name, int width) override {} + + // clang-format off + // Formatting matches that of sc_trace.h +#if SYSTEMC_VERSION >= 20171012 // SystemC >= 2.3.2 + DECL_TRACE_METHOD_A( sc_core::sc_event ) + DECL_TRACE_METHOD_A( sc_core::sc_time ) +#endif + + DECL_TRACE_METHOD_A( bool ) + DECL_TRACE_METHOD_A( sc_dt::sc_bit ) + DECL_TRACE_METHOD_A( sc_dt::sc_logic ) + + DECL_TRACE_METHOD_B( unsigned char ) + DECL_TRACE_METHOD_B( unsigned short ) + DECL_TRACE_METHOD_B( unsigned int ) + DECL_TRACE_METHOD_B( unsigned long ) + DECL_TRACE_METHOD_B( char ) + DECL_TRACE_METHOD_B( short ) + DECL_TRACE_METHOD_B( int ) + DECL_TRACE_METHOD_B( long ) + DECL_TRACE_METHOD_B( sc_dt::int64 ) + DECL_TRACE_METHOD_B( sc_dt::uint64 ) + + DECL_TRACE_METHOD_A( float ) + DECL_TRACE_METHOD_A( double ) + DECL_TRACE_METHOD_A( sc_dt::sc_int_base ) + DECL_TRACE_METHOD_A( sc_dt::sc_uint_base ) + DECL_TRACE_METHOD_A( sc_dt::sc_signed ) + DECL_TRACE_METHOD_A( sc_dt::sc_unsigned ) + + DECL_TRACE_METHOD_A( sc_dt::sc_fxval ) + DECL_TRACE_METHOD_A( sc_dt::sc_fxval_fast ) + DECL_TRACE_METHOD_A( sc_dt::sc_fxnum ) + DECL_TRACE_METHOD_A( sc_dt::sc_fxnum_fast ) + + DECL_TRACE_METHOD_A( sc_dt::sc_bv_base ) + DECL_TRACE_METHOD_A( sc_dt::sc_lv_base ) + // LCOV_EXCL_STOP + // clang-format on + +#undef DECL_TRACE_METHOD_A +#undef DECL_TRACE_METHOD_B +}; + +#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK +#undef _VL_HAVE_SYSTEMC_PHASE_CALLBACK +#endif +#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK +#undef _VL_HAVE_SYSTEMC_STAGE_CALLBACK +#endif + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sym_props.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sym_props.h new file mode 100644 index 00000000000..1cdd3a9905e --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sym_props.h @@ -0,0 +1,278 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated symbol inspection header +/// +/// This file is for inclusion by internal files that need to inspect +/// specific symbols. Applications typically use the VPI instead. +/// +/// User wrapper code wanting to inspect the symbol table should use +/// verilated_syms.h instead. +/// +//************************************************************************* +// These classes are thread safe, and read only. + +#ifndef VERILATOR_VERILATED_SYM_PROPS_H_ +#define VERILATOR_VERILATED_SYM_PROPS_H_ + +#include "verilatedos.h" + +#include + +//=========================================================================== +// Verilator range +// Thread safety: Assume is constructed only with model, then any number of readers + +// See also V3Ast::VNumRange +class VerilatedRange final { + int m_left = 0; + int m_right = 0; + +protected: + friend class VerilatedVarProps; + friend class VerilatedScope; + VerilatedRange() = default; + void init(int left, int right) { + m_left = left; + m_right = right; + } + +public: + VerilatedRange(int left, int right) + : m_left{left} + , m_right{right} {} + ~VerilatedRange() = default; + int left() const VL_PURE { return m_left; } + int right() const VL_PURE { return m_right; } + int low() const VL_PURE { return (m_left < m_right) ? m_left : m_right; } + int high() const VL_PURE { return (m_left > m_right) ? m_left : m_right; } + int elements() const VL_PURE { + return (VL_LIKELY(m_left >= m_right) ? (m_left - m_right + 1) : (m_right - m_left + 1)); + } + int increment() const VL_PURE { return (m_left >= m_right) ? 1 : -1; } +}; + +//=========================================================================== +// Verilator variable +// Thread safety: Assume is constructed only with model, then any number of readers + +class VerilatedVarProps VL_NOT_FINAL { + // TYPES + static constexpr uint32_t MAGIC = 0xddc4f829UL; + // MEMBERS + const uint32_t m_magic; // Magic number + const VerilatedVarType m_vltype; // Data type + const VerilatedVarFlags m_vlflags; // Direction + std::vector m_unpacked; // Unpacked array ranges + std::vector m_packed; // Packed array ranges + VerilatedRange m_packedDpi; // Flattened packed array range + void initUnpacked(int udims, const int* ulims) { + for (int i = 0; i < udims; ++i) { + const int uleft = ulims ? ulims[2 * i + 0] : 0; + const int uright = ulims ? ulims[2 * i + 1] : 0; + m_unpacked.emplace_back(uleft, uright); + } + } + void initPacked(int pdims, const int* plims) { + int packedSize = 1; + for (int i = 0; i < pdims; ++i) { + const int pleft = plims ? plims[2 * i + 0] : 0; + const int pright = plims ? plims[2 * i + 1] : 0; + m_packed.emplace_back(pleft, pright); + packedSize *= abs(pleft - pright) + 1; + } + if (pdims == 1) { + // Preserve packed array range if the packed component is 1-D + m_packedDpi = m_packed.front(); + } else { + m_packedDpi = VerilatedRange{packedSize - 1, 0}; + } + } + // CONSTRUCTORS +protected: + friend class VerilatedScope; + VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims, int pdims) + : m_magic{MAGIC} + , m_vltype{vltype} + , m_vlflags{vlflags} { + // Only preallocate the ranges + initUnpacked(udims, nullptr); + initPacked(pdims, nullptr); + } + +public: + class Unpacked {}; + // Without packed + VerilatedVarProps(VerilatedVarType vltype, int vlflags) + : m_magic{MAGIC} + , m_vltype{vltype} + , m_vlflags(VerilatedVarFlags(vlflags)) {} // Need () or GCC 4.8 false warning + + VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims) + : m_magic{MAGIC} + , m_vltype{vltype} + , m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning + initUnpacked(udims, ulims); + } + // With packed + class Packed {}; + VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pdims, const int* plims) + : m_magic{MAGIC} + , m_vltype{vltype} + , m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning + initPacked(pdims, plims); + } + VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims, + Packed, int pdims, const int* plims) + : m_magic{MAGIC} + , m_vltype{vltype} + , m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning + initUnpacked(udims, ulims); + initPacked(pdims, plims); + } + + ~VerilatedVarProps() = default; + // METHODS + bool magicOk() const { return m_magic == MAGIC; } + VerilatedVarType vltype() const VL_MT_SAFE { return m_vltype; } + VerilatedVarFlags vldir() const { + return static_cast(static_cast(m_vlflags) & VLVF_MASK_DIR); + } + uint32_t entSize() const VL_MT_SAFE; + uint32_t entBits() const VL_MT_SAFE { + uint32_t bits = 1; + for (auto it : m_packed) bits *= it.elements(); + return bits; + } + bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); } + bool isForceable() const { return ((m_vlflags & VLVF_FORCEABLE) != 0); } + bool isContinuously() const { return ((m_vlflags & VLVF_CONTINUOUSLY) != 0); } + // DPI compatible C standard layout + bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); } + bool isSigned() const { return ((m_vlflags & VLVF_SIGNED) != 0); } + bool isBitVar() const { return ((m_vlflags & VLVF_BITVAR) != 0); } + int udims() const VL_MT_SAFE { return m_unpacked.size(); } + int pdims() const VL_MT_SAFE { return m_packed.size(); } + int dims() const VL_MT_SAFE { return pdims() + udims(); } + const std::vector& packedRanges() const VL_MT_SAFE { return m_packed; } + const std::vector& unpackedRanges() const VL_MT_SAFE { return m_unpacked; } + const VerilatedRange* range(int dim) const VL_MT_SAFE { + if (dim < udims()) + return &m_unpacked[dim]; + else if (dim < dims()) + return &m_packed[dim - udims()]; + else + return nullptr; + } + // DPI accessors (with packed dimensions flattened!) + int left(int dim) const VL_MT_SAFE { + return dim == 0 ? m_packedDpi.left() + : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left() + : 0; + } + int right(int dim) const VL_MT_SAFE { + return dim == 0 ? m_packedDpi.right() + : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right() + : 0; + } + int low(int dim) const VL_MT_SAFE { + return dim == 0 ? m_packedDpi.low() + : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low() + : 0; + } + int high(int dim) const VL_MT_SAFE { + return dim == 0 ? m_packedDpi.high() + : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high() + : 0; + } + int increment(int dim) const { + return dim == 0 ? m_packedDpi.increment() + : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment() + : 0; + } + int elements(int dim) const VL_MT_SAFE { + return dim == 0 ? m_packedDpi.elements() + : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements() + : 0; + } + // Total size in bytes (note DPI limited to 4GB) + size_t totalSize() const; + // Adjust a data pointer to access a given array element, NULL if something goes bad + void* datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE; +}; + +//=========================================================================== +// Verilator DPI open array variable + +class VerilatedDpiOpenVar final { + // MEMBERS + const VerilatedVarProps* const m_propsp; // Variable properties + void* const m_datap; // Location of data (local to thread always, so safe) +public: + // CONSTRUCTORS + VerilatedDpiOpenVar(const VerilatedVarProps* propsp, void* datap) + : m_propsp{propsp} + , m_datap{datap} {} + VerilatedDpiOpenVar(const VerilatedVarProps* propsp, const void* datap) + : m_propsp{propsp} + , m_datap{const_cast(datap)} {} + ~VerilatedDpiOpenVar() = default; + // METHODS + void* datap() const VL_MT_SAFE { return m_datap; } + // METHODS - from VerilatedVarProps + bool magicOk() const { return m_propsp->magicOk(); } + VerilatedVarType vltype() const { return m_propsp->vltype(); } + bool isDpiStdLayout() const { return m_propsp->isDpiCLayout(); } + int entBits() const { return m_propsp->entBits(); } + int udims() const VL_MT_SAFE { return m_propsp->udims(); } + int left(int dim) const VL_MT_SAFE { return m_propsp->left(dim); } + int right(int dim) const VL_MT_SAFE { return m_propsp->right(dim); } + int low(int dim) const { return m_propsp->low(dim); } + int high(int dim) const { return m_propsp->high(dim); } + int increment(int dim) const { return m_propsp->increment(dim); } + int elements(int dim) const { return m_propsp->elements(dim); } + size_t totalSize() const { return m_propsp->totalSize(); } + void* datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE { + return m_propsp->datapAdjustIndex(datap, dim, indx); + } +}; + +//=========================================================================== +// Verilator variable +// Thread safety: Assume is constructed only with model, then any number of readers + +class VerilatedVar final : public VerilatedVarProps { + // MEMBERS + void* const m_datap; // Location of data + const char* const m_namep; // Name - slowpath +protected: + const bool m_isParam; + friend class VerilatedScope; + // CONSTRUCTORS + VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype, + VerilatedVarFlags vlflags, int udims, int pdims, bool isParam) + : VerilatedVarProps{vltype, vlflags, udims, pdims} + , m_datap{datap} + , m_namep{namep} + , m_isParam{isParam} {} + +public: + ~VerilatedVar() = default; + // ACCESSORS + void* datap() const { return m_datap; } + const char* name() const { return m_namep; } + bool isParam() const { return m_isParam; } +}; + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_syms.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_syms.h new file mode 100644 index 00000000000..949dbde9eed --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_syms.h @@ -0,0 +1,75 @@ +// -*- mode: C++; c-file-style: "cc-mode" +//-*- ************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated symbol inspection header +/// +/// This file is for inclusion by user wrapper code that needs to inspect +/// the symbol table. It is not included in verilated.h (instead see +/// verilated_sym_props.h) as it requires some heavyweight C++ classes. +/// +/// These classes are rarely used by user code; typical user code will +/// instead use the VPI to access this information. +/// +/// These classes are thread safe and read only. It is constructed only +/// when a model is built (from the main thread). +/// +//************************************************************************* + +#ifndef VERILATOR_VERILATED_SYMS_H_ +#define VERILATOR_VERILATED_SYMS_H_ + +#include "verilatedos.h" + +#include "verilated.h" +#include "verilated_sym_props.h" + +#include +#include +#include + +//====================================================================== +// Types + +// Class to sort maps keyed by const char*'s +struct VerilatedCStrCmp final { + bool operator()(const char* a, const char* b) const { return std::strcmp(a, b) < 0; } +}; + +// Map of sorted scope names to find associated scope class +// This is a class instead of typedef/using to allow forward declaration in verilated.h +class VerilatedScopeNameMap final + : public std::map { +public: + VerilatedScopeNameMap() = default; + ~VerilatedScopeNameMap() = default; +}; + +// Map of sorted variable names to find associated variable class +// This is a class instead of typedef/using to allow forward declaration in verilated.h +class VerilatedVarNameMap final : public std::map { +public: + VerilatedVarNameMap() = default; + ~VerilatedVarNameMap() = default; +}; + +// Map of parent scope to vector of children scopes +// This is a class instead of typedef/using to allow forward declaration in verilated.h +class VerilatedHierarchyMap final + : public std::unordered_map> { +public: + VerilatedHierarchyMap() = default; + ~VerilatedHierarchyMap() = default; +}; + +#endif // Guard diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.cpp new file mode 100644 index 00000000000..d814b585c7e --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.cpp @@ -0,0 +1,281 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2012-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated thread pool implementation code +/// +/// This file must be compiled and linked against all Verilated objects +/// that use --threads. +/// +/// Use "verilator --threads" to add this to the Makefile for the linker. +/// +//============================================================================= + +#include "verilatedos.h" + +#include "verilated_threads.h" + +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#include +#endif + +//============================================================================= +// Globals + +// Internal note: Globals may multi-construct, see verilated.cpp top. + +std::atomic VlMTaskVertex::s_yields; + +//============================================================================= +// VlMTaskVertex + +VlMTaskVertex::VlMTaskVertex(uint32_t upstreamDepCount) + : m_upstreamDepsDone{0} + , m_upstreamDepCount{upstreamDepCount} { + assert(atomic_is_lock_free(&m_upstreamDepsDone)); +} + +//============================================================================= +// VlWorkerThread + +VlWorkerThread::VlWorkerThread(VerilatedContext* contextp) + : m_ready_size{0} + , m_contextp{contextp} { +#ifdef VL_USE_PTHREADS + // Init attributes + pthread_attr_t attr; + pthread_attr_init(&attr); + // Attempt to use the same stack size as the current (main) thread if possible + const size_t stacksize = pthread_get_stacksize_np(pthread_self()); + if (!stacksize || pthread_attr_setstacksize(&attr, stacksize)) { + // Fall back on default atributes if failed to get/set stack size + pthread_attr_destroy(&attr); + pthread_attr_init(&attr); + } + // Create thread + if (pthread_create(&m_pthread, &attr, &VlWorkerThread::start, this)) { + std::cerr << "pthread_create failed" << std::endl; + std::abort(); + } + // Destroy attributes + pthread_attr_destroy(&attr); +#else + m_cthread = std::thread(start, this); +#endif +} + +VlWorkerThread::~VlWorkerThread() { + shutdown(); + // The thread should exit; join it. +#ifdef VL_USE_PTHREADS + pthread_join(m_pthread, nullptr); +#else + m_cthread.join(); +#endif +} + +static void shutdownTask(void*, bool) { // LCOV_EXCL_LINE + // Deliberately empty, we use the address of this function as a magic number +} + +void VlWorkerThread::shutdown() { addTask(shutdownTask, nullptr); } + +void VlWorkerThread::wait() { + // Enqueue a task that sets this flag. Execution is in-order so this ensures completion. + std::atomic flag{false}; + addTask([](void* flagp, bool) { static_cast*>(flagp)->store(true); }, &flag); + // Spin wait + for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) { + if (flag.load()) return; + VL_CPU_RELAX(); + } + // Yield wait + while (!flag.load()) std::this_thread::yield(); +} + +void VlWorkerThread::main() { + // Initialize thread_locals + Verilated::threadContextp(m_contextp); + // One work item + ExecRec work; + // Wait for the first task without spinning, in case the thread is never actually used. + dequeWork(&work); + // Loop until shutdown task is received + while (VL_UNLIKELY(work.m_fnp != shutdownTask)) { + work.m_fnp(work.m_selfp, work.m_evenCycle); + // Wait for next task with spinning. + dequeWork(&work); + } +} + +void* VlWorkerThread::start(void* argp) { + reinterpret_cast(argp)->main(); + return nullptr; +} + +//============================================================================= +// VlThreadPool + +VlThreadPool::VlThreadPool(VerilatedContext* contextp, unsigned nThreads) { + for (unsigned i = 0; i < nThreads; ++i) { + m_workers.push_back(new VlWorkerThread{contextp}); + m_unassignedWorkers.push(i); + } + m_numaStatus = numaAssign(contextp); +} + +VlThreadPool::~VlThreadPool() { + // Each ~WorkerThread will wait for its thread to exit. + for (auto& i : m_workers) delete i; +} + +std::string VlThreadPool::numaAssign(VerilatedContext* contextp) { +#if defined(__linux) || defined(CPU_ZERO) || defined(VL_CPPCHECK) // Linux-like pthreads + if (contextp && !contextp->useNumaAssign()) { return "NUMA assignment not requested"; } + std::string numa_strategy = VlOs::getenvStr("VERILATOR_NUMA_STRATEGY", "default"); + if (numa_strategy == "none") { + return "no NUMA assignment requested"; + } else if (numa_strategy != "default" && numa_strategy != "") { + return "%Warning: unknown VERILATOR_NUMA_STRATEGY value '" + numa_strategy + "'"; + } + // Get number of processor available to the current process + const unsigned num_proc = VlOs::getProcessAvailableParallelism(); + if (!num_proc) return "Can't determine number of available threads"; + // If fewer than hardware threads in the host, user presumably set affinity + if (num_proc < std::thread::hardware_concurrency()) return "processor affinity already set"; + + // Make a reasonable processor affinity selection + const int num_threads = static_cast(m_workers.size()); + if (num_threads < 2) return "too few threads"; + if (static_cast(num_threads) > num_proc) return "too many threads"; + + // Read CPU info. + // Uncertain if any modern system has gaps in the processor id (Solaris + // did), but just in case use vectors instead of processor number math. + // + // Currently ignoring socket number "physical id". + // If processor numbers are sequential on sockets, algorithm works out ok. + // If processor numbers are strided on sockets, algorithm also works out ok. + std::ifstream is{"/proc/cpuinfo"}; + if (VL_UNLIKELY(!is)) return "%Warning: no /proc/cpuinfo"; + + std::vector unassigned_processors; // Processors to assign in sorted order + std::map processor_core; + std::multimap core_processors; + std::set cores; + { + int processor = -1; + while (!is.eof()) { + std::string line; + std::getline(is, line); + std::string::size_type pos = line.find(":"); + int number = -1; + if (pos != std::string::npos) number = atoi(line.c_str() + pos + 1); + if (line.compare(0, std::strlen("processor"), "processor") == 0) { + processor = number; + } else if (line.compare(0, std::strlen("core id"), "core id") == 0) { + const int core = number; + // std::cout << "p" << processor << " socket " << socket << " c" << core << + // std::endl; + cores.emplace(core); + processor_core[processor] = core; + core_processors.emplace(core, processor); + unassigned_processors.push_back(processor); + } + } + } + + // Start scheduling on the current CPU + 1. + // This will help to land on the same socket as current CPU, and also + // help make sure that different processes have different masks (when + // num_threads is not a common-factor of the processor count). + std::sort(unassigned_processors.begin(), unassigned_processors.end()); + { + const int on_cpu = sched_getcpu(); // TODO: this is a system call. Not exactly cheap. + bool hit = false; + std::vector new_front; + std::vector new_back; + for (const int processor : unassigned_processors) { + if (hit) { + new_front.push_back(processor); + } else { + new_back.push_back(processor); + } + if (processor == on_cpu) hit = true; + } + unassigned_processors = new_front; + unassigned_processors.insert(unassigned_processors.end(), new_back.begin(), + new_back.end()); + } + + // If less threads than cores, we can schedule per-core + const bool core_per_thread = num_threads <= cores.size(); + + // Compute core mapping + std::multimap thread_processors; + { + std::set assigned_processors; + int thread = 0; + for (const int processor : unassigned_processors) { + // Find free processor, the current thread can use that + if (assigned_processors.find(processor) != assigned_processors.end()) continue; + assigned_processors.emplace(processor); + thread_processors.emplace(thread, processor); + if (core_per_thread) { + // Also include all other processors same core, + // so that another thread doesn't land on different processor in same core + const int core = processor_core[processor]; + const auto bounds = core_processors.equal_range(core); + for (auto it{bounds.first}; it != bounds.second; ++it) { + if (assigned_processors.find(it->second) != assigned_processors.end()) + continue; + if (it->second == processor) continue; + thread_processors.emplace(thread, it->second); + assigned_processors.emplace(it->second); + } + } + // Prepare for next loop + thread = (thread + 1) % num_threads; + } + } + + // Set affinity + std::string status = "assigned "; + for (int thread = 0; thread < num_threads; ++thread) { + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + + const auto bounds = thread_processors.equal_range(thread); + for (auto it{bounds.first}; it != bounds.second; ++it) { + if (it != bounds.first) status += ','; + status += std::to_string(it->second); + CPU_SET(it->second, &cpuset); + } + status += ";"; + + const int rc = pthread_setaffinity_np(m_workers[thread]->m_cthread.native_handle(), + sizeof(cpu_set_t), &cpuset); + if (rc != 0) return "%Warning: pthread_setaffinity_np failed"; + } + // std::cout << "Status: " << status << std::endl; + return status; +#else + return "non-supported host OS"; +#endif +} diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.h new file mode 100644 index 00000000000..9ac23392bf0 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.h @@ -0,0 +1,260 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//============================================================================= +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2012-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//============================================================================= +/// +/// \file +/// \brief Verilated thread pool and profiling header +/// +/// This file is not part of the Verilated public-facing API. +/// It is only for internal use by Verilated library multithreaded +/// routines. +/// +//============================================================================= + +#ifndef VERILATOR_VERILATED_THREADS_H_ +#define VERILATOR_VERILATED_THREADS_H_ + +#include "verilatedos.h" + +#include "verilated.h" // for VerilatedMutex and clang annotations + +#include +#include +#include +#include +#include +#include + +// Use pthreads directly on macOS (could do this on Linux too if needing APIs unavailable via C++) +#if defined(_POSIX_THREADS) && defined(__APPLE__) +#define VL_USE_PTHREADS +#endif + +#ifdef VL_USE_PTHREADS +#include +#endif + +class VlExecutionProfiler; +class VlThreadPool; + +// VlMTaskVertex and VlThreadpool will work with multiple model class types. +// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it +// as a void* here. +using VlSelfP = void*; + +using VlExecFnp = void (*)(VlSelfP, bool); + +// Track dependencies for a single MTask. +class VlMTaskVertex final { + // MEMBERS + static std::atomic s_yields; // Statistics + + // On even cycles, _upstreamDepsDone increases as upstream + // dependencies complete. When it reaches _upstreamDepCount, + // this MTaskVertex is ready. + // + // On odd cycles, _upstreamDepsDone decreases as upstream + // dependencies complete, and when it reaches zero this MTaskVertex + // is ready. + // + // An atomic is smaller than a mutex, and lock-free. + // + // (Why does the size of this class matter? If an mtask has many + // downstream mtasks to notify, we hope these will pack into a + // small number of cache lines to reduce the cost of pointer chasing + // during done-notification. Nobody's quantified that cost though. + // If we were really serious about shrinking this class, we could + // use 16-bit types here...) + std::atomic m_upstreamDepsDone; + const uint32_t m_upstreamDepCount; + +public: + // CONSTRUCTORS + + // 'upstreamDepCount' is the number of upstream MTaskVertex's + // that must notify this MTaskVertex before it will become ready + // to run. + explicit VlMTaskVertex(uint32_t upstreamDepCount); + ~VlMTaskVertex() = default; + + static uint64_t yields() { return s_yields; } + static void yieldThread() { + ++s_yields; // Statistics + std::this_thread::yield(); + } + + // Upstream mtasks must call this when they complete. + // Returns true when the current MTaskVertex becomes ready to execute, + // false while it's still waiting on more dependencies. + bool signalUpstreamDone(bool evenCycle) { + if (evenCycle) { + const uint32_t upstreamDepsDone + = 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release); + assert(upstreamDepsDone <= m_upstreamDepCount); + return (upstreamDepsDone == m_upstreamDepCount); + } else { + const uint32_t upstreamDepsDone_prev + = m_upstreamDepsDone.fetch_sub(1, std::memory_order_release); + assert(upstreamDepsDone_prev > 0); + return (upstreamDepsDone_prev == 1); + } + } + bool areUpstreamDepsDone(bool evenCycle) const { + const uint32_t target = evenCycle ? m_upstreamDepCount : 0; + return m_upstreamDepsDone.load(std::memory_order_acquire) == target; + } + void waitUntilUpstreamDone(bool evenCycle) const { + unsigned ct = 0; + while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) { + VL_CPU_RELAX(); + ++ct; + if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) { + ct = 0; + yieldThread(); + } + } + } +}; + +class VlWorkerThread final { + friend class VlThreadPool; + + // TYPES + struct ExecRec final { + VlExecFnp m_fnp = nullptr; // Function to execute + VlSelfP m_selfp = nullptr; // Symbol table to execute + bool m_evenCycle = false; // Even/odd for flag alternation + ExecRec() = default; + ExecRec(VlExecFnp fnp, VlSelfP selfp, bool evenCycle) + : m_fnp{fnp} + , m_selfp{selfp} + , m_evenCycle{evenCycle} {} + }; + + // MEMBERS + mutable VerilatedMutex m_mutex; + std::condition_variable_any m_cv; + // Only notify the condition_variable if the worker is waiting + bool m_waiting VL_GUARDED_BY(m_mutex) = false; + + // Why a vector? We expect the pending list to be very short, typically + // 0 or 1 or 2, so popping from the front shouldn't be + // expensive. Revisit if we ever have longer queues... + std::vector m_ready VL_GUARDED_BY(m_mutex); + // Store the size atomically, so we can spin wait + std::atomic m_ready_size; + // Thread context + VerilatedContext* const m_contextp; + // Underlying thread record +#ifdef VL_USE_PTHREADS + pthread_t m_pthread{}; +#else + std::thread m_cthread{}; +#endif + + // METHDOS + static void* start(void*); // Static entry point, invokes 'main' + void main(); // 'main' loop of thread + + VL_UNCOPYABLE(VlWorkerThread); + +public: + // CONSTRUCTORS + explicit VlWorkerThread(VerilatedContext* contextp); + ~VlWorkerThread(); + + // METHODS + template + void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) { + // Spin for a while, waiting for new data + if VL_CONSTEXPR_CXX17 (N_SpinWait) { + for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) { + if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) break; + VL_CPU_RELAX(); + } + } + VerilatedLockGuard lock{m_mutex}; + while (m_ready.empty()) { + m_waiting = true; + m_cv.wait(m_mutex); + } + m_waiting = false; + // As noted above this is inefficient if our ready list is ever + // long (but it shouldn't be) + *workp = m_ready.front(); + m_ready.erase(m_ready.begin()); + m_ready_size.fetch_sub(1, std::memory_order_relaxed); + } + void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle = false) + VL_MT_SAFE_EXCLUDES(m_mutex) { + bool notify; + { + const VerilatedLockGuard lock{m_mutex}; + m_ready.emplace_back(fnp, selfp, evenCycle); + m_ready_size.fetch_add(1, std::memory_order_relaxed); + notify = m_waiting; + } + if (notify) m_cv.notify_one(); + } + + void shutdown(); // Finish current tasks, then terminate thread + void wait(); // Blocks calling thread until all tasks complete in this thread +}; + +class VlThreadPool final : public VerilatedVirtualBase { + // MEMBERS + std::vector m_workers; // our workers + + mutable VerilatedMutex m_mutex; // Guards indexes of unassigned workers + // Indexes of unassigned workers + std::stack m_unassignedWorkers VL_GUARDED_BY(m_mutex); + // For sequentially generating task IDs to avoid shadowing + std::atomic m_assignedTasks{0}; + std::string m_numaStatus; // Status of NUMA assignment + +public: + // CONSTRUCTORS + // Construct a thread pool with 'nThreads' dedicated threads. The thread + // pool will create these threads and make them available to execute tasks + // via this->workerp(index)->addTask(...) + VlThreadPool(VerilatedContext* contextp, unsigned nThreads); + ~VlThreadPool() override; + + // METHODS + size_t assignWorkerIndex() { + const VerilatedLockGuard lock{m_mutex}; + assert(!m_unassignedWorkers.empty()); + const size_t index = m_unassignedWorkers.top(); + m_unassignedWorkers.pop(); + return index; + } + void freeWorkerIndexes(std::vector& indexes) { + const VerilatedLockGuard lock{m_mutex}; + for (size_t index : indexes) m_unassignedWorkers.push(index); + indexes.clear(); + } + unsigned assignTaskIndex() { return m_assignedTasks++; } + int numThreads() const { return static_cast(m_workers.size()); } + std::string numaStatus() const { return m_numaStatus; } + VlWorkerThread* workerp(int index) { + assert(index >= 0); + assert(index < static_cast(m_workers.size())); + return m_workers[index]; + } + +private: + VL_UNCOPYABLE(VlThreadPool); + + std::string numaAssign(VerilatedContext* contextp); +}; + +#endif diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.cpp new file mode 100644 index 00000000000..51dd88b9a0a --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.cpp @@ -0,0 +1,271 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//========================================================================= +/// +/// \file +/// \brief Verilated timing implementation code +/// +/// This file must be compiled and linked against all Verilated objects +/// that use timing features. +/// +/// See the internals documentation docs/internals.rst for details. +/// +//========================================================================= + +#include "verilated_timing.h" + +//====================================================================== +// VlCoroutineHandle:: Methods + +void VlCoroutineHandle::resume() { + // Only null if we have a fork..join_any and one of the other child processes resumed the + // main process + if (VL_LIKELY(m_coro)) { + VL_DEBUG_IF(VL_DBG_MSGF(" Resuming: "); dump();); + if (m_process) { // If process state is managed with std::process + if (m_process->state() == VlProcess::KILLED) { + m_coro.destroy(); + } else { + m_process->state(VlProcess::RUNNING); + m_coro(); + } + } else { + m_coro(); + } + m_coro = nullptr; + } +} + +#ifdef VL_DEBUG +void VlCoroutineHandle::dump() const { + VL_PRINTF("Process waiting at %s:%d\n", m_fileline.filename(), m_fileline.lineno()); +} +#endif + +//====================================================================== +// VlDelayScheduler:: Methods + +void VlDelayScheduler::resume() { +#ifdef VL_DEBUG + VL_DEBUG_IF(dump(); VL_DBG_MSGF(" Resuming delayed processes\n");); +#endif + bool resumed = false; + + while (!m_queue.empty() && (m_queue.cbegin()->first == m_context.time())) { + VlCoroutineHandle handle = std::move(m_queue.begin()->second); + m_queue.erase(m_queue.begin()); + handle.resume(); + resumed = true; + } + + if (!resumed) { + if (m_context.time() == 0) { + // Nothing was scheduled at time 0, but resume() got called due to --x-initial-edge + return; + } + + VL_FATAL_MT(__FILE__, __LINE__, "", + "%Error: Encountered process that should've been resumed at an " + "earlier simulation time. Missed a time slot?\n"); + } +} + +void VlDelayScheduler::resumeZeroDelay() { + m_zeroDelayesSwap.swap(m_zeroDelayed); + for (VlCoroutineHandle& handle : m_zeroDelayesSwap) handle.resume(); + m_zeroDelayesSwap.clear(); +} + +uint64_t VlDelayScheduler::nextTimeSlot() const { + if (!m_queue.empty()) return m_queue.cbegin()->first; + if (m_zeroDelayed.empty()) + VL_FATAL_MT(__FILE__, __LINE__, "", "There is no next time slot scheduled"); + return m_context.time(); +} + +#ifdef VL_DEBUG +void VlDelayScheduler::dump() const { + if (m_queue.empty() && m_zeroDelayed.empty()) { + VL_DBG_MSGF(" No delayed processes:\n"); + } else { + VL_DBG_MSGF(" Delayed processes:\n"); + for (const auto& susp : m_zeroDelayed) { + VL_DBG_MSGF(" Awaiting #0-delayed resumption, " + "time () %" PRIu64 ": ", + m_context.time()); + susp.dump(); + } + for (const auto& susp : m_queue) { + VL_DBG_MSGF(" Awaiting time %" PRIu64 ": ", susp.first); + susp.second.dump(); + } + } +} +#endif + +//====================================================================== +// VlTriggerScheduler:: Methods + +void VlTriggerScheduler::resume(const char* eventDescription) { +#ifdef VL_DEBUG + VL_DEBUG_IF(dump(eventDescription); + VL_DBG_MSGF(" Resuming processes waiting for %s\n", eventDescription);); +#endif + for (VlCoroutineHandle& coro : m_toResume) coro.resume(); + m_toResume.clear(); +} + +void VlTriggerScheduler::moveToResumeQueue(const char* eventDescription) { +#ifdef VL_DEBUG + if (!m_fired.empty()) { + VL_DEBUG_IF(VL_DBG_MSGF(" Moving to resume queue processes waiting for %s:\n", + eventDescription); + for (const auto& susp + : m_fired) { + VL_DBG_MSGF(" - "); + susp.dump(); + }); + } +#endif + std::swap(m_fired, m_toResume); +} + +void VlTriggerScheduler::ready(const char* eventDescription) { +#ifdef VL_DEBUG + if (!m_awaiting.empty()) { + VL_DEBUG_IF( + VL_DBG_MSGF(" Committing processes waiting for %s:\n", eventDescription); + for (const auto& susp + : m_awaiting) { + VL_DBG_MSGF(" - "); + susp.dump(); + }); + } +#endif + const size_t expectedSize = m_fired.size() + m_awaiting.size(); + if (m_fired.capacity() < expectedSize) m_fired.reserve(expectedSize * 2); + m_fired.insert(m_fired.end(), std::make_move_iterator(m_awaiting.begin()), + std::make_move_iterator(m_awaiting.end())); + m_awaiting.clear(); +} + +#ifdef VL_DEBUG +void VlTriggerScheduler::dump(const char* eventDescription) const { + if (m_toResume.empty()) { + VL_DBG_MSGF(" No process to resume waiting for %s\n", eventDescription); + } else { + for (const auto& susp : m_toResume) { + VL_DBG_MSGF(" Processes to resume waiting for %s:\n", eventDescription); + VL_DBG_MSGF(" - "); + susp.dump(); + } + } + if (!m_fired.empty()) { + VL_DBG_MSGF(" Triggered processes waiting for %s:\n", eventDescription); + for (const auto& susp : m_awaiting) { + VL_DBG_MSGF(" - "); + susp.dump(); + } + } + if (!m_awaiting.empty()) { + VL_DBG_MSGF(" Not triggered processes waiting for %s:\n", eventDescription); + for (const auto& susp : m_awaiting) { + VL_DBG_MSGF(" - "); + susp.dump(); + } + } +} +#endif + +//====================================================================== +// VlDynamicTriggerScheduler:: Methods + +bool VlDynamicTriggerScheduler::evaluate() { + m_anyTriggered = false; + VL_DEBUG_IF(dump();); + std::swap(m_suspended, m_evaluated); + for (auto& coro : m_evaluated) coro.resume(); + m_evaluated.clear(); + return m_anyTriggered; +} + +void VlDynamicTriggerScheduler::doPostUpdates() { + VL_DEBUG_IF(if (!m_post.empty()) + VL_DBG_MSGF(" Doing post updates for processes:\n"); // + for (const auto& susp + : m_post) { + VL_DBG_MSGF(" - "); + susp.dump(); + }); + for (auto& coro : m_post) coro.resume(); + m_post.clear(); +} + +void VlDynamicTriggerScheduler::resume() { + VL_DEBUG_IF(if (!m_triggered.empty()) VL_DBG_MSGF(" Resuming processes:\n"); // + for (const auto& susp + : m_triggered) { + VL_DBG_MSGF(" - "); + susp.dump(); + }); + for (auto& coro : m_triggered) coro.resume(); + m_triggered.clear(); +} + +#ifdef VL_DEBUG +void VlDynamicTriggerScheduler::dump() const { + if (m_suspended.empty()) { + VL_DBG_MSGF(" No suspended processes waiting for dynamic trigger evaluation\n"); + } else { + for (const auto& susp : m_suspended) { + VL_DBG_MSGF(" Suspended processes waiting for dynamic trigger evaluation:\n"); + VL_DBG_MSGF(" - "); + susp.dump(); + } + } +} +#endif + +//====================================================================== +// VlForkSync:: Methods + +void VlForkSync::done(const char* filename, int lineno) { + VL_DEBUG_IF(VL_DBG_MSGF(" Process forked at %s:%d finished\n", filename, lineno);); + if (m_join->m_counter > 0) m_join->m_counter--; + if (m_join->m_counter == 0) m_join->m_susp.resume(); +} + +//====================================================================== +// VlCoroutine:: Methods + +VlCoroutine::VlPromise::~VlPromise() { + // Indicate to the return object that the coroutine has finished or been destroyed + if (m_corop) m_corop->m_promisep = nullptr; + // If there is a continuation, destroy it + if (m_continuation) m_continuation.destroy(); +} + +std::suspend_never VlCoroutine::VlPromise::final_suspend() noexcept { + // Indicate to the return object that the coroutine has finished + if (m_corop) { + m_corop->m_promisep = nullptr; + // Forget the return value, we won't need it and it won't be able to let us know if + // it's destroyed + m_corop = nullptr; + } + // If there is a continuation, resume it + if (m_continuation) { + m_continuation(); + m_continuation = nullptr; + } + return {}; +} diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.h new file mode 100644 index 00000000000..a657ac45886 --- /dev/null +++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.h @@ -0,0 +1,482 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Code available from: https://verilator.org +// +// This program is free software; you can redistribute it and/or modify it +// under the terms of either the GNU Lesser General Public License Version 3 +// or the Perl Artistic License Version 2.0. +// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder +// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 +// +//************************************************************************* +/// +/// \file +/// \brief Verilated timing header +/// +/// This file is included automatically by Verilator in some of the C++ files +/// it generates if timing features are used. +/// +/// This file is not part of the Verilated public-facing API. +/// It is only for internal use. +/// +/// See the internals documentation docs/internals.rst for details. +/// +//************************************************************************* +#ifndef VERILATOR_VERILATED_TIMING_H_ +#define VERILATOR_VERILATED_TIMING_H_ + +#include "verilated.h" + +#include +#include + +// clang-format off +// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++ +#if defined _LIBCPP_VERSION // libc++ +# if defined(__has_include) && !__has_include() && __has_include() +# if __clang_major__ > 13 // Clang > 13 warns that coroutine types in std::experimental are deprecated +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine" +# endif +# include + namespace std { + using namespace experimental; // Bring std::experimental into the std namespace + } +# else +# include +# endif +#else +# if defined __clang__ && defined __GLIBCXX__ && !defined __cpp_impl_coroutine +# define __cpp_impl_coroutine 1 // Clang doesn't define this, but it's needed for libstdc++ +# endif +# include +# if __clang_major__ < 14 + namespace std { // Bring coroutine library into std::experimental, as Clang < 14 expects it to be there + namespace experimental { + using namespace std; + } + } +# endif +#endif +// clang-format on + +// Placeholder for compiling with --protect-ids +#define VL_UNKNOWN "" + +//============================================================================= +// VlFileLineDebug stores a SystemVerilog source code location. Used in VlCoroutineHandle for +// debugging purposes. + +class VlFileLineDebug final { + // MEMBERS +#ifdef VL_DEBUG + const char* m_filename = nullptr; + int m_lineno = 0; +#endif + +public: + // CONSTRUCTORS + // Construct + VlFileLineDebug() = default; + VlFileLineDebug(const char* filename, int lineno) +#ifdef VL_DEBUG + : m_filename{filename} + , m_lineno{lineno} +#endif + { + } + + // METHODS +#ifdef VL_DEBUG + const char* filename() const { return m_filename; } + int lineno() const { return m_lineno; } +#endif +}; + +//============================================================================= +// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is +// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got +// suspended, another VlCoroutineHandle was created to manage it. + +class VlCoroutineHandle final { + VL_UNCOPYABLE(VlCoroutineHandle); + + // MEMBERS + std::coroutine_handle<> m_coro; // The wrapped coroutine handle + VlProcessRef m_process; // Data of the suspended process, null if not needed + VlFileLineDebug m_fileline; + +public: + // CONSTRUCTORS + // Construct + // non-explicit: + // cppcheck-suppress noExplicitConstructor + VlCoroutineHandle(VlProcessRef process) + : m_coro{nullptr} + , m_process{process} { + if (m_process) m_process->state(VlProcess::WAITING); + } + VlCoroutineHandle(std::coroutine_handle<> coro, VlProcessRef process, VlFileLineDebug fileline) + : m_coro{coro} + , m_process{process} + , m_fileline{fileline} { + if (m_process) m_process->state(VlProcess::WAITING); + } + // Move the handle, leaving a nullptr + // non-explicit: + // cppcheck-suppress noExplicitConstructor + VlCoroutineHandle(VlCoroutineHandle&& moved) + : m_coro{std::exchange(moved.m_coro, nullptr)} + , m_process{std::exchange(moved.m_process, nullptr)} + , m_fileline{moved.m_fileline} {} + // Destroy if the handle isn't null + ~VlCoroutineHandle() { + // Usually these coroutines should get resumed; we only need to clean up if we destroy a + // model with some coroutines suspended + if (VL_UNLIKELY(m_coro)) { + m_coro.destroy(); + if (m_process && m_process->state() != VlProcess::KILLED) { + m_process->state(VlProcess::FINISHED); + } + } + } + // METHODS + // Move the handle, leaving a null handle + auto& operator=(VlCoroutineHandle&& moved) { + m_coro = std::exchange(moved.m_coro, nullptr); + m_process = std::exchange(moved.m_process, nullptr); + m_fileline = moved.m_fileline; + return *this; + } + // Resume the coroutine if the handle isn't null and the process isn't killed + void resume(); +#ifdef VL_DEBUG + void dump() const; +#endif +}; + +enum class VlDelayPhase : bool { ACTIVE, INACTIVE }; + +//============================================================================= +// VlDelayScheduler stores coroutines to be resumed at a certain simulation time. If the current +// time is equal to a coroutine's resume time, the coroutine gets resumed. + +class VlDelayScheduler final { + // TYPES + // Time-sorted queue of timestamps and handles + using VlDelayedCoroutineQueue = std::multimap; + + // MEMBERS + VerilatedContext& m_context; + VlDelayedCoroutineQueue m_queue; // Coroutines to be restored at a certain simulation time + std::vector m_zeroDelayed; // Coroutines waiting for #0 + // Coroutines that waited for #0 and are being resumed now. As member to avoid reallocations + std::vector m_zeroDelayesSwap; + +public: + // CONSTRUCTORS + explicit VlDelayScheduler(VerilatedContext& context) + : m_context{context} {} + // METHODS + // Resume coroutines waiting for the current simulation time + void resume(); + // Resume coroutines waiting for #0 + void resumeZeroDelay(); + // Returns the simulation time of the next time slot (aborts if there are no delayed + // coroutines) + uint64_t nextTimeSlot() const; + // Are there no delayed coroutines awaiting? + bool empty() const { return m_queue.empty() && m_zeroDelayed.empty(); } + // Are there coroutines to resume at the current simulation time? + bool awaitingCurrentTime() const { + return (!m_queue.empty() && (m_queue.cbegin()->first <= m_context.time())); + } + // Are there coroutines to resume in the inactive region after a #0 delay? + bool awaitingZeroDelay() const { return !m_zeroDelayed.empty(); } +#ifdef VL_DEBUG + void dump() const; +#endif + // Used by coroutines for co_awaiting a certain simulation time + auto delay(uint64_t delay, VlProcessRef process, const char* filename = VL_UNKNOWN, + int lineno = 0) { + struct Awaitable final { + VlProcessRef process; // Data of the suspended process, null if not needed + VlDelayedCoroutineQueue& queue; + std::vector& queueZeroDelay; + const uint64_t delay; + const VlDelayPhase phase; + const VlFileLineDebug fileline; + + bool await_ready() const { return false; } // Always suspend + void await_suspend(std::coroutine_handle<> coro) { + // Both active delays and fork..join_none #0 are resumed out of the time queue. + if (phase != VlDelayPhase::INACTIVE) { + queue.emplace(delay, VlCoroutineHandle{coro, process, fileline}); + } else { + queueZeroDelay.emplace_back(VlCoroutineHandle{coro, process, fileline}); + } + } + void await_resume() const {} + }; + + VlDelayPhase phase; + if (delay != 0) { + // UINT64_MAX is a sentinel for synthetic fork..join_none delays. + if (delay == std::numeric_limits::max()) delay = 0; + phase = VlDelayPhase::ACTIVE; + } else { + phase = VlDelayPhase::INACTIVE; + } + return Awaitable{process, m_queue, + m_zeroDelayed, m_context.time() + delay, + phase, VlFileLineDebug{filename, lineno}}; + } +}; + +//============================================================================= +// VlTriggerScheduler stores coroutines to be resumed by a trigger. It does not keep track of its +// trigger, relying on calling code to resume when appropriate. Coroutines are kept in three stages +// - 'awaiting', 'fired' and 'toResume'. Whenever a coroutine is suspended, it lands in the +// 'awaiting' stage. Only when ready() is called, these coroutines get moved to the 'fired' stage. +// When moveToResumeQueue() is begin called all coroutines from 'ready' are moved to 'toResume'. +// That's when they can be resumed. This is done to avoid resuming processes before they start +// waiting. + +class VlTriggerScheduler final { + // TYPES + using VlCoroutineVec = std::vector; + + // MEMBERS + VlCoroutineVec m_awaiting; // Coroutines suspended before ready() was called + // (not resumable) + VlCoroutineVec m_fired; // Coroutines that were triggered (all coros from m_awaiting are moved + // here in ready()) + VlCoroutineVec m_toResume; // Coroutines to resume in next resumePrep() + // - moved here in commit() + +public: + // METHODS + // Resumes all coroutines from the m_toResume + void resume(const char* eventDescription = VL_UNKNOWN); + // Moves all coroutines from m_fired to m_toResume + void moveToResumeQueue(const char* eventDescription = VL_UNKNOWN); + // Moves all coroutines from m_awaiting to m_fired + void ready(const char* eventDescription = VL_UNKNOWN); + // Are there no coroutines awaiting? + bool empty() const { return m_fired.empty() && m_awaiting.empty(); } +#ifdef VL_DEBUG + void dump(const char* eventDescription) const; +#endif + // Used by coroutines for co_awaiting a certain trigger + auto trigger(bool ready, VlProcessRef process, const char* eventDescription = VL_UNKNOWN, + const char* filename = VL_UNKNOWN, int lineno = 0) { + VL_DEBUG_IF(VL_DBG_MSGF(" Suspending process waiting for %s at %s:%d\n", + eventDescription, filename, lineno);); + struct Awaitable final { + VlCoroutineVec& suspended; // Coros waiting on trigger + VlProcessRef process; // Data of the suspended process, null if not needed + VlFileLineDebug fileline; + + bool await_ready() const { return false; } // Always suspend + void await_suspend(std::coroutine_handle<> coro) { + suspended.emplace_back(coro, process, fileline); + } + void await_resume() const {} + }; + return Awaitable{ready ? m_fired : m_awaiting, process, VlFileLineDebug{filename, lineno}}; + } +}; + +//============================================================================= +// VlDynamicTriggerScheduler is used for cases where triggers cannot be statically referenced and +// evaluated. Coroutines that make use of this scheduler must adhere to a certain procedure: +// __Vtrigger = 0; +// +// while (!__Vtrigger) { +// co_await __VdynSched.evaluation(); +//
;
+//         __Vtrigger = ;
+//         __VdynShed.anyTriggered(__Vtrigger);
+//         [optionally] co_await __VdynSched.postUpdate();
+//         ;
+//     }
+//    co_await __VdynSched.resumption();
+// The coroutines get resumed at trigger evaluation time, evaluate their local triggers, optionally
+// await the post update step, and if the trigger is set, await proper resumption in the 'act' eval
+// step.
+
+class VlDynamicTriggerScheduler final {
+    // TYPES
+    using VlCoroutineVec = std::vector;
+
+    // MEMBERS
+    bool m_anyTriggered = false;  // If true, at least one trigger was set
+    VlCoroutineVec m_suspended;  // Suspended coroutines awaiting trigger evaluation
+    VlCoroutineVec m_evaluated;  // Coroutines currently being evaluated (for evaluate())
+    VlCoroutineVec m_triggered;  // Coroutines whose triggers were set, and are awaiting resumption
+    VlCoroutineVec m_post;  // Coroutines awaiting the post update step (only relevant for triggers
+                            // with destructive post updates, e.g. named events)
+
+    // METHODS
+    auto awaitable(VlProcessRef process, VlCoroutineVec& queue, const char* filename, int lineno) {
+        struct Awaitable final {
+            VlProcessRef process;  // Data of the suspended process, null if not needed
+            VlCoroutineVec& suspended;  // Coros waiting on trigger
+            VlFileLineDebug fileline;
+
+            bool await_ready() const { return false; }  // Always suspend
+            void await_suspend(std::coroutine_handle<> coro) {
+                suspended.emplace_back(coro, process, fileline);
+            }
+            void await_resume() const {}
+        };
+        return Awaitable{process, queue, VlFileLineDebug{filename, lineno}};
+    }
+
+public:
+    // Evaluates all dynamic triggers (resumed coroutines that co_await evaluation())
+    bool evaluate();
+    // Called by coroutines that evaluate triggers to notify the scheduler if any triggers were set
+    void anyTriggered(bool triggered) { m_anyTriggered = m_anyTriggered || triggered; }
+    // Runs post updates for all dynamic triggers (resumes coroutines that co_await postUpdate())
+    void doPostUpdates();
+    // Resumes all coroutines whose triggers are set (those that co_await resumption())
+    void resume();
+#ifdef VL_DEBUG
+    void dump() const;
+#endif
+    // Used by coroutines for co_awaiting trigger evaluation
+    auto evaluation(VlProcessRef process, const char* eventDescription, const char* filename,
+                    int lineno) {
+        VL_DEBUG_IF(VL_DBG_MSGF("         Suspending process waiting for %s at %s:%d\n",
+                                eventDescription, filename, lineno););
+        return awaitable(process, m_suspended, filename, lineno);
+    }
+    // Used by coroutines for co_awaiting the trigger post update step
+    auto postUpdate(VlProcessRef process, const char* eventDescription, const char* filename,
+                    int lineno) {
+        VL_DEBUG_IF(
+            VL_DBG_MSGF("         Process waiting for %s at %s:%d awaiting the post update step\n",
+                        eventDescription, filename, lineno););
+        return awaitable(process, m_post, filename, lineno);
+    }
+    // Used by coroutines for co_awaiting the resumption step (in 'act' eval)
+    auto resumption(VlProcessRef process, const char* eventDescription, const char* filename,
+                    int lineno) {
+        VL_DEBUG_IF(VL_DBG_MSGF("         Process waiting for %s at %s:%d awaiting resumption\n",
+                                eventDescription, filename, lineno););
+        return awaitable(process, m_triggered, filename, lineno);
+    }
+};
+
+//=============================================================================
+// VlForever is a helper awaitable type for suspending coroutines forever. Used for constant
+// wait statements.
+
+struct VlForever final {
+    bool await_ready() const { return false; }  // Always suspend
+    void await_suspend(std::coroutine_handle<> coro) const { coro.destroy(); }
+    void await_resume() const {}
+};
+
+//=============================================================================
+// VlForkSync is used to manage fork..join and fork..join_any constructs.
+
+class VlForkSync final {
+    // VlJoin stores the handle of a suspended coroutine that did a fork..join or fork..join_any.
+    // If the counter reaches 0, the suspended coroutine shall be resumed.
+    struct VlJoin final {
+        size_t m_counter = 0;  // When reaches 0, resume suspended coroutine
+        VlCoroutineHandle m_susp;  // Coroutine to resume
+    };
+
+    // The join info is shared among all forked processes
+    std::shared_ptr m_join;
+
+public:
+    // Create the join object and set the counter to the specified number
+    void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); }
+    // Called whenever any of the forked processes finishes. If the join counter reaches 0, the
+    // main process gets resumed
+    void done(const char* filename = VL_UNKNOWN, int lineno = 0);
+    // Used by coroutines for co_awaiting a join
+    auto join(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) {
+        assert(m_join);
+        VL_DEBUG_IF(
+            VL_DBG_MSGF("             Awaiting join of fork at: %s:%d\n", filename, lineno););
+        struct Awaitable final {
+            VlProcessRef process;  // Data of the suspended process, null if not needed
+            const std::shared_ptr join;  // Join to await on
+            VlFileLineDebug fileline;
+
+            bool await_ready() { return join->m_counter == 0; }  // Suspend if join still exists
+            void await_suspend(std::coroutine_handle<> coro) {
+                join->m_susp = {coro, process, fileline};
+            }
+            void await_resume() const {}
+        };
+        return Awaitable{process, m_join, VlFileLineDebug{filename, lineno}};
+    }
+};
+
+//=============================================================================
+// VlCoroutine
+// Return value of a coroutine. Used for chaining coroutine suspension/resumption.
+
+class VlCoroutine final {
+private:
+    // TYPES
+    struct VlPromise final {
+        std::coroutine_handle<> m_continuation;  // Coroutine to resume after this one finishes
+        VlCoroutine* m_corop = nullptr;  // Pointer to the coroutine return object
+
+        ~VlPromise();
+
+        VlCoroutine get_return_object() { return {this}; }
+
+        // Never suspend at the start of the coroutine
+        std::suspend_never initial_suspend() const { return {}; }
+
+        // Never suspend at the end of the coroutine (thanks to this, the coroutine will clean up
+        // after itself)
+        std::suspend_never final_suspend() noexcept;
+
+        void unhandled_exception() const { std::abort(); }
+        void return_void() const {}
+    };
+
+    // MEMBERS
+    VlPromise* m_promisep;  // The promise created for this coroutine
+
+public:
+    // TYPES
+    using promise_type = VlPromise;  // promise_type has to be public
+
+    // CONSTRUCTORS
+    // Construct
+    // cppcheck-suppress noExplicitConstructor
+    VlCoroutine(VlPromise* promisep)
+        : m_promisep{promisep} {
+        m_promisep->m_corop = this;
+    }
+    // Move. Update the pointers each time the return object is moved
+    // cppcheck-suppress noExplicitConstructor
+    VlCoroutine(VlCoroutine&& other)
+        : m_promisep{std::exchange(other.m_promisep, nullptr)} {
+        if (m_promisep) m_promisep->m_corop = this;
+    }
+    ~VlCoroutine() {
+        // Indicate to the promise that the return object is gone
+        if (m_promisep) m_promisep->m_corop = nullptr;
+    }
+
+    // METHODS
+    // Suspend the awaiter if the coroutine is suspended (the promise exists)
+    bool await_ready() const noexcept { return !m_promisep; }
+    // Set the awaiting coroutine as the continuation of the current coroutine
+    void await_suspend(std::coroutine_handle<> coro) { m_promisep->m_continuation = coro; }
+    void await_resume() const noexcept {}
+};
+
+#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace.h
new file mode 100644
index 00000000000..15e3cd9645d
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace.h
@@ -0,0 +1,648 @@
+// -*- mode: C++; c-file-style: "cc-mode" -*-
+//=============================================================================
+//
+// Code available from: https://verilator.org
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of either the GNU Lesser General Public License Version 3
+// or the Perl Artistic License Version 2.0.
+// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
+// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+//
+//=============================================================================
+///
+/// \file
+/// \brief Verilated internal common-tracing header
+///
+/// This file is not part of the Verilated public-facing API.
+/// It is only for internal use by Verilated tracing routines.
+///
+//=============================================================================
+
+#ifndef VERILATOR_VERILATED_TRACE_H_
+#define VERILATOR_VERILATED_TRACE_H_
+
+// clang-format off
+
+#include "verilated.h"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+#include 
+
+// clang-format on
+
+class VlThreadPool;
+template 
+class VerilatedTraceBuffer;
+template 
+class VerilatedTraceOffloadBuffer;
+
+//=============================================================================
+// Common enumerations
+
+enum class VerilatedTracePrefixType : uint8_t {
+    // Note: Entries must match VTracePrefixType (by name, not necessarily by value)
+    ARRAY_PACKED,
+    ARRAY_UNPACKED,
+    ROOTIO_WRAPPER,  // $rootio suppressed due to name()!=""
+    SCOPE_MODULE,
+    SCOPE_INTERFACE,
+    STRUCT_PACKED,
+    STRUCT_UNPACKED,
+    UNION_PACKED
+};
+
+// Direction attribute for ports
+enum class VerilatedTraceSigDirection : uint8_t {
+    NONE,
+    INPUT,
+    OUTPUT,
+    INOUT,
+};
+
+// Kind of signal. Similar to nettype but with a few more alternatives
+enum class VerilatedTraceSigKind : uint8_t {
+    PARAMETER,
+    SUPPLY0,
+    SUPPLY1,
+    TRI,
+    TRI0,
+    TRI1,
+    TRIAND,
+    TRIOR,
+    TRIREG,
+    WIRE,
+    VAR,
+};
+
+// Base data type of signal
+enum class VerilatedTraceSigType : uint8_t {
+    DOUBLE,
+    INTEGER,
+    BIT,
+    LOGIC,
+    INT,
+    SHORTINT,
+    LONGINT,
+    BYTE,
+    EVENT,
+    TIME,
+};
+
+//=============================================================================
+// Offloaded tracing
+
+// A simple synchronized first in first out queue
+template 
+class VerilatedThreadQueue final {  // LCOV_EXCL_LINE  // lcov bug
+private:
+    mutable VerilatedMutex m_mutex;  // Protects m_queue
+    std::condition_variable_any m_cv;
+    std::deque m_queue VL_GUARDED_BY(m_mutex);
+
+public:
+    // Put an element at the back of the queue
+    void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
+        const VerilatedLockGuard lock{m_mutex};
+        m_queue.push_back(value);
+        m_cv.notify_one();
+    }
+
+    // Put an element at the front of the queue
+    void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
+        const VerilatedLockGuard lock{m_mutex};
+        m_queue.push_front(value);
+        m_cv.notify_one();
+    }
+
+    // Get an element from the front of the queue. Blocks if none available
+    T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
+        VerilatedLockGuard lock{m_mutex};
+        m_cv.wait(m_mutex, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
+        assert(!m_queue.empty());
+        T value = m_queue.front();
+        m_queue.pop_front();
+        return value;
+    }
+
+    // Non blocking get
+    bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) {
+        const VerilatedLockGuard lockGuard{m_mutex};
+        if (m_queue.empty()) return false;
+        result = m_queue.front();
+        m_queue.pop_front();
+        return true;
+    }
+};
+
+// Commands used by thread tracing. Anonymous enum in class, as we want
+// it scoped, but we also want the automatic conversion to integer types.
+class VerilatedTraceOffloadCommand final {
+public:
+    // These must all fit in 4 bit at the moment, as the tracing routines
+    // pack parameters in the top bits.
+    enum : uint8_t {
+        CHG_BIT_0 = 0x0,
+        CHG_BIT_1 = 0x1,
+        CHG_CDATA = 0x2,
+        CHG_SDATA = 0x3,
+        CHG_IDATA = 0x4,
+        CHG_QDATA = 0x5,
+        CHG_WDATA = 0x6,
+        CHG_DOUBLE = 0x8,
+        CHG_EVENT = 0x9,
+        // TODO: full..
+        TIME_CHANGE = 0xc,
+        TRACE_BUFFER = 0xd,
+        END = 0xe,  // End of buffer
+        SHUTDOWN = 0xf  // Shutdown worker thread, also marks end of buffer
+    };
+};
+
+//=============================================================================
+// VerilatedTraceConfig
+
+// Simple data representing trace configuration required by generated models.
+class VerilatedTraceConfig final {
+public:
+    const bool m_useParallel;  // Use parallel tracing
+    const bool m_useOffloading;  // Offloading trace rendering
+    const bool m_useFstWriterThread;  // Use the separate FST writer thread
+
+    VerilatedTraceConfig(bool useParallel, bool useOffloading, bool useFstWriterThread)
+        : m_useParallel{useParallel}
+        , m_useOffloading{useOffloading}
+        , m_useFstWriterThread{useFstWriterThread} {}
+};
+
+//=============================================================================
+// VerilatedTraceBaseC - base class of all Verilated*C trace classes
+// Internal use only
+
+class VerilatedTraceBaseC VL_NOT_FINAL {
+    bool m_modelConnected = false;  // Model connected by calling Verilated::trace()
+public:
+    /// True if file currently open
+    virtual bool isOpen() const VL_MT_SAFE = 0;
+
+    // internal use only
+    bool modelConnected() const VL_MT_SAFE { return m_modelConnected; }
+    void modelConnected(bool flag) VL_MT_SAFE { m_modelConnected = flag; }
+};
+
+//=============================================================================
+// VerilatedTrace
+
+// T_Trace is the format-specific subclass of VerilatedTrace.
+// T_Buffer is the format-specific base class of VerilatedTraceBuffer.
+template 
+class VerilatedTrace VL_NOT_FINAL {
+public:
+    using Buffer = VerilatedTraceBuffer;
+    using OffloadBuffer = VerilatedTraceOffloadBuffer;
+
+    //=========================================================================
+    // Generic tracing internals
+
+    using initCb_t = void (*)(void*, T_Trace*, uint32_t);  // Type of init callbacks
+    using dumpCb_t = void (*)(void*, Buffer*);  // Type of dump callbacks
+    using dumpOffloadCb_t = void (*)(void*, OffloadBuffer*);  // Type of offload dump callbacks
+    using cleanupCb_t = void (*)(void*, T_Trace*);  // Type of cleanup callbacks
+
+private:
+    // Give the buffer (both base and derived) access to the private bits
+    friend T_Buffer;
+    friend Buffer;
+    friend OffloadBuffer;
+
+    struct CallbackRecord final {
+        union {  // The callback
+            const initCb_t m_initCb;
+            const dumpCb_t m_dumpCb;
+            const dumpOffloadCb_t m_dumpOffloadCb;
+            const cleanupCb_t m_cleanupCb;
+        };
+        const uint32_t m_fidx;  // The index of the tracing function
+        void* const m_userp;  // The user pointer to pass to the callback (the symbol table)
+        const bool m_isLibInstance;  // Whether the callback is for a --lib-create instance
+        const std::string m_name;  // The name of the instance callback is for
+        const uint32_t m_nTraceCodes;  // The number of trace codes used by callback
+        CallbackRecord(initCb_t cb, void* userp, bool isLibInstance, const std::string& name,
+                       uint32_t nTraceCodes)
+            : m_initCb{cb}
+            , m_fidx{0}
+            , m_userp{userp}
+            , m_isLibInstance{isLibInstance}
+            , m_name{name}
+            , m_nTraceCodes{nTraceCodes} {}
+        CallbackRecord(dumpCb_t cb, uint32_t fidx, void* userp)
+            : m_dumpCb{cb}
+            , m_fidx{fidx}
+            , m_userp{userp}
+            , m_isLibInstance{false}  // Don't care
+            , m_name{}  // Don't care
+            , m_nTraceCodes{0}  // Don't care
+        {}
+        CallbackRecord(dumpOffloadCb_t cb, uint32_t fidx, void* userp)
+            : m_dumpOffloadCb{cb}
+            , m_fidx{fidx}
+            , m_userp{userp}
+            , m_isLibInstance{false}  // Don't care
+            , m_name{}  // Don't care
+            , m_nTraceCodes{0}  // Don't care
+        {}
+        CallbackRecord(cleanupCb_t cb, void* userp)
+            : m_cleanupCb{cb}
+            , m_fidx{0}
+            , m_userp{userp}
+            , m_isLibInstance{false}  // Don't care
+            , m_name{}  // Don't care
+            , m_nTraceCodes{0}  // Don't care
+        {}
+    };
+
+    bool m_offload = false;  // Use the offload thread
+    bool m_parallel = false;  // Use parallel tracing
+
+    struct ParallelWorkerData final {
+        const dumpCb_t m_cb;  // The callback
+        void* const m_userp;  // The use pointer to pass to the callback
+        Buffer* const m_bufp;  // The buffer pointer to pass to the callback
+        std::atomic m_ready{false};  // The ready flag
+        mutable VerilatedMutex m_mutex;  // Mutex for suspension until ready
+        std::condition_variable_any m_cv;  // Condition variable for suspension
+        bool m_waiting VL_GUARDED_BY(m_mutex) = false;  // Whether a thread is suspended in wait()
+
+        void wait();
+
+        ParallelWorkerData(dumpCb_t cb, void* userp, Buffer* bufp)
+            : m_cb{cb}
+            , m_userp{userp}
+            , m_bufp{bufp} {}
+    };
+
+    // Passed a ParallelWorkerData*, second argument is ignored
+    static void parallelWorkerTask(void*, bool);
+
+protected:
+    uint32_t* m_sigs_oldvalp = nullptr;  // Previous value store
+    EData* m_sigs_enabledp = nullptr;  // Bit vector of enabled codes (nullptr = all on)
+private:
+    std::vector m_sigs_enabledVec;  // Staging for m_sigs_enabledp
+    std::vector m_initCbs;  // Routines to initialize tracing
+    std::vector m_constCbs;  // Routines to perform const dump
+    std::vector m_constOffloadCbs;  // Routines to perform offloaded const dump
+    std::vector m_fullCbs;  // Routines to perform full dump
+    std::vector m_fullOffloadCbs;  // Routines to perform offloaded full dump
+    std::vector m_chgCbs;  // Routines to perform incremental dump
+    std::vector m_chgOffloadCbs;  // Routines to perform offloaded incremental dump
+    std::vector m_cleanupCbs;  // Routines to call at the end of dump
+    bool m_constDump = true;  // Whether a const dump is required on the next call to 'dump'
+    bool m_fullDump = true;  // Whether a full dump is required on the next call to 'dump'
+    uint32_t m_nextCode = 0;  // Next code number to assign
+    uint32_t m_numSignals = 0;  // Number of distinct signals
+    uint32_t m_maxBits = 0;  // Number of bits in the widest signal
+    void* m_initUserp = nullptr;  // The callback userp of the instance currently being initialized
+    // TODO: Should keep this as a Trie, that is how it's accessed all the time.
+    std::vector> m_dumpvars;  // dumpvar() entries
+    double m_timeRes = 1e-9;  // Time resolution (ns/ms etc)
+    double m_timeUnit = 1e-0;  // Time units (ns/ms etc)
+    uint64_t m_timeLastDump = 0;  // Last time we did a dump
+    bool m_didSomeDump = false;  // Did at least one dump (i.e.: m_timeLastDump is valid)
+    VerilatedContext* m_contextp = nullptr;  // The context used by the traced models
+    std::set m_models;  // The collection of models being traced
+
+    void addCallbackRecord(std::vector& cbVec, CallbackRecord&& cbRec)
+        VL_MT_SAFE_EXCLUDES(m_mutex);
+
+    // Equivalent to 'this' but is of the sub-type 'T_Trace*'. Use 'self()->'
+    // to access duck-typed functions to avoid a virtual function call.
+    T_Trace* self() { return static_cast(this); }
+
+    void runCallbacks(const std::vector& cbVec);
+    void runOffloadedCallbacks(const std::vector& cbVec);
+
+    // Flush any remaining data for this file
+    static void onFlush(void* selfp) VL_MT_UNSAFE_ONE;
+    // Close the file on termination
+    static void onExit(void* selfp) VL_MT_UNSAFE_ONE;
+
+    // Number of total offload buffers that have been allocated
+    uint32_t m_numOffloadBuffers = 0;
+    // Size of offload buffers
+    size_t m_offloadBufferSize = 0;
+    // Buffers handed to worker for processing
+    VerilatedThreadQueue m_offloadBuffersToWorker;
+    // Buffers returned from worker after processing
+    VerilatedThreadQueue m_offloadBuffersFromWorker;
+
+protected:
+    // Write pointer into current buffer
+    uint32_t* m_offloadBufferWritep = nullptr;
+    // End of offload buffer
+    uint32_t* m_offloadBufferEndp = nullptr;
+
+private:
+    // The offload worker thread itself
+    std::unique_ptr m_workerThread;
+
+    // Get a new offload buffer that can be populated. May block if none available
+    uint32_t* getOffloadBuffer();
+
+    // The function executed by the offload worker thread
+    void offloadWorkerThreadMain();
+
+    // Wait until given offload buffer is placed in m_offloadBuffersFromWorker
+    void waitForOffloadBuffer(const uint32_t* bufferp);
+
+    // Shut down and join worker, if it's running, otherwise do nothing
+    void shutdownOffloadWorker();
+
+    // CONSTRUCTORS
+    VL_UNCOPYABLE(VerilatedTrace);
+
+protected:
+    //=========================================================================
+    // Internals available to format-specific implementations
+
+    mutable VerilatedMutex m_mutex;  // Ensure dump() etc only called from single thread
+
+    uint32_t nextCode() const { return m_nextCode; }
+    uint32_t numSignals() const { return m_numSignals; }
+    uint32_t maxBits() const { return m_maxBits; }
+    void* initUserp() const { return m_initUserp; }
+    void constDump(bool value) { m_constDump = value; }
+    void fullDump(bool value) { m_fullDump = value; }
+
+    double timeRes() const { return m_timeRes; }
+    double timeUnit() const { return m_timeUnit; }
+    std::string timeResStr() const;
+
+    void traceInit() VL_MT_UNSAFE;
+
+    // Declare new signal and return true if enabled
+    bool declCode(uint32_t code, const std::string& declName, uint32_t bits);
+
+    void closeBase();
+    void flushBase();
+
+    bool offload() const { return m_offload; }
+    bool parallel() const { return m_parallel; }
+
+    // Return last ' ' separated word. Assumes string does not end in ' '.
+    static std::string lastWord(const std::string& str) {
+        const size_t idx = str.rfind(' ');
+        if (idx == std::string::npos) return str;
+        return str.substr(idx + 1);
+    }
+
+    //=========================================================================
+    // Virtual functions to be provided by the format-specific implementation
+
+    // Called when the trace moves forward to a new time point
+    virtual void emitTimeChange(uint64_t timeui) = 0;
+
+    // These hooks are called before a full or change based dump is produced.
+    // The return value indicates whether to proceed with the dump.
+    virtual bool preFullDump() = 0;
+    virtual bool preChangeDump() = 0;
+
+    // Trace buffer management
+    virtual Buffer* getTraceBuffer(uint32_t fidx) = 0;
+    virtual void commitTraceBuffer(Buffer*) = 0;
+
+    // Configure sub-class
+    virtual void configure(const VerilatedTraceConfig&) = 0;
+
+public:
+    //=========================================================================
+    // External interface to client code
+
+    explicit VerilatedTrace();
+    ~VerilatedTrace();
+
+    // Set time units (s/ms, defaults to ns)
+    void set_time_unit(const char* unitp) VL_MT_SAFE;
+    void set_time_unit(const std::string& unit) VL_MT_SAFE;
+    // Set time resolution (s/ms, defaults to ns)
+    void set_time_resolution(const char* unitp) VL_MT_SAFE;
+    void set_time_resolution(const std::string& unit) VL_MT_SAFE;
+    // Set variables to dump, using $dumpvars format
+    // If level = 0, dump everything and hier is then ignored
+    void dumpvars(int level, const std::string& hier) VL_MT_SAFE;
+
+    // Call
+    void dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);
+
+    //=========================================================================
+    // Internal interface to Verilator generated code
+
+    //=========================================================================
+    // Non-hot path internal interface to Verilator generated code
+
+    void addModel(VerilatedModel*) VL_MT_SAFE_EXCLUDES(m_mutex);
+    void addInitCb(initCb_t cb, void* userp, const std::string& name, bool isLibInstance,
+                   uint32_t nTraceCodes) VL_MT_SAFE;
+    void addConstCb(dumpCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
+    void addConstCb(dumpOffloadCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
+    void addFullCb(dumpCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
+    void addFullCb(dumpOffloadCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
+    void addChgCb(dumpCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
+    void addChgCb(dumpOffloadCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
+    void addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE;
+    void initLib(const std::string& name) VL_MT_UNSAFE;
+};
+
+//=============================================================================
+// VerilatedTraceBuffer
+
+// T_Buffer is the format-specific base class of VerilatedTraceBuffer.
+// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
+template 
+class VerilatedTraceBuffer VL_NOT_FINAL : public T_Buffer {
+protected:
+    // Type of the owner trace file
+    using Trace = typename std::remove_cv<
+        typename std::remove_reference::type>::type;
+
+    static_assert(std::has_virtual_destructor::value, "");
+    static_assert(std::is_base_of, Trace>::value, "");
+
+    friend Trace;  // Give the trace file access to the private bits
+    friend std::default_delete>;
+
+    uint32_t* const m_sigs_oldvalp;  // Previous value store
+    EData* const m_sigs_enabledp;  // Bit vector of enabled codes (nullptr = all on)
+
+    explicit VerilatedTraceBuffer(Trace& owner);
+    ~VerilatedTraceBuffer() override = default;
+
+public:
+    //=========================================================================
+    // Hot path internal interface to Verilator generated code
+
+    // Implementation note: We rely on the following duck-typed implementations
+    // in the derived class T_Derived. These emit* functions record a format-
+    // specific trace entry. Normally one would use pure virtual functions for
+    // these here, but we cannot afford dynamic dispatch for calling these as
+    // this is very hot code during tracing.
+
+    // duck-typed void emitBit(uint32_t code, CData newval) = 0;
+    // duck-typed void emitCData(uint32_t code, CData newval, int bits) = 0;
+    // duck-typed void emitSData(uint32_t code, SData newval, int bits) = 0;
+    // duck-typed void emitIData(uint32_t code, IData newval, int bits) = 0;
+    // duck-typed void emitQData(uint32_t code, QData newval, int bits) = 0;
+    // duck-typed void emitWData(uint32_t code, const WData* newvalp, int bits) = 0;
+    // duck-typed void emitDouble(uint32_t code, double newval) = 0;
+
+    VL_ATTR_ALWINLINE uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
+
+    // Write to previous value buffer value and emit trace entry.
+    void fullBit(uint32_t* oldp, CData newval);
+    void fullCData(uint32_t* oldp, CData newval, int bits);
+    void fullSData(uint32_t* oldp, SData newval, int bits);
+    void fullIData(uint32_t* oldp, IData newval, int bits);
+    void fullQData(uint32_t* oldp, QData newval, int bits);
+    void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
+    void fullDouble(uint32_t* oldp, double newval);
+    void fullEvent(uint32_t* oldp, const VlEventBase* newvalp);
+    void fullEventTriggered(uint32_t* oldp);
+
+    // In non-offload mode, these are called directly by the trace callbacks,
+    // and are called chg*. In offload mode, they are called by the worker
+    // thread and are called chg*Impl
+
+    // Check previous dumped value of signal. If changed, then emit trace entry
+    VL_ATTR_ALWINLINE void chgBit(uint32_t* oldp, CData newval) {
+        const uint32_t diff = *oldp ^ newval;
+        if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
+    }
+    VL_ATTR_ALWINLINE void chgCData(uint32_t* oldp, CData newval, int bits) {
+        const uint32_t diff = *oldp ^ newval;
+        if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
+    }
+    VL_ATTR_ALWINLINE void chgSData(uint32_t* oldp, SData newval, int bits) {
+        const uint32_t diff = *oldp ^ newval;
+        if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
+    }
+    VL_ATTR_ALWINLINE void chgIData(uint32_t* oldp, IData newval, int bits) {
+        const uint32_t diff = *oldp ^ newval;
+        if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
+    }
+    VL_ATTR_ALWINLINE void chgQData(uint32_t* oldp, QData newval, int bits) {
+        QData old;
+        std::memcpy(&old, oldp, sizeof(old));
+        const uint64_t diff = old ^ newval;
+        if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
+    }
+    VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
+        for (int i = 0; i < (bits + 31) / 32; ++i) {
+            if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
+                fullWData(oldp, newvalp, bits);
+                return;
+            }
+        }
+    }
+    VL_ATTR_ALWINLINE void chgEvent(uint32_t* oldp, const VlEventBase* newvalp) {
+        if (newvalp->isTriggered()) fullEvent(oldp, newvalp);
+    }
+    VL_ATTR_ALWINLINE void chgEventTriggered(uint32_t* oldp) { fullEventTriggered(oldp); }
+    VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) {
+        double old;  // LCOV_EXCL_LINE  // lcov bug
+        std::memcpy(&old, oldp, sizeof(old));
+        if (VL_UNLIKELY(old != newval)) fullDouble(oldp, newval);
+    }
+};
+
+//=============================================================================
+// VerilatedTraceOffloadBuffer
+
+// T_Buffer is the format-specific base class of VerilatedTraceBuffer.
+// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
+template 
+class VerilatedTraceOffloadBuffer final : public VerilatedTraceBuffer {
+    using typename VerilatedTraceBuffer::Trace;
+
+    friend Trace;  // Give the trace file access to the private bits
+
+    uint32_t* m_offloadBufferWritep;  // Write pointer into current buffer
+    uint32_t* const m_offloadBufferEndp;  // End of offload buffer
+
+    explicit VerilatedTraceOffloadBuffer(Trace& owner);
+    ~VerilatedTraceOffloadBuffer() override = default;
+
+public:
+    //=========================================================================
+    // Hot path internal interface to Verilator generated code
+
+    // Offloaded tracing. Just dump everything in the offload buffer
+    void chgBit(uint32_t code, CData newval) {
+        m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
+        m_offloadBufferWritep[1] = code;
+        m_offloadBufferWritep += 2;
+        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
+    }
+    void chgCData(uint32_t code, CData newval, int bits) {
+        m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
+        m_offloadBufferWritep[1] = code;
+        m_offloadBufferWritep[2] = newval;
+        m_offloadBufferWritep += 3;
+        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
+    }
+    void chgSData(uint32_t code, SData newval, int bits) {
+        m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
+        m_offloadBufferWritep[1] = code;
+        m_offloadBufferWritep[2] = newval;
+        m_offloadBufferWritep += 3;
+        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
+    }
+    void chgIData(uint32_t code, IData newval, int bits) {
+        m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
+        m_offloadBufferWritep[1] = code;
+        m_offloadBufferWritep[2] = newval;
+        m_offloadBufferWritep += 3;
+        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
+    }
+    void chgQData(uint32_t code, QData newval, int bits) {
+        m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
+        m_offloadBufferWritep[1] = code;
+        *reinterpret_cast(m_offloadBufferWritep + 2) = newval;
+        m_offloadBufferWritep += 4;
+        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
+    }
+    void chgWData(uint32_t code, const WData* newvalp, int bits) {
+        m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
+        m_offloadBufferWritep[1] = code;
+        m_offloadBufferWritep += 2;
+        for (int i = 0; i < (bits + 31) / 32; ++i) *m_offloadBufferWritep++ = newvalp[i];
+        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
+    }
+    void chgDouble(uint32_t code, double newval) {
+        m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
+        m_offloadBufferWritep[1] = code;
+        // cppcheck-suppress invalidPointerCast
+        *reinterpret_cast(m_offloadBufferWritep + 2) = newval;
+        m_offloadBufferWritep += 4;
+        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
+    }
+    void chgEvent(uint32_t code, const VlEventBase* newvalp) {
+        if (newvalp->isTriggered()) chgEventTriggered(code);
+    }
+    void chgEventTriggered(uint32_t code) {
+        m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_EVENT;
+        m_offloadBufferWritep[1] = code;
+        m_offloadBufferWritep += 2;
+        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
+    }
+};
+
+#endif  // guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace_imp.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace_imp.h
new file mode 100644
index 00000000000..635e3e897ce
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace_imp.h
@@ -0,0 +1,936 @@
+// -*- mode: C++; c-file-style: "cc-mode" -*-
+//=============================================================================
+//
+// Code available from: https://verilator.org
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of either the GNU Lesser General Public License Version 3
+// or the Perl Artistic License Version 2.0.
+// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
+// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+//
+//=============================================================================
+//
+// Verilated tracing implementation code template common to all formats.
+// This file is included by the format-specific implementations and
+// should not be used otherwise.
+//
+//=============================================================================
+
+// clang-format off
+
+#ifndef VL_CPPCHECK
+#if !defined(VL_SUB_T) || !defined(VL_BUF_T)
+# error "This file should be included in trace format implementations"
+#endif
+
+#include "verilated_intrinsics.h"
+#include "verilated_trace.h"
+#include "verilated_threads.h"
+#include 
+
+#if 0
+# include 
+# define VL_TRACE_OFFLOAD_DEBUG(msg) std::cout << "TRACE OFFLOAD THREAD: " << msg << "\n"
+#else
+# define VL_TRACE_OFFLOAD_DEBUG(msg)
+#endif
+
+// clang-format on
+
+//=============================================================================
+// Static utility functions
+
+static double timescaleToDouble(const char* unitp) VL_PURE {
+    char* endp = nullptr;
+    double value = std::strtod(unitp, &endp);
+    // On error so we allow just "ns" to return 1e-9.
+    if (value == 0.0 && endp == unitp) value = 1;
+    unitp = endp;
+    for (; *unitp && std::isspace(*unitp); ++unitp) {}
+    switch (*unitp) {
+    case 's': value *= 1e0; break;
+    case 'm': value *= 1e-3; break;
+    case 'u': value *= 1e-6; break;
+    case 'n': value *= 1e-9; break;
+    case 'p': value *= 1e-12; break;
+    case 'f': value *= 1e-15; break;
+    case 'a': value *= 1e-18; break;
+    }
+    return value;
+}
+
+//=========================================================================
+// Buffer management
+
+template <>
+uint32_t* VerilatedTrace::getOffloadBuffer() {
+    uint32_t* bufferp;
+    // Some jitter is expected, so some number of alternative offload buffers are
+    // required, but don't allocate more than 8 buffers.
+    if (m_numOffloadBuffers < 8) {
+        // Allocate a new buffer if none is available
+        if (!m_offloadBuffersFromWorker.tryGet(bufferp)) {
+            ++m_numOffloadBuffers;
+            // Note: over allocate a bit so pointer comparison is well defined
+            // if we overflow only by a small amount
+            bufferp = new uint32_t[m_offloadBufferSize + 16];
+        }
+    } else {
+        // Block until a buffer becomes available
+        bufferp = m_offloadBuffersFromWorker.get();
+    }
+    return bufferp;
+}
+
+template <>
+void VerilatedTrace::waitForOffloadBuffer(const uint32_t* buffp) {
+    // Slow path code only called on flush/shutdown, so use a simple algorithm.
+    // Collect buffers from worker and stash them until we get the one we want.
+    std::deque stash;
+    do { stash.push_back(m_offloadBuffersFromWorker.get()); } while (stash.back() != buffp);
+    // Now put them back in the queue, in the original order.
+    while (!stash.empty()) {
+        m_offloadBuffersFromWorker.put_front(stash.back());
+        stash.pop_back();
+    }
+}
+
+//=========================================================================
+// Worker thread
+
+template <>
+void VerilatedTrace::offloadWorkerThreadMain() {
+    bool shutdown = false;
+
+    do {
+        uint32_t* const bufferp = m_offloadBuffersToWorker.get();
+
+        VL_TRACE_OFFLOAD_DEBUG("");
+        VL_TRACE_OFFLOAD_DEBUG("Got buffer: " << bufferp);
+
+        const uint32_t* readp = bufferp;
+
+        std::unique_ptr traceBufp;  // We own the passed tracebuffer
+
+        while (true) {
+            const uint32_t cmd = readp[0];
+            const uint32_t top = cmd >> 4;
+            // Always set this up, as it is almost always needed
+            uint32_t* const oldp = m_sigs_oldvalp + readp[1];
+            // Note this increment needs to be undone on commands which do not
+            // actually contain a code, but those are the rare cases.
+            readp += 2;
+
+            switch (cmd & 0xF) {
+                //===
+                // CHG_* commands
+            case VerilatedTraceOffloadCommand::CHG_BIT_0:
+                VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_0 " << top);
+                traceBufp->chgBit(oldp, 0);
+                continue;
+            case VerilatedTraceOffloadCommand::CHG_BIT_1:
+                VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_1 " << top);
+                traceBufp->chgBit(oldp, 1);
+                continue;
+            case VerilatedTraceOffloadCommand::CHG_CDATA:
+                VL_TRACE_OFFLOAD_DEBUG("Command CHG_CDATA " << top);
+                // Bits stored in bottom byte of command
+                traceBufp->chgCData(oldp, *readp, top);
+                readp += 1;
+                continue;
+            case VerilatedTraceOffloadCommand::CHG_SDATA:
+                VL_TRACE_OFFLOAD_DEBUG("Command CHG_SDATA " << top);
+                // Bits stored in bottom byte of command
+                traceBufp->chgSData(oldp, *readp, top);
+                readp += 1;
+                continue;
+            case VerilatedTraceOffloadCommand::CHG_IDATA:
+                VL_TRACE_OFFLOAD_DEBUG("Command CHG_IDATA " << top);
+                // Bits stored in bottom byte of command
+                traceBufp->chgIData(oldp, *readp, top);
+                readp += 1;
+                continue;
+            case VerilatedTraceOffloadCommand::CHG_QDATA:
+                VL_TRACE_OFFLOAD_DEBUG("Command CHG_QDATA " << top);
+                // Bits stored in bottom byte of command
+                traceBufp->chgQData(oldp, *reinterpret_cast(readp), top);
+                readp += 2;
+                continue;
+            case VerilatedTraceOffloadCommand::CHG_WDATA:
+                VL_TRACE_OFFLOAD_DEBUG("Command CHG_WDATA " << top);
+                traceBufp->chgWData(oldp, readp, top);
+                readp += VL_WORDS_I(top);
+                continue;
+            case VerilatedTraceOffloadCommand::CHG_DOUBLE:
+                VL_TRACE_OFFLOAD_DEBUG("Command CHG_DOUBLE " << top);
+                traceBufp->chgDouble(oldp, *reinterpret_cast(readp));
+                readp += 2;
+                continue;
+            case VerilatedTraceOffloadCommand::CHG_EVENT:
+                VL_TRACE_OFFLOAD_DEBUG("Command CHG_EVENT " << top);
+                traceBufp->chgEventTriggered(oldp);
+                continue;
+
+                //===
+                // Rare commands
+            case VerilatedTraceOffloadCommand::TIME_CHANGE: {
+                VL_TRACE_OFFLOAD_DEBUG("Command TIME_CHANGE " << top);
+                readp -= 1;  // No code in this command, undo increment
+                const uint64_t timeui
+                    = static_cast(*reinterpret_cast(readp)) << 32ULL
+                      | static_cast(*reinterpret_cast(readp + 1));
+                emitTimeChange(timeui);
+                readp += 2;
+                continue;
+            }
+            case VerilatedTraceOffloadCommand::TRACE_BUFFER:
+                VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
+                readp -= 1;  // No code in this command, undo increment
+                traceBufp.reset(*reinterpret_cast(readp));
+                readp += 2;
+                continue;
+
+                //===
+                // Commands ending this buffer
+            case VerilatedTraceOffloadCommand::END:  //
+                VL_TRACE_OFFLOAD_DEBUG("Command END");
+                break;
+            case VerilatedTraceOffloadCommand::SHUTDOWN:
+                VL_TRACE_OFFLOAD_DEBUG("Command SHUTDOWN");
+                shutdown = true;
+                break;
+
+            //===
+            // Unknown command
+            default: {  // LCOV_EXCL_START
+                VL_TRACE_OFFLOAD_DEBUG("Command UNKNOWN " << cmd);
+                VL_FATAL_MT(__FILE__, __LINE__, "", "Unknown trace command");
+                break;
+            }  // LCOV_EXCL_STOP
+            }
+
+            // The above switch will execute 'continue' when necessary,
+            // so if we ever reach here, we are done with the buffer.
+            break;
+        }
+
+        VL_TRACE_OFFLOAD_DEBUG("Returning buffer");
+
+        // Return buffer
+        m_offloadBuffersFromWorker.put(bufferp);
+    } while (VL_LIKELY(!shutdown));
+}
+
+template <>
+void VerilatedTrace::shutdownOffloadWorker() {
+    // If the worker thread is not running, done..
+    if (!m_workerThread) return;
+
+    // Hand an buffer with a shutdown command to the worker thread
+    uint32_t* const bufferp = getOffloadBuffer();
+    bufferp[0] = VerilatedTraceOffloadCommand::SHUTDOWN;
+    m_offloadBuffersToWorker.put(bufferp);
+    // Wait for it to return
+    waitForOffloadBuffer(bufferp);
+    // Join the thread and delete it
+    m_workerThread->join();
+    m_workerThread.reset(nullptr);
+}
+
+//=============================================================================
+// Life cycle
+
+template <>
+void VerilatedTrace::closeBase() {
+    if (offload()) {
+        shutdownOffloadWorker();
+        while (m_numOffloadBuffers) {
+            delete[] m_offloadBuffersFromWorker.get();
+            --m_numOffloadBuffers;
+        }
+    }
+}
+
+template <>
+void VerilatedTrace::flushBase() {
+    if (offload()) {
+        // Hand an empty buffer to the worker thread
+        uint32_t* const bufferp = getOffloadBuffer();
+        *bufferp = VerilatedTraceOffloadCommand::END;
+        m_offloadBuffersToWorker.put(bufferp);
+        // Wait for it to be returned. As the processing is in-order,
+        // this ensures all previous buffers have been processed.
+        waitForOffloadBuffer(bufferp);
+    }
+}
+
+//=============================================================================
+// Callbacks to run on global events
+
+template <>
+void VerilatedTrace::onFlush(void* selfp) {
+    // This calls 'flush' on the derived class (which must then get any mutex)
+    reinterpret_cast(selfp)->flush();
+}
+
+template <>
+void VerilatedTrace::onExit(void* selfp) {
+    // This calls 'close' on the derived class (which must then get any mutex)
+    reinterpret_cast(selfp)->close();
+}
+
+//=============================================================================
+// VerilatedTrace
+
+template <>
+VerilatedTrace::VerilatedTrace() {
+    set_time_unit(Verilated::threadContextp()->timeunitString());
+    set_time_resolution(Verilated::threadContextp()->timeprecisionString());
+}
+
+template <>
+VerilatedTrace::~VerilatedTrace() {
+    if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = nullptr);
+    if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
+    Verilated::removeFlushCb(VerilatedTrace::onFlush, this);
+    Verilated::removeExitCb(VerilatedTrace::onExit, this);
+    if (offload()) closeBase();
+}
+
+//=========================================================================
+// Internals available to format-specific implementations
+
+template <>
+void VerilatedTrace::traceInit() VL_MT_UNSAFE {
+    // Note: It is possible to re-open a trace file (VCD in particular),
+    // so we must reset the next code here, but it must have the same number
+    // of codes on re-open
+    const uint32_t expectedCodes = nextCode();
+    m_nextCode = 1;
+    m_numSignals = 0;
+    m_maxBits = 0;
+    m_sigs_enabledVec.clear();
+
+    // Call all initialize callbacks for root (non-library) instances, which will:
+    // - Call decl* for each signal (these eventually call ::declCode)
+    // - Call the initialize callbacks of library instances underneath
+    // - Store the base code
+    for (const CallbackRecord& cbr : m_initCbs) {
+        if (cbr.m_isLibInstance) continue;  // Will be called from parent callback
+        const uint32_t baseCode = nextCode();
+        m_nextCode += cbr.m_nTraceCodes;
+        m_initUserp = cbr.m_userp;
+        cbr.m_initCb(cbr.m_userp, self(), baseCode);
+    }
+
+    if (expectedCodes && nextCode() != expectedCodes) {
+        VL_FATAL_MT(__FILE__, __LINE__, "",
+                    "Reopening trace file with different number of signals");
+    }
+
+    // Now that we know the number of codes, allocate space for the buffer
+    // holding previous signal values.
+    if (!m_sigs_oldvalp) m_sigs_oldvalp = new uint32_t[nextCode()];
+
+    // Apply enables
+    if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
+    if (!m_sigs_enabledVec.empty()) {
+        // Else if was empty, m_sigs_enabledp = nullptr to short circuit tests
+        // But it isn't, so alloc one bit for each code to indicate enablement
+        // We don't want to still use m_signs_enabledVec as std::vector is not
+        // guaranteed to be fast
+        m_sigs_enabledp = new uint32_t[1 + VL_WORDS_I(nextCode())]{0};
+        m_sigs_enabledVec.reserve(nextCode());
+        for (size_t code = 0; code < nextCode(); ++code) {
+            if (m_sigs_enabledVec[code]) {
+                m_sigs_enabledp[VL_BITWORD_I(code)] |= 1U << VL_BITBIT_I(code);
+            }
+        }
+        m_sigs_enabledVec.clear();
+    }
+
+    // Set callback so flush/abort will flush this file
+    Verilated::addFlushCb(VerilatedTrace::onFlush, this);
+    Verilated::addExitCb(VerilatedTrace::onExit, this);
+
+    if (offload()) {
+        // Compute offload buffer size. we need to be able to store a new value for
+        // each signal, which is 'nextCode()' entries after the init callbacks
+        // above have been run, plus up to 2 more words of metadata per signal,
+        // plus fixed overhead of 1 for a termination flag and 3 for a time stamp
+        // update and 2 for the buffer address.
+        m_offloadBufferSize = nextCode() + numSignals() * 2 + 6;
+
+        // Start the worker thread
+        m_workerThread.reset(
+            new std::thread{&VerilatedTrace::offloadWorkerThreadMain, this});
+    }
+}
+
+template <>
+bool VerilatedTrace::declCode(uint32_t code, const std::string& declName,
+                                                  uint32_t bits) {
+    if (VL_UNCOVERABLE(!code)) {
+        VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
+    }
+    // To keep it simple, this is O(enables * signals), but we expect few enables
+    bool enabled = false;
+    if (m_dumpvars.empty()) enabled = true;
+    for (const auto& item : m_dumpvars) {
+        const int dumpvarsLevel = item.first;
+        const char* dvp = item.second.c_str();
+        const char* np = declName.c_str();
+        while (*dvp && *dvp == *np) {
+            ++dvp;
+            ++np;
+        }
+        if (*dvp) continue;  // Didn't match dumpvar item
+        if (*np && *np != ' ') continue;  // e.g. "t" isn't a match for "top"
+        int levels = 0;
+        while (*np) {
+            if (*np++ == ' ') ++levels;
+        }
+        if (levels > dumpvarsLevel) continue;  // Too deep
+        // We only need to set first code word if it's a multicode signal
+        // as that's all we'll check for later
+        if (m_sigs_enabledVec.size() <= code) m_sigs_enabledVec.resize((code + 1024) * 2);
+        m_sigs_enabledVec[code] = true;
+        enabled = true;
+        break;
+    }
+
+    ++m_numSignals;
+    m_maxBits = std::max(m_maxBits, bits);
+    return enabled;
+}
+
+//=========================================================================
+// Internals available to format-specific implementations
+
+template <>
+std::string VerilatedTrace::timeResStr() const {
+    return vl_timescaled_double(m_timeRes);
+}
+
+//=========================================================================
+// External interface to client code
+
+template <>
+void VerilatedTrace::set_time_unit(const char* unitp) VL_MT_SAFE {
+    m_timeUnit = timescaleToDouble(unitp);
+}
+template <>
+void VerilatedTrace::set_time_unit(const std::string& unit) VL_MT_SAFE {
+    set_time_unit(unit.c_str());
+}
+template <>
+void VerilatedTrace::set_time_resolution(const char* unitp) VL_MT_SAFE {
+    m_timeRes = timescaleToDouble(unitp);
+}
+template <>
+void VerilatedTrace::set_time_resolution(const std::string& unit) VL_MT_SAFE {
+    set_time_resolution(unit.c_str());
+}
+template <>
+void VerilatedTrace::dumpvars(int level, const std::string& hier) VL_MT_SAFE {
+    if (level == 0) {
+        m_dumpvars.clear();  // empty = everything on
+    } else {
+        // Convert Verilog . separators to trace space separators
+        std::string hierSpaced = hier;
+        for (auto& i : hierSpaced) {
+            if (i == '.') i = ' ';
+        }
+        m_dumpvars.emplace_back(level, hierSpaced);
+    }
+}
+
+template <>
+void VerilatedTrace::parallelWorkerTask(void* datap, bool) {
+    ParallelWorkerData* const wdp = reinterpret_cast(datap);
+    // Run the task
+    wdp->m_cb(wdp->m_userp, wdp->m_bufp);
+    // Mark buffer as ready
+    const VerilatedLockGuard lock{wdp->m_mutex};
+    wdp->m_ready.store(true);
+    if (wdp->m_waiting) wdp->m_cv.notify_one();
+}
+
+template <>
+VL_ATTR_NOINLINE void VerilatedTrace::ParallelWorkerData::wait() {
+    // Spin for a while, waiting for the buffer to become ready
+    for (int i = 0; i < VL_LOCK_SPINS; ++i) {
+        if (VL_LIKELY(m_ready.load(std::memory_order_relaxed))) return;
+        VL_CPU_RELAX();
+    }
+    // We have been spinning for a while, so yield the thread
+    VerilatedLockGuard lock{m_mutex};
+    m_waiting = true;
+    m_cv.wait(m_mutex, [this] { return m_ready.load(std::memory_order_relaxed); });
+    m_waiting = false;
+}
+
+template <>
+void VerilatedTrace::runCallbacks(const std::vector& cbVec) {
+    if (parallel()) {
+        // If tracing in parallel, dispatch to the thread pool
+        VlThreadPool* threadPoolp = static_cast(m_contextp->threadPoolp());
+        // List of work items for thread (std::list, as ParallelWorkerData is not movable)
+        std::list workerData;
+        // We use the whole pool + the main thread
+        const unsigned threads = threadPoolp->numThreads() + 1;
+        // Main thread executes all jobs with index % threads == 0
+        std::vector mainThreadWorkerData;
+        // Enqueue all the jobs
+        for (const CallbackRecord& cbr : cbVec) {
+            // Always get the trace buffer on the main thread
+            Buffer* const bufp = getTraceBuffer(cbr.m_fidx);
+            // Create new work item
+            workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
+            // Grab the new work item
+            ParallelWorkerData* const itemp = &workerData.back();
+            // Enqueue task to thread pool, or main thread
+            if (unsigned rem = cbr.m_fidx % threads) {
+                threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp);
+            } else {
+                mainThreadWorkerData.push_back(itemp);
+            }
+        }
+        // Execute main thread jobs
+        for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
+            parallelWorkerTask(itemp, false);
+        }
+        // Commit all trace buffers in order
+        for (ParallelWorkerData& item : workerData) {
+            // Wait until ready
+            item.wait();
+            // Commit the buffer
+            commitTraceBuffer(item.m_bufp);
+        }
+
+        // Done
+        return;
+    }
+    // Fall back on sequential execution
+    for (const CallbackRecord& cbr : cbVec) {
+        Buffer* const traceBufferp = getTraceBuffer(cbr.m_fidx);
+        cbr.m_dumpCb(cbr.m_userp, traceBufferp);
+        commitTraceBuffer(traceBufferp);
+    }
+}
+
+template <>
+void VerilatedTrace::runOffloadedCallbacks(
+    const std::vector& cbVec) {
+    // Fall back on sequential execution
+    for (const CallbackRecord& cbr : cbVec) {
+        Buffer* traceBufferp = getTraceBuffer(cbr.m_fidx);
+        cbr.m_dumpOffloadCb(cbr.m_userp, static_cast(traceBufferp));
+        commitTraceBuffer(traceBufferp);
+    }
+}
+
+template <>
+void VerilatedTrace::dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
+    // Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
+    // This does get the mutex, but if multiple threads are trying to dump
+    // chances are the data being dumped will have other problems
+    const VerilatedLockGuard lock{m_mutex};
+    if (VL_UNCOVERABLE(m_didSomeDump && timeui <= m_timeLastDump)) {  // LCOV_EXCL_START
+        VL_PRINTF_MT("%%Warning: previous dump at t=%" PRIu64 ", requesting t=%" PRIu64
+                     ", dump call ignored\n",
+                     m_timeLastDump, timeui);
+        return;
+    }  // LCOV_EXCL_STOP
+    m_timeLastDump = timeui;
+    m_didSomeDump = true;
+
+    Verilated::quiesce();
+
+    // Call hook for format-specific behaviour
+    if (VL_UNLIKELY(m_fullDump)) {
+        if (!preFullDump()) return;
+    } else {
+        if (!preChangeDump()) return;
+    }
+
+    uint32_t* bufferp = nullptr;
+    if (offload()) {
+        // Currently only incremental dumps run on the worker thread
+        if (VL_LIKELY(!m_fullDump)) {
+            // Get the offload buffer we are about to fill
+            bufferp = getOffloadBuffer();
+            m_offloadBufferWritep = bufferp;
+            m_offloadBufferEndp = bufferp + m_offloadBufferSize;
+
+            // Tell worker to update time point
+            m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
+            *reinterpret_cast(m_offloadBufferWritep + 1)
+                = static_cast(timeui >> 32ULL);
+            *reinterpret_cast(m_offloadBufferWritep + 2)
+                = static_cast(timeui);
+            m_offloadBufferWritep += 3;
+        } else {
+            // Update time point
+            flushBase();
+            emitTimeChange(timeui);
+        }
+    } else {
+        // Update time point
+        emitTimeChange(timeui);
+    }
+
+    // Run the callbacks
+    if (VL_UNLIKELY(m_fullDump)) {
+        m_fullDump = false;  // No more need for next dump to be full
+        if (offload()) {
+            runOffloadedCallbacks(m_fullOffloadCbs);
+        } else {
+            runCallbacks(m_fullCbs);
+        }
+    } else {
+        if (offload()) {
+            runOffloadedCallbacks(m_chgOffloadCbs);
+        } else {
+            runCallbacks(m_chgCbs);
+        }
+    }
+
+    if (VL_UNLIKELY(m_constDump)) {
+        m_constDump = false;
+        if (offload()) {
+            runOffloadedCallbacks(m_constOffloadCbs);
+        } else {
+            runCallbacks(m_constCbs);
+        }
+    }
+
+    for (const CallbackRecord& cbr : m_cleanupCbs) cbr.m_cleanupCb(cbr.m_userp, self());
+
+    if (offload() && VL_LIKELY(bufferp)) {
+        // Mark end of the offload buffer we just filled
+        *m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::END;
+
+        // Assert no buffer overflow
+        assert(static_cast(m_offloadBufferWritep - bufferp) <= m_offloadBufferSize);
+
+        // Reset our pointers as we are giving up the buffer
+        m_offloadBufferWritep = nullptr;
+        m_offloadBufferEndp = nullptr;
+
+        // Pass it to the worker thread
+        m_offloadBuffersToWorker.put(bufferp);
+    }
+}
+
+//=============================================================================
+// Non-hot path internal interface to Verilator generated code
+
+template <>
+void VerilatedTrace::addModel(VerilatedModel* modelp)
+    VL_MT_SAFE_EXCLUDES(m_mutex) {
+    const VerilatedLockGuard lock{m_mutex};
+
+    const bool firstModel = m_models.empty();
+    const bool newModel = m_models.insert(modelp).second;
+    VerilatedContext* const contextp = modelp->contextp();
+
+    // Validate
+    if (!newModel) {  // LCOV_EXCL_START
+        VL_FATAL_MT(
+            __FILE__, __LINE__, "",
+            "The same model has already been added to this trace file or VerilatedContext");
+    }
+    if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
+        VL_FATAL_MT(__FILE__, __LINE__, "",
+                    "A trace file instance can only handle models from the same VerilatedContext");
+    }
+    if (VL_UNCOVERABLE(m_didSomeDump)) {
+        VL_FATAL_MT(__FILE__, __LINE__, "",
+                    "Cannot add models to a trace file if 'dump' has already been called");
+    }  // LCOV_EXCL_STOP
+
+    // Keep hold of the context
+    m_contextp = contextp;
+
+    // Get the desired trace config from the model
+    const std::unique_ptr configp = modelp->traceConfig();
+
+    // Configure trace base class
+    if (!firstModel) {
+        if (m_offload != configp->m_useOffloading) {
+            VL_FATAL_MT(__FILE__, __LINE__, "",
+                        "Either all or no models using the same trace file must use offloading");
+        }
+    }
+    m_offload = configp->m_useOffloading;
+    // If at least one model requests parallel tracing, then use it
+    m_parallel |= configp->m_useParallel;
+
+    if (VL_UNCOVERABLE(m_parallel && m_offload)) {  // LCOV_EXCL_START
+        VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use parallel tracing with offloading");
+    }  // LCOV_EXCL_STOP
+
+    // Configure format-specific sub class
+    configure(*(configp.get()));
+}
+
+template <>
+void VerilatedTrace::addCallbackRecord(std::vector& cbVec,
+                                                           CallbackRecord&& cbRec)
+    VL_MT_SAFE_EXCLUDES(m_mutex) {
+    const VerilatedLockGuard lock{m_mutex};
+    cbVec.push_back(cbRec);
+}
+
+template <>
+void VerilatedTrace::addInitCb(initCb_t cb, void* userp,
+                                                   const std::string& name, bool isLibInstance,
+                                                   uint32_t nTraceCodes) VL_MT_SAFE {
+    addCallbackRecord(m_initCbs, CallbackRecord{cb, userp, isLibInstance, name, nTraceCodes});
+}
+template <>
+void VerilatedTrace::addConstCb(dumpCb_t cb, uint32_t fidx,
+                                                    void* userp) VL_MT_SAFE {
+    addCallbackRecord(m_constCbs, CallbackRecord{cb, fidx, userp});
+}
+template <>
+void VerilatedTrace::addConstCb(dumpOffloadCb_t cb, uint32_t fidx,
+                                                    void* userp) VL_MT_SAFE {
+    addCallbackRecord(m_constOffloadCbs, CallbackRecord{cb, fidx, userp});
+}
+template <>
+void VerilatedTrace::addFullCb(dumpCb_t cb, uint32_t fidx,
+                                                   void* userp) VL_MT_SAFE {
+    addCallbackRecord(m_fullCbs, CallbackRecord{cb, fidx, userp});
+}
+template <>
+void VerilatedTrace::addFullCb(dumpOffloadCb_t cb, uint32_t fidx,
+                                                   void* userp) VL_MT_SAFE {
+    addCallbackRecord(m_fullOffloadCbs, CallbackRecord{cb, fidx, userp});
+}
+template <>
+void VerilatedTrace::addChgCb(dumpCb_t cb, uint32_t fidx,
+                                                  void* userp) VL_MT_SAFE {
+    addCallbackRecord(m_chgCbs, CallbackRecord{cb, fidx, userp});
+}
+template <>
+void VerilatedTrace::addChgCb(dumpOffloadCb_t cb, uint32_t fidx,
+                                                  void* userp) VL_MT_SAFE {
+    addCallbackRecord(m_chgOffloadCbs, CallbackRecord{cb, fidx, userp});
+}
+template <>
+void VerilatedTrace::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE {
+    addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
+}
+
+template <>
+void VerilatedTrace::initLib(const std::string& name) VL_MT_SAFE {
+    // Note it's possible the instance doesn't exist if the lib was compiled without tracing
+    void* const prevInitUserp = m_initUserp;
+    for (const CallbackRecord& cbr : m_initCbs) {
+        if (cbr.m_name != name) continue;
+        const uint32_t baseCode = nextCode();
+        m_nextCode += cbr.m_nTraceCodes;
+        m_initUserp = cbr.m_userp;
+        cbr.m_initCb(cbr.m_userp, self(), baseCode);
+        m_initUserp = prevInitUserp;
+    }
+}
+
+//=========================================================================
+// Primitives converting binary values to strings...
+
+// All of these take a destination pointer where the string will be emitted,
+// and a value to convert. There are a couple of variants for efficiency.
+
+static inline void cvtCDataToStr(char* dstp, CData value) {
+#ifdef VL_HAVE_SSE2
+    // Similar to cvtSDataToStr but only the bottom 8 byte lanes are used
+    const __m128i a = _mm_cvtsi32_si128(value);
+    const __m128i b = _mm_unpacklo_epi8(a, a);
+    const __m128i c = _mm_shufflelo_epi16(b, 0);
+    const __m128i m = _mm_set1_epi64x(0x0102040810204080);
+    const __m128i d = _mm_cmpeq_epi8(_mm_and_si128(c, m), m);
+    const __m128i result = _mm_sub_epi8(_mm_set1_epi8('0'), d);
+    _mm_storel_epi64(reinterpret_cast<__m128i*>(dstp), result);
+#else
+    dstp[0] = '0' | static_cast((value >> 7) & 1);
+    dstp[1] = '0' | static_cast((value >> 6) & 1);
+    dstp[2] = '0' | static_cast((value >> 5) & 1);
+    dstp[3] = '0' | static_cast((value >> 4) & 1);
+    dstp[4] = '0' | static_cast((value >> 3) & 1);
+    dstp[5] = '0' | static_cast((value >> 2) & 1);
+    dstp[6] = '0' | static_cast((value >> 1) & 1);
+    dstp[7] = '0' | static_cast(value & 1);
+#endif
+}
+
+static inline void cvtSDataToStr(char* dstp, SData value) {
+#ifdef VL_HAVE_SSE2
+    // We want each bit in the 16-bit input value to end up in a byte lane
+    // within the 128-bit XMM register. Note that x86 is little-endian and we
+    // want the MSB of the input at the low address, so we will bit-reverse
+    // at the same time.
+
+    // Put value in bottom of 128-bit register a[15:0] = value
+    const __m128i a = _mm_cvtsi32_si128(value);
+    // Interleave bytes with themselves
+    // b[15: 0] = {2{a[ 7:0]}} == {2{value[ 7:0]}}
+    // b[31:16] = {2{a[15:8]}} == {2{value[15:8]}}
+    const __m128i b = _mm_unpacklo_epi8(a, a);
+    // Shuffle bottom 64 bits, note swapping high bytes with low bytes
+    // c[31: 0] = {2{b[31:16]}} == {4{value[15:8}}
+    // c[63:32] = {2{b[15: 0]}} == {4{value[ 7:0}}
+    const __m128i c = _mm_shufflelo_epi16(b, 0x05);
+    // Shuffle whole register
+    // d[ 63: 0] = {2{c[31: 0]}} == {8{value[15:8}}
+    // d[126:54] = {2{c[63:32]}} == {8{value[ 7:0}}
+    const __m128i d = _mm_shuffle_epi32(c, 0x50);
+    // Test each bit within the bytes, this sets each byte lane to 0
+    // if the bit for that lane is 0 and to 0xff if the bit is 1.
+    const __m128i m = _mm_set1_epi64x(0x0102040810204080);
+    const __m128i e = _mm_cmpeq_epi8(_mm_and_si128(d, m), m);
+    // Convert to ASCII by subtracting the masks from ASCII '0':
+    // '0' - 0 is '0', '0' - -1 is '1'
+    const __m128i result = _mm_sub_epi8(_mm_set1_epi8('0'), e);
+    // Store the 16 characters to the un-aligned buffer
+    _mm_storeu_si128(reinterpret_cast<__m128i*>(dstp), result);
+#else
+    cvtCDataToStr(dstp, value >> 8);
+    cvtCDataToStr(dstp + 8, value);
+#endif
+}
+
+static inline void cvtIDataToStr(char* dstp, IData value) {
+#ifdef VL_HAVE_AVX2
+    // Similar to cvtSDataToStr but the bottom 16-bits are processed in the
+    // top half of the YMM registers
+    const __m256i a = _mm256_insert_epi32(_mm256_undefined_si256(), value, 0);
+    const __m256i b = _mm256_permute4x64_epi64(a, 0);
+    const __m256i s = _mm256_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
+                                      2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3);
+    const __m256i c = _mm256_shuffle_epi8(b, s);
+    const __m256i m = _mm256_set1_epi64x(0x0102040810204080);
+    const __m256i d = _mm256_cmpeq_epi8(_mm256_and_si256(c, m), m);
+    const __m256i result = _mm256_sub_epi8(_mm256_set1_epi8('0'), d);
+    _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp), result);
+#else
+    cvtSDataToStr(dstp, value >> 16);
+    cvtSDataToStr(dstp + 16, value);
+#endif
+}
+
+static inline void cvtQDataToStr(char* dstp, QData value) {
+    cvtIDataToStr(dstp, value >> 32);
+    cvtIDataToStr(dstp + 32, value);
+}
+
+#define cvtEDataToStr cvtIDataToStr
+
+//=========================================================================
+// VerilatedTraceBuffer
+
+template <>
+VerilatedTraceBuffer::VerilatedTraceBuffer(Trace& owner)
+    : VL_BUF_T{owner}
+    , m_sigs_oldvalp{owner.m_sigs_oldvalp}
+    , m_sigs_enabledp{owner.m_sigs_enabledp} {}
+
+// These functions must write the new value back into the old value store,
+// and subsequently call the format-specific emit* implementations. Note
+// that this file must be included in the format-specific implementation, so
+// the emit* functions can be inlined for performance.
+
+template <>
+void VerilatedTraceBuffer::fullBit(uint32_t* oldp, CData newval) {
+    const uint32_t code = oldp - m_sigs_oldvalp;
+    *oldp = newval;  // Still copy even if not tracing so chg doesn't call full
+    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
+    emitBit(code, newval);
+}
+
+template <>
+void VerilatedTraceBuffer::fullEvent(uint32_t* oldp, const VlEventBase* newvalp) {
+    const uint32_t code = oldp - m_sigs_oldvalp;
+    // No need to update *oldp
+    if (newvalp->isTriggered()) emitEvent(code);
+}
+
+template <>
+void VerilatedTraceBuffer::fullEventTriggered(uint32_t* oldp) {
+    const uint32_t code = oldp - m_sigs_oldvalp;
+    // No need to update *oldp
+    emitEvent(code);
+}
+
+template <>
+void VerilatedTraceBuffer::fullCData(uint32_t* oldp, CData newval, int bits) {
+    const uint32_t code = oldp - m_sigs_oldvalp;
+    *oldp = newval;  // Still copy even if not tracing so chg doesn't call full
+    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
+    emitCData(code, newval, bits);
+}
+
+template <>
+void VerilatedTraceBuffer::fullSData(uint32_t* oldp, SData newval, int bits) {
+    const uint32_t code = oldp - m_sigs_oldvalp;
+    *oldp = newval;  // Still copy even if not tracing so chg doesn't call full
+    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
+    emitSData(code, newval, bits);
+}
+
+template <>
+void VerilatedTraceBuffer::fullIData(uint32_t* oldp, IData newval, int bits) {
+    const uint32_t code = oldp - m_sigs_oldvalp;
+    *oldp = newval;  // Still copy even if not tracing so chg doesn't call full
+    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
+    emitIData(code, newval, bits);
+}
+
+template <>
+void VerilatedTraceBuffer::fullQData(uint32_t* oldp, QData newval, int bits) {
+    const uint32_t code = oldp - m_sigs_oldvalp;
+    std::memcpy(oldp, &newval, sizeof(newval));
+    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
+    emitQData(code, newval, bits);
+}
+
+template <>
+void VerilatedTraceBuffer::fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
+    const uint32_t code = oldp - m_sigs_oldvalp;
+    for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
+    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
+    emitWData(code, newvalp, bits);
+}
+
+template <>
+void VerilatedTraceBuffer::fullDouble(uint32_t* oldp, double newval) {
+    const uint32_t code = oldp - m_sigs_oldvalp;
+    std::memcpy(oldp, &newval, sizeof(newval));
+    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
+    // cppcheck-suppress invalidPointerCast
+    emitDouble(code, newval);
+}
+
+//=========================================================================
+// VerilatedTraceOffloadBuffer
+
+template <>
+VerilatedTraceOffloadBuffer::VerilatedTraceOffloadBuffer(VL_SUB_T& owner)
+    : VerilatedTraceBuffer{owner}
+    , m_offloadBufferWritep{owner.m_offloadBufferWritep}
+    , m_offloadBufferEndp{owner.m_offloadBufferEndp} {
+    if (m_offloadBufferWritep) {
+        using This = VerilatedTraceBuffer*;
+        // Tack on the buffer address
+        static_assert(2 * sizeof(uint32_t) >= sizeof(This),
+                      "This should be enough on all platforms");
+        *m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
+        *reinterpret_cast(m_offloadBufferWritep) = static_cast(this);
+        m_offloadBufferWritep += 2;
+    }
+}
+
+#endif  // VL_CPPCHECK
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_types.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_types.h
new file mode 100644
index 00000000000..da8c94977a0
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_types.h
@@ -0,0 +1,2139 @@
+// -*- mode: C++; c-file-style: "cc-mode" -*-
+//*************************************************************************
+//
+// Code available from: https://verilator.org
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of either the GNU Lesser General Public License Version 3
+// or the Perl Artistic License Version 2.0.
+// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
+// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+//
+//*************************************************************************
+///
+/// \file
+/// \brief Verilated common data type containers
+///
+/// verilated.h should be included instead of this file.
+///
+/// Those macro/function/variable starting or ending in _ are internal,
+/// however many of the other function/macros here are also internal.
+///
+//*************************************************************************
+
+#ifndef VERILATOR_VERILATED_TYPES_H_
+#define VERILATOR_VERILATED_TYPES_H_
+
+#ifndef VERILATOR_VERILATED_H_INTERNAL_
+#error "verilated_types.h should only be included by verilated.h"
+#endif
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+class VlProcess;
+template 
+class VlUnpacked;
+
+//=========================================================================
+// Debug functions
+
+#ifdef VL_DEBUG
+/// Evaluate statement if VL_DEBUG defined
+#define VL_DEBUG_IFDEF(stmt) \
+    do { stmt } while (false)
+/// Evaluate statement if VL_DEBUG defined and Verilated::debug() enabled
+#define VL_DEBUG_IF(stmt) \
+    do { \
+        if (VL_UNLIKELY(Verilated::debug())) { stmt } \
+    } while (false)
+#else
+// We intentionally do not compile the stmt to improve compile speed
+#define VL_DEBUG_IFDEF(stmt) \
+    do { \
+    } while (false)
+#define VL_DEBUG_IF(stmt) \
+    do { \
+    } while (false)
+#endif
+
+//===================================================================
+// String formatters (required by below containers)
+
+extern std::string VL_TO_STRING(CData lhs);
+extern std::string VL_TO_STRING(SData lhs);
+extern std::string VL_TO_STRING(IData lhs);
+extern std::string VL_TO_STRING(QData lhs);
+extern std::string VL_TO_STRING(double lhs);
+inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
+extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
+
+//=========================================================================
+// Declare net data types
+
+#define VL_SIG8(name, msb, lsb) CData name  ///< Declare signal, 1-8 bits
+#define VL_SIG16(name, msb, lsb) SData name  ///< Declare signal, 9-16 bits
+#define VL_SIG64(name, msb, lsb) QData name  ///< Declare signal, 33-64 bits
+#define VL_SIG(name, msb, lsb) IData name  ///< Declare signal, 17-32 bits
+#define VL_SIGW(name, msb, lsb, words) VlWide name  ///< Declare signal, 65+ bits
+#define VL_IN8(name, msb, lsb) CData name  ///< Declare input signal, 1-8 bits
+#define VL_IN16(name, msb, lsb) SData name  ///< Declare input signal, 9-16 bits
+#define VL_IN64(name, msb, lsb) QData name  ///< Declare input signal, 33-64 bits
+#define VL_IN(name, msb, lsb) IData name  ///< Declare input signal, 17-32 bits
+#define VL_INW(name, msb, lsb, words) VlWide name  ///< Declare input signal, 65+ bits
+#define VL_INOUT8(name, msb, lsb) CData name  ///< Declare bidir signal, 1-8 bits
+#define VL_INOUT16(name, msb, lsb) SData name  ///< Declare bidir signal, 9-16 bits
+#define VL_INOUT64(name, msb, lsb) QData name  ///< Declare bidir signal, 33-64 bits
+#define VL_INOUT(name, msb, lsb) IData name  ///< Declare bidir signal, 17-32 bits
+#define VL_INOUTW(name, msb, lsb, words) VlWide name  ///< Declare bidir signal, 65+ bits
+#define VL_OUT8(name, msb, lsb) CData name  ///< Declare output signal, 1-8 bits
+#define VL_OUT16(name, msb, lsb) SData name  ///< Declare output signal, 9-16 bits
+#define VL_OUT64(name, msb, lsb) QData name  ///< Declare output signal, 33-64 bits
+#define VL_OUT(name, msb, lsb) IData name  ///< Declare output signal, 17-32 bits
+#define VL_OUTW(name, msb, lsb, words) VlWide name  ///< Declare output signal, 65+ bits
+
+//===================================================================
+// Functions needed here
+
+constexpr IData VL_CLOG2_CE_Q(QData lhs) VL_PURE {
+    // constexpr usage only! Recuses to meet C++11 constexpr func limitations
+    return lhs <= 1 ? 0 : VL_CLOG2_CE_Q((lhs + 1) >> 1ULL) + 1;
+}
+
+// Metadata of processes
+using VlProcessRef = std::shared_ptr;
+
+class VlProcess final {
+    // MEMBERS
+    int m_state;  // Current state of the process
+    VlProcessRef m_parentp = nullptr;  // Parent process, if exists
+    std::set m_children;  // Active child processes
+
+public:
+    // TYPES
+    enum : int {  // Type int for compatibility with $c
+        FINISHED = 0,
+        RUNNING = 1,
+        WAITING = 2,
+        SUSPENDED = 3,
+        KILLED = 4,
+    };
+
+    // CONSTRUCTORS
+    // Construct independent process
+    VlProcess()
+        : m_state{RUNNING} {}
+    // Construct child process of parent
+    explicit VlProcess(VlProcessRef parentp)
+        : m_state{RUNNING}
+        , m_parentp{parentp} {
+        m_parentp->attach(this);
+    }
+
+    ~VlProcess() {
+        if (m_parentp) m_parentp->detach(this);
+    }
+
+    void attach(VlProcess* childp) { m_children.insert(childp); }
+    void detach(VlProcess* childp) { m_children.erase(childp); }
+
+    int state() const { return m_state; }
+    void state(int s) { m_state = s; }
+    void disable() {
+        state(KILLED);
+        disableFork();
+    }
+    void disableFork() {
+        for (VlProcess* childp : m_children) childp->disable();
+    }
+    bool completed() const { return state() == FINISHED || state() == KILLED; }
+    bool completedFork() const {
+        for (const VlProcess* const childp : m_children)
+            if (!childp->completed()) return false;
+        return true;
+    }
+
+    std::string randstate() const VL_MT_UNSAFE;
+    void randstate(const std::string& state) VL_MT_UNSAFE;
+};
+
+inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
+
+//===================================================================
+// SystemVerilog event type
+
+class VlEventBase VL_NOT_FINAL {
+public:
+    virtual ~VlEventBase() = default;
+
+    virtual void fire() = 0;
+    virtual bool isFired() const = 0;
+    virtual bool isTriggered() const = 0;
+    virtual void clearFired() = 0;
+    virtual void clearTriggered() = 0;
+};
+
+class VlEvent final : public VlEventBase {
+    // MEMBERS
+    bool m_fired = false;  // Fired on this scheduling iteration
+    bool m_triggered = false;  // Triggered state of event persisting until next time step
+
+public:
+    // CONSTRUCTOR
+    VlEvent() = default;
+    ~VlEvent() override = default;
+
+    friend std::string VL_TO_STRING(const VlEvent& e);
+    friend class VlAssignableEvent;
+    // METHODS
+    void fire() override { m_fired = m_triggered = true; }
+    bool isFired() const override { return m_fired; }
+    bool isTriggered() const override { return m_triggered; }
+    void clearFired() override { m_fired = false; }
+    void clearTriggered() override { m_triggered = false; }
+};
+
+class VlAssignableEvent final : public std::shared_ptr, public VlEventBase {
+public:
+    // Constructor
+    VlAssignableEvent()
+        : std::shared_ptr(new VlEvent) {}
+    ~VlAssignableEvent() override = default;
+
+    // METHODS
+    void fire() override { (*this)->m_fired = (*this)->m_triggered = true; }
+    bool isFired() const override { return (*this)->m_fired; }
+    bool isTriggered() const override { return (*this)->m_triggered; }
+    void clearFired() override { (*this)->m_fired = false; }
+    void clearTriggered() override { (*this)->m_triggered = false; }
+};
+
+inline std::string VL_TO_STRING(const VlEventBase& e);
+
+inline std::string VL_TO_STRING(const VlEvent& e) {
+    return "triggered="s + (e.isTriggered() ? "true" : "false");
+}
+
+inline std::string VL_TO_STRING(const VlAssignableEvent& e) {
+    return "&{ " + VL_TO_STRING(*e) + " }";
+}
+
+inline std::string VL_TO_STRING(const VlEventBase& e) {
+    if (const VlAssignableEvent& assignable = dynamic_cast(e)) {
+        return VL_TO_STRING(assignable);
+    }
+    return "triggered="s + (e.isTriggered() ? "true" : "false");
+}
+
+//===================================================================
+// Random
+
+// Random Number Generator with internal state
+class VlRNG final {
+    std::array m_state;
+
+public:
+    // The default constructor simply sets state, to avoid vl_rand64()
+    // having to check for construction at each call
+    // Alternative: seed with zero and check on rand64() call
+    VlRNG() VL_MT_SAFE;
+    explicit VlRNG(uint64_t seed) VL_PURE;
+    void srandom(uint64_t n) VL_MT_UNSAFE;
+    std::string get_randstate() const VL_MT_UNSAFE;
+    void set_randstate(const std::string& state) VL_MT_UNSAFE;
+    uint64_t rand64() VL_MT_UNSAFE;
+    // Threadsafe, but requires use on vl_thread_rng
+    static uint64_t vl_thread_rng_rand64() VL_MT_SAFE;
+    static VlRNG& vl_thread_rng() VL_MT_SAFE;
+};
+
+inline uint64_t vl_rand64() VL_MT_SAFE { return VlRNG::vl_thread_rng_rand64(); }
+
+// RNG for shuffle()
+class VlURNG final {
+public:
+    using result_type = size_t;
+    static constexpr size_t min() { return 0; }
+    static constexpr size_t max() { return 1ULL << 31; }
+    size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
+};
+
+template 
+class VlRandC final {
+    T_Value m_remaining = 0;  // Number of values to pull before re-randomize
+    T_Value m_lfsr = 1;  // LFSR state
+
+public:
+    // CONSTRUCTORS
+    VlRandC() {
+        static_assert(N_NumValues >= 1, "");
+        static_assert(sizeof(T_Value) == 8 || (N_NumValues < (1ULL << (8 * sizeof(T_Value)))), "");
+    }
+    // METHODS
+    T_Value randomize(VlRNG& rngr) {
+        if (VL_UNLIKELY(!m_remaining)) reseed(rngr);
+        // Polynomials are first listed at https://users.ece.cmu.edu/~koopman/lfsr/
+        static constexpr uint64_t s_polynomials[] = {
+            0x0ULL,  // 0 never used (constant, no randomization)
+            0x0ULL,  // 1
+            0x3ULL,        0x5ULL,       0x9ULL,        0x12ULL,       0x21ULL,
+            0x41ULL,       0x8eULL,      0x108ULL,      0x204ULL,      0x402ULL,
+            0x829ULL,      0x100dULL,    0x2015ULL,     0x4001ULL,
+            0x8016ULL,  // 16
+            0x10004ULL,    0x20040ULL,   0x40013ULL,    0x80004ULL,    0x100002ULL,
+            0x200001ULL,   0x400010ULL,  0x80000dULL,   0x1000004ULL,  0x2000023ULL,
+            0x4000013ULL,  0x8000004ULL, 0x10000002ULL, 0x20000029ULL, 0x40000004ULL,
+            0x80000057ULL,  // 32
+            0x100000029ULL  // 33
+        };
+        constexpr uint32_t clogWidth = VL_CLOG2_CE_Q(N_NumValues) + 1;
+        constexpr uint32_t lfsrWidth = (clogWidth < 2) ? 2 : clogWidth;
+        constexpr T_Value polynomial = static_cast(s_polynomials[lfsrWidth]);
+        // printf("  numV=%ld w=%d poly=%x\n", N_NumValues, lfsrWidth, polynomial);
+        //  Loop until get reasonable value. Because we picked a LFSR of at most one
+        //  extra bit in width, this will only require at most on average 1.5 loops
+        do {
+            m_lfsr = (m_lfsr & 1ULL) ? ((m_lfsr >> 1ULL) ^ polynomial) : (m_lfsr >> 1ULL);
+        } while (m_lfsr > N_NumValues);  // Note if == then output value 0
+        --m_remaining;
+        T_Value result = (m_lfsr == N_NumValues) ? 0 : m_lfsr;
+        // printf("    result=%x  (numv=%ld, rem=%d)\n", result, N_NumValues, m_remaining);
+        return result;
+    }
+    void reseed(VlRNG& rngr) {
+        constexpr uint32_t lfsrWidth = VL_CLOG2_CE_Q(N_NumValues) + 1;
+        m_remaining = N_NumValues;
+        do {
+            m_lfsr = rngr.rand64() & VL_MASK_Q(lfsrWidth);
+            // printf("    lfsr.reseed=%x\n", m_lfsr);
+        } while (!m_lfsr);  // 0 not a legal seed
+    }
+};
+
+// These require the class object to have the thread safety lock
+inline IData VL_RANDOM_RNG_I(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); }
+inline QData VL_RANDOM_RNG_Q(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); }
+extern double VL_RANDOM_RNG_D(VlRNG& rngr) VL_MT_UNSAFE;
+extern WDataOutP VL_RANDOM_RNG_W(VlRNG& rngr, int obits, WDataOutP outwp) VL_MT_UNSAFE;
+
+//===================================================================
+// Readmem/Writemem operation classes
+
+class VlReadMem final {
+    const bool m_hex;  // Hex format
+    const int m_bits;  // Bit width of values
+    const std::string& m_filename;  // Filename
+    const QData m_end;  // End address (as specified by user)
+    FILE* m_fp = nullptr;  // File handle for filename
+    QData m_addr = 0;  // Next address to read
+    int m_linenum = 0;  // Line number last read from file
+    bool m_anyAddr = false;  // Had address directive in the file
+public:
+    VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
+    ~VlReadMem();
+    bool isOpen() const { return m_fp != nullptr; }
+    int linenum() const { return m_linenum; }
+    bool get(QData& addrr, std::string& valuer);
+    void setData(void* valuep, const std::string& rhs);
+};
+
+class VlWriteMem final {
+    const bool m_hex;  // Hex format
+    const int m_bits;  // Bit width of values
+    FILE* m_fp = nullptr;  // File handle for filename
+    QData m_addr = 0;  // Next address to write
+public:
+    VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
+    ~VlWriteMem();
+    bool isOpen() const { return m_fp != nullptr; }
+    void print(QData addr, bool addrstamp, const void* valuep);
+};
+
+//===================================================================
+/// Verilog wide packed bit container.
+/// Similar to std::array, but lighter weight, only methods needed
+/// by Verilator, to help compile time.
+///
+/// A 'struct' as we want this to be an aggregate type that allows
+/// static aggregate initialization. Consider data members private.
+///
+/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
+/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
+/// zero in memory, but during intermediate operations in the Verilated
+/// internals is unpredictable.
+
+static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
+
+template 
+struct VlWide;
+
+// Type trait to check if a type is VlWide
+template 
+struct VlIsVlWide : public std::false_type {};
+
+template 
+struct VlIsVlWide> : public std::true_type {};
+
+template 
+struct VlWide final {
+    static constexpr size_t Words = N_Words;
+
+    // MEMBERS
+    // This should be the only data member, otherwise generated static initializers need updating
+    EData m_storage[N_Words];  // Contents of the packed array
+
+    // CONSTRUCTORS
+    // Default constructors and destructor are used. Note however that C++20 requires that
+    // aggregate types do not have a user declared constructor, not even an explicitly defaulted
+    // one.
+
+    // OPERATOR METHODS
+    // Default copy assignment operators are used.
+    operator WDataOutP() VL_PURE { return &m_storage[0]; }  // This also allows []
+    operator WDataInP() const VL_PURE { return &m_storage[0]; }  // This also allows []
+    bool operator!=(const VlWide& that) const VL_PURE {
+        for (size_t i = 0; i < N_Words; ++i) {
+            if (m_storage[i] != that.m_storage[i]) return true;
+        }
+        return false;
+    }
+
+    // METHODS
+    const EData& at(size_t index) const { return m_storage[index]; }
+    EData& at(size_t index) { return m_storage[index]; }
+    size_t size() const { return N_Words; }
+    WData* data() { return &m_storage[0]; }
+    const WData* data() const { return &m_storage[0]; }
+    bool operator<(const VlWide& rhs) const {
+        return _vl_cmp_w(N_Words, data(), rhs.data()) < 0;
+    }
+};
+
+// Convert a C array to std::array reference by pointer magic, without copy.
+// Data type (second argument) is so the function template can automatically generate.
+template 
+VlWide& VL_CVT_W_A(const WDataInP inp, const VlWide&) {
+    return *((VlWide*)inp);
+}
+
+template 
+std::string VL_TO_STRING(const VlWide& obj) {
+    return VL_TO_STRING_W(N_Words, obj.data());
+}
+
+//===================================================================
+// Verilog queue and dynamic array container
+// There are no multithreaded locks on this; the base variable must
+// be protected by other means
+//
+// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
+// For dynamic arrays it is always zero
+template 
+class VlQueue final {
+private:
+    // TYPES
+    using Deque = std::deque;
+
+public:
+    using const_iterator = typename Deque::const_iterator;
+    template 
+    using WithFuncReturnType = decltype(std::declval()(0, std::declval()));
+
+private:
+    // MEMBERS
+    Deque m_deque;  // State of the assoc array
+    T_Value m_defaultValue{};  // Default value
+
+public:
+    // CONSTRUCTORS
+    // cppcheck-suppress uninitMemberVar // m_defaultValue isn't defaulted, caller must
+    VlQueue() = default;
+    ~VlQueue() = default;
+    VlQueue(const VlQueue&) = default;
+    VlQueue(VlQueue&&) = default;
+    VlQueue& operator=(const VlQueue&) = default;
+    VlQueue& operator=(VlQueue&&) = default;
+    bool operator==(const VlQueue& rhs) const { return m_deque == rhs.m_deque; }
+    bool operator!=(const VlQueue& rhs) const { return m_deque != rhs.m_deque; }
+    bool operator<(const VlQueue& rhs) const {
+        for (int index = 0; index < m_deque.size(); ++index) {
+            if (m_deque[index] < rhs.m_deque[index]) return true;
+        }
+        return false;
+    }
+
+    // Standard copy constructor works. Verilog: assoca = assocb
+    // Also must allow conversion from a different N_MaxSize queue
+    template 
+    VlQueue operator=(const VlQueue& rhs) {
+        m_deque = rhs.privateDeque();
+        if (VL_UNLIKELY(N_MaxSize && N_MaxSize < m_deque.size())) m_deque.resize(N_MaxSize - 1);
+        return *this;
+    }
+
+    // Construct new object from _V_alue and/or _C_ontainer child objects
+    static VlQueue consV(const T_Value& lhs) {
+        VlQueue out;
+        out.push_back(lhs);
+        return out;
+    }
+    static VlQueue consVV(const T_Value& lhs, const T_Value& rhs) {
+        VlQueue out;
+        out.push_back(rhs);
+        out.push_back(lhs);
+        return out;
+    }
+    static VlQueue consCV(const VlQueue& lhs, const T_Value& rhs) {
+        VlQueue out = lhs;
+        out.push_front(rhs);
+        return out;
+    }
+    static VlQueue consVC(const T_Value& lhs, const VlQueue& rhs) {
+        VlQueue out = rhs;
+        out.push_back(lhs);
+        return out;
+    }
+    static VlQueue consCC(const VlQueue& lhs, const VlQueue& rhs) {
+        VlQueue out = rhs;
+        for (const auto& i : lhs.m_deque) out.push_back(i);
+        return out;
+    }
+
+    // METHODS
+    T_Value& atDefault() { return m_defaultValue; }
+    const T_Value& atDefault() const { return m_defaultValue; }
+    const Deque& privateDeque() const { return m_deque; }
+
+    // Size. Verilog: function int size(), or int num()
+    int size() const { return m_deque.size(); }
+    // Clear array. Verilog: function void delete([input index])
+    void clear() { m_deque.clear(); }
+    void erase(int32_t index) {
+        if (VL_LIKELY(index >= 0 && index < m_deque.size()))
+            m_deque.erase(m_deque.begin() + index);
+    }
+
+    // Dynamic array new[] becomes a renew()
+    void renew(size_t size) {
+        clear();
+        m_deque.resize(size, atDefault());
+    }
+    // Dynamic array new[]() becomes a renew_copy()
+    void renew_copy(size_t size, const VlQueue& rhs) {
+        if (size == 0) {
+            clear();
+        } else {
+            *this = rhs;
+            m_deque.resize(size, atDefault());
+        }
+    }
+    // Unpacked array new[]() becomes a renew_copy()
+    template 
+    void renew_copy(size_t size, const VlUnpacked& rhs);
+
+    void resize(size_t size) { m_deque.resize(size, atDefault()); }
+
+    // function void q.push_front(value)
+    void push_front(const T_Value& value) {
+        m_deque.push_front(value);
+        if (VL_UNLIKELY(N_MaxSize != 0 && m_deque.size() > N_MaxSize)) m_deque.pop_back();
+    }
+    // function void q.push_back(value)
+    void push_back(const T_Value& value) {
+        if (VL_LIKELY(N_MaxSize == 0 || m_deque.size() < N_MaxSize)) m_deque.push_back(value);
+    }
+    // function value_t q.pop_front();
+    T_Value pop_front() {
+        if (m_deque.empty()) return m_defaultValue;
+        T_Value v = m_deque.front();
+        m_deque.pop_front();
+        return v;
+    }
+    // function value_t q.pop_back();
+    T_Value pop_back() {
+        if (m_deque.empty()) return m_defaultValue;
+        T_Value v = m_deque.back();
+        m_deque.pop_back();
+        return v;
+    }
+
+    // Setting. Verilog: assoc[index] = v (should only be used by dynamic arrays)
+    T_Value& atWrite(int32_t index) {
+        // cppcheck-suppress variableScope
+        static thread_local T_Value t_throwAway;
+        // Needs to work for dynamic arrays, so does not use N_MaxSize
+        if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
+            t_throwAway = atDefault();
+            return t_throwAway;
+        }
+        return m_deque[index];
+    }
+    // Setting. Verilog: assoc[index] = v (should only be used by queues)
+    T_Value& atWriteAppend(int32_t index) {
+        // cppcheck-suppress variableScope
+        static thread_local T_Value t_throwAway;
+        if (index == m_deque.size()) push_back(atDefault());
+        if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
+            t_throwAway = atDefault();
+            return t_throwAway;
+        }
+        return m_deque[index];
+    }
+    // Accessing. Verilog: v = assoc[index]
+    const T_Value& at(int32_t index) const {
+        // Needs to work for dynamic arrays, so does not use N_MaxSize
+        if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
+            return atDefault();
+        } else {
+            return m_deque[index];
+        }
+    }
+    // Access with an index counted from end (e.g. q[$])
+    T_Value& atWriteAppendBack(int32_t index) { return atWriteAppend(m_deque.size() - 1 - index); }
+    const T_Value& atBack(int32_t index) const { return at(m_deque.size() - 1 - index); }
+
+    // function void q.insert(index, value);
+    void insert(int32_t index, const T_Value& value) {
+        if (VL_UNLIKELY(index < 0 || index > m_deque.size())) return;
+        m_deque.insert(m_deque.begin() + index, value);
+    }
+
+    // inside (set membership operator)
+    bool inside(const T_Value& value) const {
+        return std::find(m_deque.cbegin(), m_deque.cend(), value) != m_deque.cend();
+    }
+
+    // Return slice q[lsb:msb]
+    VlQueue slice(int32_t lsb, int32_t msb) const {
+        VlQueue out;
+        if (VL_UNLIKELY(lsb < 0)) lsb = 0;
+        if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
+        if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
+        for (int32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
+        return out;
+    }
+    VlQueue sliceFrontBack(int32_t lsb, int32_t msb) const {
+        return slice(lsb, m_deque.size() - 1 - msb);
+    }
+    VlQueue sliceBackBack(int32_t lsb, int32_t msb) const {
+        return slice(m_deque.size() - 1 - lsb, m_deque.size() - 1 - msb);
+    }
+
+    // For save/restore
+    const_iterator begin() const { return m_deque.begin(); }
+    const_iterator end() const { return m_deque.end(); }
+
+    // Methods
+    void sort() { std::sort(m_deque.begin(), m_deque.end()); }
+    template 
+    void sort(T_Func with_func) {
+        // with_func returns arbitrary type to use for the sort comparison
+        std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
+            // index number is meaningless with sort, as it changes
+            return with_func(0, a) < with_func(0, b);
+        });
+    }
+    void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
+    template 
+    void rsort(T_Func with_func) {
+        // with_func returns arbitrary type to use for the sort comparison
+        std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
+            // index number is meaningless with sort, as it changes
+            return with_func(0, a) < with_func(0, b);
+        });
+    }
+    void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
+    void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG{}); }
+    VlQueue unique() const {
+        VlQueue out;
+        std::set saw;
+        for (const auto& i : m_deque) {
+            const auto it = saw.find(i);
+            if (it == saw.end()) {
+                saw.insert(it, i);
+                out.push_back(i);
+            }
+        }
+        return out;
+    }
+    template 
+    VlQueue unique(T_Func with_func) const {
+        VlQueue out;
+        std::set saw;
+        for (const auto& i : m_deque) {
+            const auto i_mapped = with_func(0, i);
+            const auto it = saw.find(i_mapped);
+            if (it == saw.end()) {
+                saw.insert(it, i_mapped);
+                out.push_back(i);
+            }
+        }
+        return out;
+    }
+    VlQueue unique_index() const {
+        VlQueue out;
+        IData index = 0;
+        std::set saw;
+        for (const auto& i : m_deque) {
+            const auto it = saw.find(i);
+            if (it == saw.end()) {
+                saw.insert(it, i);
+                out.push_back(index);
+            }
+            ++index;
+        }
+        return out;
+    }
+    template 
+    VlQueue unique_index(T_Func with_func) const {
+        VlQueue out;
+        IData index = 0;
+        std::set saw;
+        for (const auto& i : m_deque) {
+            const auto i_mapped = with_func(index, i);
+            auto it = saw.find(i_mapped);
+            if (it == saw.end()) {
+                saw.insert(it, i_mapped);
+                out.push_back(index);
+            }
+            ++index;
+        }
+        return out;
+    }
+    template 
+    VlQueue find(T_Func with_func) const {
+        VlQueue out;
+        IData index = 0;
+        for (const auto& i : m_deque) {
+            if (with_func(index, i)) out.push_back(i);
+            ++index;
+        }
+        return out;
+    }
+    template 
+    VlQueue find_index(T_Func with_func) const {
+        VlQueue out;
+        IData index = 0;
+        for (const auto& i : m_deque) {
+            if (with_func(index, i)) out.push_back(index);
+            ++index;
+        }
+        return out;
+    }
+    template 
+    VlQueue find_first(T_Func with_func) const {
+        // Can't use std::find_if as need index number
+        IData index = 0;
+        for (const auto& i : m_deque) {
+            if (with_func(index, i)) return VlQueue::consV(i);
+            ++index;
+        }
+        return VlQueue{};
+    }
+    template 
+    VlQueue find_first_index(T_Func with_func) const {
+        IData index = 0;
+        for (const auto& i : m_deque) {
+            if (with_func(index, i)) return VlQueue::consV(index);
+            ++index;
+        }
+        return VlQueue{};
+    }
+    template 
+    VlQueue find_last(T_Func with_func) const {
+        IData index = m_deque.size() - 1;
+        for (auto& item : vlstd::reverse_view(m_deque)) {
+            if (with_func(index, item)) return VlQueue::consV(item);
+            --index;
+        }
+        return VlQueue{};
+    }
+    template 
+    VlQueue find_last_index(T_Func with_func) const {
+        IData index = m_deque.size() - 1;
+        for (auto& item : vlstd::reverse_view(m_deque)) {
+            if (with_func(index, item)) return VlQueue::consV(index);
+            --index;
+        }
+        return VlQueue{};
+    }
+
+    // Reduction operators
+    VlQueue min() const {
+        if (m_deque.empty()) return VlQueue{};
+        const auto it = std::min_element(m_deque.cbegin(), m_deque.cend());
+        return VlQueue::consV(*it);
+    }
+    template 
+    VlQueue min(T_Func with_func) const {
+        if (m_deque.empty()) return VlQueue{};
+        const auto it = std::min_element(m_deque.cbegin(), m_deque.cend(),
+                                         [&with_func](const IData& a, const IData& b) {
+                                             return with_func(0, a) < with_func(0, b);
+                                         });
+        return VlQueue::consV(*it);
+    }
+    VlQueue max() const {
+        if (m_deque.empty()) return VlQueue{};
+        const auto it = std::max_element(m_deque.cbegin(), m_deque.cend());
+        return VlQueue::consV(*it);
+    }
+    template 
+    VlQueue max(T_Func with_func) const {
+        if (m_deque.empty()) return VlQueue{};
+        const auto it = std::max_element(m_deque.cbegin(), m_deque.cend(),
+                                         [&with_func](const IData& a, const IData& b) {
+                                             return with_func(0, a) < with_func(0, b);
+                                         });
+        return VlQueue::consV(*it);
+    }
+
+    T_Value r_sum() const {
+        T_Value out(0);  // Type must have assignment operator
+        for (const auto& i : m_deque) out += i;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_sum(T_Func with_func) const {
+        WithFuncReturnType out = WithFuncReturnType(0);
+        IData index = 0;
+        for (const auto& i : m_deque) out += with_func(index++, i);
+        return out;
+    }
+    T_Value r_product() const {
+        if (m_deque.empty()) return T_Value(0);  // The big three do it this way
+        T_Value out = T_Value(1);
+        for (const auto& i : m_deque) out *= i;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_product(T_Func with_func) const {
+        if (m_deque.empty()) return WithFuncReturnType(0);  // The big three do it this way
+        WithFuncReturnType out = WithFuncReturnType(1);
+        IData index = 0;
+        for (const auto& i : m_deque) out *= with_func(index++, i);
+        return out;
+    }
+    T_Value r_and() const {
+        if (m_deque.empty()) return T_Value(0);  // The big three do it this way
+        T_Value out = ~T_Value(0);
+        for (const auto& i : m_deque) out &= i;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_and(T_Func with_func) const {
+        if (m_deque.empty()) return WithFuncReturnType(0);  // The big three do it this way
+        IData index = 0;
+        WithFuncReturnType out = ~WithFuncReturnType(0);
+        for (const auto& i : m_deque) out &= with_func(index++, i);
+        return out;
+    }
+    T_Value r_or() const {
+        T_Value out = T_Value(0);
+        for (const auto& i : m_deque) out |= i;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_or(T_Func with_func) const {
+        WithFuncReturnType out = WithFuncReturnType(0);
+        IData index = 0;
+        for (const auto& i : m_deque) out |= with_func(index++, i);
+        return out;
+    }
+    T_Value r_xor() const {
+#ifdef VERILATOR_BIG3_NULLARY_ARITHMETICS_QUIRKS
+        if (m_deque.empty()) return T_Value(0);
+#endif
+        T_Value out = T_Value(0);
+        for (const auto& i : m_deque) out ^= i;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_xor(T_Func with_func) const {
+        WithFuncReturnType out = WithFuncReturnType(0);
+        IData index = 0;
+        for (const auto& i : m_deque) out ^= with_func(index++, i);
+        return out;
+    }
+
+    // Dumping. Verilog: str = $sformatf("%p", assoc)
+    std::string to_string() const {
+        if (m_deque.empty()) return "'{}";  // No trailing space
+        std::string out = "'{";
+        std::string comma;
+        for (const auto& i : m_deque) {
+            out += comma + VL_TO_STRING(i);
+            comma = ", ";
+        }
+        return out + "}";
+    }
+};
+
+template 
+std::string VL_TO_STRING(const VlQueue& obj) {
+    return obj.to_string();
+}
+
+template 
+struct VlContainsCustomStruct> : VlContainsCustomStruct {};
+
+//===================================================================
+// Verilog associative array container
+// There are no multithreaded locks on this; the base variable must
+// be protected by other means
+//
+template 
+class VlAssocArray final {
+private:
+    // TYPES
+    using Map = std::map;
+
+public:
+    using const_iterator = typename Map::const_iterator;
+    template 
+    using WithFuncReturnType
+        = decltype(std::declval()(std::declval(), std::declval()));
+
+private:
+    // MEMBERS
+    Map m_map;  // State of the assoc array
+    T_Value m_defaultValue;  // Default value
+
+public:
+    // CONSTRUCTORS
+    // m_defaultValue isn't defaulted. Caller's constructor must do it.
+    VlAssocArray() = default;
+    ~VlAssocArray() = default;
+    VlAssocArray(const VlAssocArray&) = default;
+    VlAssocArray(VlAssocArray&&) = default;
+    VlAssocArray& operator=(const VlAssocArray&) = default;
+    VlAssocArray& operator=(VlAssocArray&&) = default;
+    bool operator==(const VlAssocArray& rhs) const { return m_map == rhs.m_map; }
+    bool operator!=(const VlAssocArray& rhs) const { return m_map != rhs.m_map; }
+    bool operator<(const VlAssocArray& rhs) const { return m_map < rhs.m_map; }
+    // METHODS
+    T_Value& atDefault() { return m_defaultValue; }
+    const T_Value& atDefault() const { return m_defaultValue; }
+
+    // Size of array. Verilog: function int size(), or int num()
+    int size() const { return m_map.size(); }
+    bool empty() const { return m_map.empty(); }
+    // Clear array. Verilog: function void delete([input index])
+    void clear() { m_map.clear(); }
+    void erase(const T_Key& index) { m_map.erase(index); }
+    // Return 0/1 if element exists. Verilog: function int exists(input index)
+    int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
+    // Return first element.  Verilog: function int first(ref index);
+    int first(T_Key& indexr) const {
+        const auto it = m_map.cbegin();
+        if (it == m_map.end()) return 0;
+        indexr = it->first;
+        return 1;
+    }
+    // Return last element.  Verilog: function int last(ref index)
+    int last(T_Key& indexr) const {
+        const auto it = m_map.crbegin();
+        if (it == m_map.crend()) return 0;
+        indexr = it->first;
+        return 1;
+    }
+    // Return next element. Verilog: function int next(ref index)
+    int next(T_Key& indexr) const {
+        auto it = m_map.find(indexr);
+        if (VL_UNLIKELY(it == m_map.end())) return 0;
+        ++it;
+        if (VL_UNLIKELY(it == m_map.end())) return 0;
+        indexr = it->first;
+        return 1;
+    }
+    // Return prev element. Verilog: function int prev(ref index)
+    int prev(T_Key& indexr) const {
+        auto it = m_map.find(indexr);
+        if (VL_UNLIKELY(it == m_map.end())) return 0;
+        if (VL_UNLIKELY(it == m_map.begin())) return 0;
+        --it;
+        indexr = it->first;
+        return 1;
+    }
+    // Setting. Verilog: assoc[index] = v
+    // Can't just overload operator[] or provide a "at" reference to set,
+    // because we need to be able to insert only when the value is set
+    T_Value& at(const T_Key& index) {
+        const auto it = m_map.find(index);
+        if (it == m_map.end()) {
+            std::pair pit = m_map.emplace(index, m_defaultValue);
+            return pit.first->second;
+        }
+        return it->second;
+    }
+    // Accessing. Verilog: v = assoc[index]
+    const T_Value& at(const T_Key& index) const {
+        const auto it = m_map.find(index);
+        if (it == m_map.end()) {
+            return m_defaultValue;
+        } else {
+            return it->second;
+        }
+    }
+    // Setting as a chained operation
+    VlAssocArray& set(const T_Key& index, const T_Value& value) {
+        at(index) = value;
+        return *this;
+    }
+    VlAssocArray& setDefault(const T_Value& value) {
+        atDefault() = value;
+        return *this;
+    }
+
+    // For save/restore
+    const_iterator begin() const { return m_map.begin(); }
+    const_iterator end() const { return m_map.end(); }
+
+    // Methods
+    VlQueue unique() const {
+        VlQueue out;
+        std::set saw;
+        for (const auto& i : m_map) {
+            auto it = saw.find(i.second);
+            if (it == saw.end()) {
+                saw.insert(it, i.second);
+                out.push_back(i.second);
+            }
+        }
+        return out;
+    }
+    template 
+    VlQueue unique(T_Func with_func) const {
+        VlQueue out;
+        T_Key default_key;
+        using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
+        std::set saw;
+        for (const auto& i : m_map) {
+            const auto i_mapped = with_func(default_key, i.second);
+            const auto it = saw.find(i_mapped);
+            if (it == saw.end()) {
+                saw.insert(it, i_mapped);
+                out.push_back(i.second);
+            }
+        }
+        return out;
+    }
+    VlQueue unique_index() const {
+        VlQueue out;
+        std::set saw;
+        for (const auto& i : m_map) {
+            auto it = saw.find(i.second);
+            if (it == saw.end()) {
+                saw.insert(it, i.second);
+                out.push_back(i.first);
+            }
+        }
+        return out;
+    }
+    template 
+    VlQueue unique_index(T_Func with_func) const {
+        VlQueue out;
+        using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
+        std::set saw;
+        for (const auto& i : m_map) {
+            const auto i_mapped = with_func(i.first, i.second);
+            auto it = saw.find(i_mapped);
+            if (it == saw.end()) {
+                saw.insert(it, i_mapped);
+                out.push_back(i.first);
+            }
+        }
+        return out;
+    }
+    template 
+    VlQueue find(T_Func with_func) const {
+        VlQueue out;
+        for (const auto& i : m_map)
+            if (with_func(i.first, i.second)) out.push_back(i.second);
+        return out;
+    }
+    template 
+    VlQueue find_index(T_Func with_func) const {
+        VlQueue out;
+        for (const auto& i : m_map)
+            if (with_func(i.first, i.second)) out.push_back(i.first);
+        return out;
+    }
+    template 
+    VlQueue find_first(T_Func with_func) const {
+        const auto it
+            = std::find_if(m_map.cbegin(), m_map.cend(), [=](const std::pair& i) {
+                  return with_func(i.first, i.second);
+              });
+        if (it == m_map.end()) return VlQueue{};
+        return VlQueue::consV(it->second);
+    }
+    template 
+    VlQueue find_first_index(T_Func with_func) const {
+        const auto it
+            = std::find_if(m_map.cbegin(), m_map.cend(), [=](const std::pair& i) {
+                  return with_func(i.first, i.second);
+              });
+        if (it == m_map.end()) return VlQueue{};
+        return VlQueue::consV(it->first);
+    }
+    template 
+    VlQueue find_last(T_Func with_func) const {
+        const auto it = std::find_if(
+            m_map.crbegin(), m_map.crend(),
+            [=](const std::pair& i) { return with_func(i.first, i.second); });
+        if (it == m_map.rend()) return VlQueue{};
+        return VlQueue::consV(it->second);
+    }
+    template 
+    VlQueue find_last_index(T_Func with_func) const {
+        const auto it = std::find_if(
+            m_map.crbegin(), m_map.crend(),
+            [=](const std::pair& i) { return with_func(i.first, i.second); });
+        if (it == m_map.rend()) return VlQueue{};
+        return VlQueue::consV(it->first);
+    }
+
+    // Reduction operators
+    VlQueue min() const {
+        if (m_map.empty()) return VlQueue();
+        const auto it = std::min_element(
+            m_map.cbegin(), m_map.cend(),
+            [](const std::pair& a, const std::pair& b) {
+                return a.second < b.second;
+            });
+        return VlQueue::consV(it->second);
+    }
+    template 
+    VlQueue min(T_Func with_func) const {
+        if (m_map.empty()) return VlQueue();
+        const auto it = std::min_element(
+            m_map.cbegin(), m_map.cend(),
+            [&with_func](const std::pair& a, const std::pair& b) {
+                return with_func(a.first, a.second) < with_func(b.first, b.second);
+            });
+        return VlQueue::consV(it->second);
+    }
+    VlQueue max() const {
+        if (m_map.empty()) return VlQueue();
+        const auto it = std::max_element(
+            m_map.cbegin(), m_map.cend(),
+            [](const std::pair& a, const std::pair& b) {
+                return a.second < b.second;
+            });
+        return VlQueue::consV(it->second);
+    }
+    template 
+    VlQueue max(T_Func with_func) const {
+        if (m_map.empty()) return VlQueue();
+        const auto it = std::max_element(
+            m_map.cbegin(), m_map.cend(),
+            [&with_func](const std::pair& a, const std::pair& b) {
+                return with_func(a.first, a.second) < with_func(b.first, b.second);
+            });
+        return VlQueue::consV(it->second);
+    }
+
+    T_Value r_sum() const {
+        T_Value out(0);  // Type must have assignment operator
+        for (const auto& i : m_map) out += i.second;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_sum(T_Func with_func) const {
+        WithFuncReturnType out = WithFuncReturnType(0);
+        for (const auto& i : m_map) out += with_func(i.first, i.second);
+        return out;
+    }
+    T_Value r_product() const {
+        if (m_map.empty()) return T_Value(0);  // The big three do it this way
+        T_Value out = T_Value(1);
+        for (const auto& i : m_map) out *= i.second;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_product(T_Func with_func) const {
+        if (m_map.empty()) return WithFuncReturnType(0);  // The big three do it this way
+        WithFuncReturnType out = WithFuncReturnType(1);
+        for (const auto& i : m_map) out *= with_func(i.first, i.second);
+        return out;
+    }
+    T_Value r_and() const {
+        if (m_map.empty()) return T_Value(0);  // The big three do it this way
+        T_Value out = ~T_Value(0);
+        for (const auto& i : m_map) out &= i.second;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_and(T_Func with_func) const {
+        if (m_map.empty()) return WithFuncReturnType(0);  // The big three do it this way
+        WithFuncReturnType out = ~WithFuncReturnType(0);
+        for (const auto& i : m_map) out &= with_func(i.first, i.second);
+        return out;
+    }
+    T_Value r_or() const {
+        T_Value out = T_Value(0);
+        for (const auto& i : m_map) out |= i.second;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_or(T_Func with_func) const {
+        WithFuncReturnType out = WithFuncReturnType(0);
+        for (const auto& i : m_map) out |= with_func(i.first, i.second);
+        return out;
+    }
+    T_Value r_xor() const {
+        T_Value out = T_Value(0);
+        for (const auto& i : m_map) out ^= i.second;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_xor(T_Func with_func) const {
+        WithFuncReturnType out = WithFuncReturnType(0);
+        for (const auto& i : m_map) out ^= with_func(i.first, i.second);
+        return out;
+    }
+
+    // Dumping. Verilog: str = $sformatf("%p", assoc)
+    std::string to_string() const {
+        if (m_map.empty()) return "'{}";  // No trailing space
+        std::string out = "'{";
+        std::string comma;
+        for (const auto& i : m_map) {
+            out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
+            comma = ", ";
+        }
+        // Default not printed - maybe random init data
+        return out + "}";
+    }
+};
+
+template 
+std::string VL_TO_STRING(const VlAssocArray& obj) {
+    return obj.to_string();
+}
+
+template 
+struct VlContainsCustomStruct> : VlContainsCustomStruct {};
+
+template 
+void VL_READMEM_N(bool hex, int bits, const std::string& filename,
+                  VlAssocArray& obj, QData start, QData end) VL_MT_SAFE {
+    VlReadMem rmem{hex, bits, filename, start, end};
+    if (VL_UNLIKELY(!rmem.isOpen())) return;
+    while (true) {
+        QData addr;
+        std::string data;
+        if (rmem.get(addr /*ref*/, data /*ref*/)) {
+            rmem.setData(&(obj.at(addr)), data);
+        } else {
+            break;
+        }
+    }
+}
+
+template 
+void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
+                   const VlAssocArray& obj, QData start, QData end) VL_MT_SAFE {
+    VlWriteMem wmem{hex, bits, filename, start, end};
+    if (VL_UNLIKELY(!wmem.isOpen())) return;
+    for (const auto& i : obj) {
+        const QData addr = i.first;
+        if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
+    }
+}
+
+//===================================================================
+/// Verilog unpacked array container
+/// For when a standard C++[] array is not sufficient, e.g. an
+/// array under a queue, or methods operating on the array.
+///
+/// A 'struct' as we want this to be an aggregate type that allows
+/// static aggregate initialization. Consider data members private.
+///
+/// This class may get exposed to a Verilated Model's top I/O, if the top
+/// IO has an unpacked array.
+
+template 
+class VlUnpacked final {
+    // TYPES
+    using T_Key = IData;  // Index type, for uniformity with other containers
+    using Unpacked = T_Value[N_Depth];
+
+public:
+    template 
+    using WithFuncReturnType = decltype(std::declval()(0, std::declval()));
+
+    // MEMBERS
+    // This should be the only data member, otherwise generated static initializers need updating
+    Unpacked m_storage;  // Contents of the unpacked array
+
+    // CONSTRUCTORS
+    // Default constructors and destructor are used. Note however that C++20 requires that
+    // aggregate types do not have a user declared constructor, not even an explicitly defaulted
+    // one.
+
+    // OPERATOR METHODS
+    // Default copy assignment operators are used.
+
+    // METHODS
+public:
+    // Raw access
+    WData* data() { return &m_storage[0]; }
+    const WData* data() const { return &m_storage[0]; }
+
+    constexpr std::size_t size() const { return N_Depth; }
+
+    void fill(const T_Value& value) {
+        std::fill(std::begin(m_storage), std::end(m_storage), value);
+    }
+
+    // To fit C++14
+    template 
+    int find_length(int dimension, std::false_type) const {
+        return size();
+    }
+
+    template 
+    int find_length(int dimension, std::true_type) const {
+        if (dimension == N_CurrentDimension) {
+            return size();
+        } else {
+            return m_storage[0].template find_length(dimension);
+        }
+    }
+
+    template 
+    int find_length(int dimension) const {
+        return find_length(dimension, std::is_class{});
+    }
+
+    template 
+    auto& find_element(const std::vector& indices, std::false_type) {
+        return m_storage[indices[N_CurrentDimension]];
+    }
+
+    template 
+    auto& find_element(const std::vector& indices, std::true_type) {
+        return m_storage[indices[N_CurrentDimension]]
+            .template find_element(indices);
+    }
+
+    template 
+    auto& find_element(const std::vector& indices) {
+        return find_element(indices, std::is_class{});
+    }
+
+    T_Value& operator[](size_t index) { return m_storage[index]; }
+    constexpr const T_Value& operator[](size_t index) const { return m_storage[index]; }
+
+    // *this != that, which might be used for change detection/trigger computation, but avoid
+    // operator overloading in VlUnpacked for safety in other contexts.
+    bool neq(const VlUnpacked& that) const { return neq(*this, that); }
+    // Similar to 'neq' above, *this = that used for change detection
+    void assign(const VlUnpacked& that) { *this = that; }
+    bool operator==(const VlUnpacked& that) const { return !neq(that); }
+    bool operator!=(const VlUnpacked& that) const { return neq(that); }
+    // interface to C style arrays (used in ports), see issue #5125
+    bool neq(const T_Value that[N_Depth]) const { return neq(*this, that); }
+    void assign(const T_Value that[N_Depth]) { std::copy_n(that, N_Depth, m_storage); }
+    void operator=(const T_Value that[N_Depth]) { assign(that); }
+    bool operator<(const VlUnpacked& that) const {
+        for (int index = 0; index < N_Depth; ++index) {
+            if (m_storage[index] < that.m_storage[index]) return true;
+        }
+        return false;
+    }
+
+    // inside (set membership operator)
+    bool inside(const T_Value& value) const {
+        return std::find(std::begin(m_storage), std::end(m_storage), value) != std::end(m_storage);
+    }
+
+    void sort() { std::sort(std::begin(m_storage), std::end(m_storage)); }
+    template 
+    void sort(T_Func with_func) {
+        // with_func returns arbitrary type to use for the sort comparison
+        std::sort(std::begin(m_storage), std::end(m_storage),
+                  [=](const T_Value& a, const T_Value& b) {
+                      // index number is meaningless with sort, as it changes
+                      return with_func(0, a) < with_func(0, b);
+                  });
+    }
+    // std::rbegin/std::rend not available until C++14
+    void rsort() {
+        std::sort(std::begin(m_storage), std::end(m_storage), std::greater());
+    }
+    template 
+    void rsort(T_Func with_func) {
+        // with_func returns arbitrary type to use for the sort comparison
+        // std::rbegin/std::rend not available until C++14, so using > below
+        std::sort(std::begin(m_storage), std::end(m_storage),
+                  [=](const T_Value& a, const T_Value& b) {
+                      // index number is meaningless with sort, as it changes
+                      return with_func(0, a) > with_func(0, b);
+                  });
+    }
+    void reverse() { std::reverse(std::begin(m_storage), std::end(m_storage)); }
+    void shuffle() { std::shuffle(std::begin(m_storage), std::end(m_storage), VlURNG{}); }
+    VlQueue unique() const {
+        VlQueue out;
+        std::set saw;
+        for (const auto& i : m_storage) {
+            const auto it = saw.find(i);
+            if (it == saw.end()) {
+                saw.insert(it, i);
+                out.push_back(i);
+            }
+        }
+        return out;
+    }
+    template 
+    VlQueue unique(T_Func with_func) const {
+        VlQueue out;
+        std::set saw;
+        for (const auto& i : m_storage) {
+            const auto i_mapped = with_func(0, i);
+            const auto it = saw.find(i_mapped);
+            if (it == saw.end()) {
+                saw.insert(it, i_mapped);
+                out.push_back(i);
+            }
+        }
+        return out;
+    }
+    VlQueue unique_index() const {
+        VlQueue out;
+        IData index = 0;
+        std::set saw;
+        for (const auto& i : m_storage) {
+            const auto it = saw.find(i);
+            if (it == saw.end()) {
+                saw.insert(it, i);
+                out.push_back(index);
+            }
+            ++index;
+        }
+        return out;
+    }
+    template 
+    VlQueue unique_index(T_Func with_func) const {
+        VlQueue out;
+        IData index = 0;
+        std::set saw;
+        for (const auto& i : m_storage) {
+            const auto i_mapped = with_func(index, i);
+            auto it = saw.find(i_mapped);
+            if (it == saw.end()) {
+                saw.insert(it, i_mapped);
+                out.push_back(index);
+            }
+            ++index;
+        }
+        return out;
+    }
+    template 
+    VlQueue find(T_Func with_func) const {
+        VlQueue out;
+        IData index = 0;
+        for (const auto& i : m_storage) {
+            if (with_func(index, i)) out.push_back(i);
+            ++index;
+        }
+        return out;
+    }
+    template 
+    VlQueue find_index(T_Func with_func) const {
+        VlQueue out;
+        IData index = 0;
+        for (const auto& i : m_storage) {
+            if (with_func(index, i)) out.push_back(index);
+            ++index;
+        }
+        return out;
+    }
+    template 
+    VlQueue find_first(T_Func with_func) const {
+        // Can't use std::find_if as need index number
+        IData index = 0;
+        for (const auto& i : m_storage) {
+            if (with_func(index, i)) return VlQueue::consV(i);
+            ++index;
+        }
+        return VlQueue{};
+    }
+    template 
+    VlQueue find_first_index(T_Func with_func) const {
+        IData index = 0;
+        for (const auto& i : m_storage) {
+            if (with_func(index, i)) return VlQueue::consV(index);
+            ++index;
+        }
+        return VlQueue{};
+    }
+    template 
+    VlQueue find_last(T_Func with_func) const {
+        for (int i = N_Depth - 1; i >= 0; i--) {
+            if (with_func(i, m_storage[i])) return VlQueue::consV(m_storage[i]);
+        }
+        return VlQueue{};
+    }
+    template 
+    VlQueue find_last_index(T_Func with_func) const {
+        for (int i = N_Depth - 1; i >= 0; i--) {
+            if (with_func(i, m_storage[i])) return VlQueue::consV(i);
+        }
+        return VlQueue{};
+    }
+
+    // Reduction operators
+    VlQueue min() const {
+        const auto it = std::min_element(std::begin(m_storage), std::end(m_storage));
+        return VlQueue::consV(*it);
+    }
+    template 
+    VlQueue min(T_Func with_func) const {
+        const auto it = std::min_element(std::begin(m_storage), std::end(m_storage),
+                                         [&with_func](const IData& a, const IData& b) {
+                                             return with_func(0, a) < with_func(0, b);
+                                         });
+        return VlQueue::consV(*it);
+    }
+    VlQueue max() const {
+        const auto it = std::max_element(std::begin(m_storage), std::end(m_storage));
+        return VlQueue::consV(*it);
+    }
+    template 
+    VlQueue max(T_Func with_func) const {
+        const auto it = std::max_element(std::begin(m_storage), std::end(m_storage),
+                                         [&with_func](const IData& a, const IData& b) {
+                                             return with_func(0, a) < with_func(0, b);
+                                         });
+        return VlQueue::consV(*it);
+    }
+
+    T_Value r_sum() const {
+        T_Value out(0);  // Type must have assignment operator
+        for (const auto& i : m_storage) out += i;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_sum(T_Func with_func) const {
+        WithFuncReturnType out
+            = WithFuncReturnType(0);  // Type must have assignment operator
+        for (const auto& i : m_storage) out += with_func(0, i);
+        return out;
+    }
+    T_Value r_product() const {
+        T_Value out = T_Value(1);
+        for (const auto& i : m_storage) out *= i;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_product(T_Func with_func) const {
+        WithFuncReturnType out = WithFuncReturnType(1);
+        for (const auto& i : m_storage) out *= with_func(0, i);
+        return out;
+    }
+    T_Value r_and() const {
+        if (m_storage.empty()) return T_Value(0);  // The big three do it this way
+        T_Value out = ~T_Value(0);
+        for (const auto& i : m_storage) out &= i;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_and(T_Func with_func) const {
+        WithFuncReturnType out = ~WithFuncReturnType(0);
+        for (const auto& i : m_storage) out &= with_func(0, i);
+        return out;
+    }
+    T_Value r_or() const {
+        T_Value out = T_Value(0);
+        for (const auto& i : m_storage) out |= i;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_or(T_Func with_func) const {
+        WithFuncReturnType out = WithFuncReturnType(0);
+        for (const auto& i : m_storage) out |= with_func(0, i);
+        return out;
+    }
+    T_Value r_xor() const {
+        T_Value out = T_Value(0);
+        for (const auto& i : m_storage) out ^= i;
+        return out;
+    }
+    template 
+    WithFuncReturnType r_xor(T_Func with_func) const {
+        WithFuncReturnType out = WithFuncReturnType(0);
+        for (const auto& i : m_storage) out ^= with_func(0, i);
+        return out;
+    }
+
+    // Dumping. Verilog: str = $sformatf("%p", assoc)
+    std::string to_string() const {
+        std::string out = "'{";
+        std::string comma;
+        for (int i = 0; i < N_Depth; ++i) {
+            out += comma + VL_TO_STRING(m_storage[i]);
+            comma = ", ";
+        }
+        return out + "}";
+    }
+
+private:
+    template 
+    static bool neq(const VlUnpacked& a, const VlUnpacked& b) {
+        for (size_t i = 0; i < N_Dep; ++i) {
+            // Recursive 'neq', in case T_Val is also a VlUnpacked<_, _>
+            if (neq(a.m_storage[i], b.m_storage[i])) return true;
+        }
+        return false;
+    }
+
+    template 
+    static bool neq(const VlUnpacked& a, const T_Val b[N_Dep]) {
+        for (size_t i = 0; i < N_Dep; ++i) {
+            // Recursive 'neq', in case T_Val is also a VlUnpacked<_, _>
+            if (neq(a.m_storage[i], b[i])) return true;
+        }
+        return false;
+    }
+
+    template   //
+    static bool neq(const T_Other& a, const T_Other& b) {
+        // Base case (T_Other is not VlUnpacked<_, _>), fall back on !=
+        return a != b;
+    }
+};
+// Trait to detect VlUnpacked types
+template 
+struct IsVlUnpacked : std::false_type {};
+template 
+struct IsVlUnpacked> : std::true_type {};
+
+template 
+std::string VL_TO_STRING(const VlUnpacked& obj) {
+    return obj.to_string();
+}
+
+template 
+struct VlContainsCustomStruct> : VlContainsCustomStruct {};
+
+template 
+template 
+void VlQueue::renew_copy(
+    size_t size, const VlUnpacked& rhs) {
+    clear();
+    if (size == 0) return;
+    m_deque.resize(size, atDefault());
+    for (size_t i = 0; i < std::min(size, N_UnpackedDepth); ++i) { m_deque[i] = rhs.m_storage[i]; }
+}
+
+//===================================================================
+// Helper to apply the given indices to a target expression
+
+template 
+struct VlApplyIndices final {
+    VL_ATTR_ALWINLINE
+    static auto& apply(T_Target& target, const size_t* indicesp) {
+        return VlApplyIndices::apply(
+            target[indicesp[N_Curr]], indicesp);
+    }
+};
+
+template 
+struct VlApplyIndices final {
+    VL_ATTR_ALWINLINE
+    static T_Target& apply(T_Target& target, const size_t*) { return target; }
+};
+
+//===================================================================
+// Commit queue for NBAs - currently only for unpacked arrays
+//
+// This data-structure is used to handle non-blocking assignments
+// that might execute a variable number of times in a single
+// evaluation. It has 2 operations:
+// - 'enqueue' will add an update to the queue
+// - 'commit' will apply all enqueued updates to the target variable,
+//   in the order they were enqueued. This ensures the last NBA
+//   takes effect as it is expected.
+// There are 2 specializations of this class below:
+// - A version when a partial element update is not required,
+//   e.g, to handle:
+//      logic [31:0] array[N];
+//      for (int i = 0 ; i < N ; ++i) array[i] <= x;
+//   Here 'enqueue' takes the RHS ('x'), and the array indices ('i')
+//   as arguments.
+// - A different version when a partial element update is required,
+//   e.g. for:
+//      logic [31:0] array[N];
+//      for (int i = 0 ; i < N ; ++i) array[i][3:1] <= y;
+//   Here 'enqueue' takes one additional argument, which is a bitmask
+//   derived from the bit selects (_[3:1]), which masks the bits that
+//   need to be updated, and additionally the RHS is widened to a full
+//   element size, with the bits inserted into the masked region.
+template 
+class VlNBACommitQueue;
+
+// Specialization for whole element updates only
+template 
+class VlNBACommitQueue final {
+    // TYPES
+    struct Entry final {
+        T_Element value;
+        size_t indices[N_Rank];
+    };
+
+    // STATE
+    std::vector m_pending;  // Pending updates, in program order
+
+public:
+    // CONSTRUCTOR
+    VlNBACommitQueue() = default;
+    VL_UNCOPYABLE(VlNBACommitQueue);
+
+    // METHODS
+    template 
+    void enqueue(const T_Element& value, T_Args... indices) {
+        m_pending.emplace_back(Entry{value, {indices...}});
+    }
+
+    // Note: T_Commit might be different from T_Target. Specifically, when the signal is a
+    // top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
+    template 
+    void commit(T_Commit& target) {
+        if (m_pending.empty()) return;
+        for (const Entry& entry : m_pending) {
+            VlApplyIndices<0, N_Rank, T_Commit>::apply(target, entry.indices) = entry.value;
+        }
+        m_pending.clear();
+    }
+};
+
+// With partial element updates
+template 
+class VlNBACommitQueue final {
+    // TYPES
+    struct Entry final {
+        T_Element value;
+        T_Element mask;
+        size_t indices[N_Rank];
+    };
+
+    // STATE
+    std::vector m_pending;  // Pending updates, in program order
+
+    // STATIC METHODS
+
+    // Binary & | ~ for elements to use for masking in partial updates. Sorry for the templates.
+    template 
+    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type
+    bAnd(const T& a, const T& b) {
+        return a & b;
+    }
+
+    template 
+    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type
+    bAnd(const T& a, const T& b) {
+        T result;
+        for (size_t i = 0; i < T::Words; ++i) {
+            result.m_storage[i] = a.m_storage[i] & b.m_storage[i];
+        }
+        return result;
+    }
+
+    template 
+    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type
+    bOr(const T& a, const T& b) {
+        return a | b;
+    }
+
+    template 
+    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type  //
+    bOr(const T& a, const T& b) {
+        T result;
+        for (size_t i = 0; i < T::Words; ++i) {
+            result.m_storage[i] = a.m_storage[i] | b.m_storage[i];
+        }
+        return result;
+    }
+
+    template 
+    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type
+    bNot(const T& a) {
+        return ~a;
+    }
+
+    template 
+    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type
+    bNot(const T& a) {
+        T result;
+        for (size_t i = 0; i < T::Words; ++i) result.m_storage[i] = ~a.m_storage[i];
+        return result;
+    }
+
+public:
+    // CONSTRUCTOR
+    VlNBACommitQueue() = default;
+    VL_UNCOPYABLE(VlNBACommitQueue);
+
+    // METHODS
+    template 
+    void enqueue(const T_Element& value, const T_Element& mask, T_Args... indices) {
+        m_pending.emplace_back(Entry{value, mask, {indices...}});
+    }
+
+    // Note: T_Commit might be different from T_Target. Specifically, when the signal is a
+    // top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
+    template 
+    void commit(T_Commit& target) {
+        if (m_pending.empty()) return;
+        for (const Entry& entry : m_pending) {  //
+            auto& ref = VlApplyIndices<0, N_Rank, T_Commit>::apply(target, entry.indices);
+            // Maybe inefficient, but it works for now ...
+            const auto oldValue = ref;
+            ref = bOr(bAnd(entry.value, entry.mask), bAnd(oldValue, bNot(entry.mask)));
+        }
+        m_pending.clear();
+    }
+};
+
+//===================================================================
+// Object that VlDeleter is capable of deleting
+
+class VlDeletable VL_NOT_FINAL {
+public:
+    VlDeletable() = default;
+    virtual ~VlDeletable() = default;
+};
+
+//===================================================================
+// Class providing delayed deletion of garbage objects. Objects get deleted only when 'deleteAll()'
+// is called, or the deleter itself is destroyed.
+
+class VlDeleter final {
+    // MEMBERS
+    // Queue of new objects that should be deleted
+    std::vector m_newGarbage VL_GUARDED_BY(m_mutex);
+    // Queue of objects currently being deleted (only for deleteAll())
+    std::vector m_deleteNow VL_GUARDED_BY(m_deleteMutex);
+    mutable VerilatedMutex m_mutex;  // Mutex protecting the 'new garbage' queue
+    mutable VerilatedMutex m_deleteMutex;  // Mutex protecting the delete queue
+
+public:
+    // CONSTRUCTOR
+    VlDeleter() = default;
+    ~VlDeleter() { deleteAll(); }
+
+private:
+    VL_UNCOPYABLE(VlDeleter);
+
+public:
+    // METHODS
+    // Adds a new object to the 'new garbage' queue.
+    void put(VlDeletable* const objp) VL_MT_SAFE {
+        const VerilatedLockGuard lock{m_mutex};
+        m_newGarbage.push_back(objp);
+    }
+
+    // Deletes all queued garbage objects.
+    void deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE;
+};
+
+//===================================================================
+// Base class for all verilated classes. Includes a reference counter, and a pointer to the deleter
+// object that should destroy it after the counter reaches 0. This allows for easy construction of
+// VlClassRefs from 'this'.
+
+class VlClass VL_NOT_FINAL : public VlDeletable {
+    // TYPES
+    template 
+    friend class VlClassRef;  // Needed for access to the ref counter and deleter
+
+    // MEMBERS
+    std::atomic m_counter{1};  // Reference count for this object
+    VlDeleter* m_deleterp = nullptr;  // The deleter that will delete this object
+
+    // METHODS
+    // Atomically increments the reference counter
+    void refCountInc() VL_MT_SAFE {
+        VL_DEBUG_IFDEF(assert(m_counter););  // If zero, we might have already deleted
+        ++m_counter;
+    }
+    // Atomically decrements the reference counter. Assuming VlClassRef semantics are sound, it
+    // should never get called at m_counter == 0.
+    void refCountDec() VL_MT_SAFE {
+        if (!--m_counter) m_deleterp->put(this);
+    }
+
+public:
+    // CONSTRUCTORS
+    VlClass() {}
+    VlClass(const VlClass& copied) {}
+    ~VlClass() override = default;
+    // Polymorphic shallow clone. Overridden in each generated concrete class.
+    virtual VlClass* clone() const { return nullptr; }
+    // METHODS
+    virtual const char* typeName() const { return "VlClass"; }
+    virtual std::string to_string() const { return ""; }
+};
+
+//===================================================================
+// Represents the null pointer. Used for:
+// * setting VlClassRef to null instead of via nullptr_t, to prevent the implicit conversion of 0
+//   to nullptr,
+// * comparing interface pointers to null.
+
+struct VlNull final {
+    operator bool() const { return false; }
+    bool operator==(const void* ptr) const { return !ptr; }
+};
+inline bool operator==(const void* ptr, VlNull) { return !ptr; }
+
+//===================================================================
+// Verilog class reference container
+// There are no multithreaded locks on this; the base variable must
+// be protected by other means
+
+template 
+class VlClassRef final {
+private:
+    // TYPES
+    template 
+    friend class VlClassRef;  // Needed for template copy/move assignments
+
+    // MEMBERS
+    T_Class* m_objp = nullptr;  // Object pointed to
+
+    // METHODS
+    // Increase reference counter with null check
+    void refCountInc() const VL_MT_SAFE {
+        if (m_objp) m_objp->refCountInc();
+    }
+    // Decrease reference counter with null check
+    void refCountDec() const VL_MT_SAFE {
+        if (m_objp) m_objp->refCountDec();
+    }
+
+public:
+    // CONSTRUCTORS
+    VlClassRef() = default;
+    // Init with nullptr
+    // cppcheck-suppress noExplicitConstructor
+    VlClassRef(VlNull){};
+    template 
+    VlClassRef(VlDeleter& deleter, T_Args&&... args)
+        // () required here to avoid narrowing conversion warnings,
+        // when a new() has an e.g. CData type and passed a 1U.
+        : m_objp{new T_Class(std::forward(args)...)} {
+        // refCountInc was moved to the constructor of T_Class
+        // to fix self references in constructor.
+        m_objp->m_deleterp = &deleter;
+    }
+    // Explicit to avoid implicit conversion from 0
+    explicit VlClassRef(T_Class* objp)
+        : m_objp{objp} {
+        refCountInc();
+    }
+    // cppcheck-suppress noExplicitConstructor
+    VlClassRef(const VlClassRef& copied)
+        : m_objp{copied.m_objp} {
+        refCountInc();
+    }
+    // cppcheck-suppress noExplicitConstructor
+    VlClassRef(VlClassRef&& moved)
+        : m_objp{std::exchange(moved.m_objp, nullptr)} {}
+    // cppcheck-suppress noExplicitConstructor
+    template 
+    VlClassRef(const VlClassRef& copied)
+        : m_objp{copied.m_objp} {
+        refCountInc();
+    }
+    // cppcheck-suppress noExplicitConstructor
+    template 
+    VlClassRef(VlClassRef&& moved)
+        : m_objp{std::exchange(moved.m_objp, nullptr)} {}
+    ~VlClassRef() { refCountDec(); }
+
+    // METHODS
+    // Copy and move assignments
+    VlClassRef& operator=(const VlClassRef& copied) {
+        if (m_objp == copied.m_objp) return *this;
+        refCountDec();
+        m_objp = copied.m_objp;
+        refCountInc();
+        return *this;
+    }
+    VlClassRef& operator=(VlClassRef&& moved) {
+        if (m_objp == moved.m_objp) return *this;
+        refCountDec();
+        m_objp = std::exchange(moved.m_objp, nullptr);
+        return *this;
+    }
+    template 
+    VlClassRef& operator=(const VlClassRef& copied) {
+        if (m_objp == copied.m_objp) return *this;
+        refCountDec();
+        m_objp = copied.m_objp;
+        refCountInc();
+        return *this;
+    }
+    template 
+    VlClassRef& operator=(VlClassRef&& moved) {
+        if (m_objp == moved.m_objp) return *this;
+        refCountDec();
+        m_objp = std::exchange(moved.m_objp, nullptr);
+        return *this;
+    }
+    // Assign with nullptr
+    VlClassRef& operator=(VlNull) {
+        refCountDec();
+        m_objp = nullptr;
+        return *this;
+    }
+    // Dynamic caster
+    template 
+    VlClassRef dynamicCast() const {
+        return VlClassRef{dynamic_cast(m_objp)};
+    }
+    // Polymorphic shallow clone (IEEE 1800-2017 8.7: new  preserves runtime type)
+    VlClassRef clone(VlDeleter& deleter) const {
+        VlClass* clonedp = m_objp->clone();
+        if (VL_UNLIKELY(!clonedp)) return {};
+        clonedp->m_deleterp = &deleter;
+        VlClassRef result;
+        result.m_objp = dynamic_cast(clonedp);
+        return result;
+    }
+    // Dereference operators
+    T_Class& operator*() const { return *m_objp; }
+    T_Class* operator->() const { return m_objp; }
+    // For 'if (ptr)...'
+    operator bool() const { return m_objp; }
+    // In SV A == B iff both are handles to the same object (IEEE 1800-2023 8.4)
+    template 
+    bool operator==(const VlClassRef& rhs) const {
+        return m_objp == rhs.m_objp;
+    };
+    template 
+    bool operator!=(const VlClassRef& rhs) const {
+        return m_objp != rhs.m_objp;
+    };
+    template 
+    bool operator<(const VlClassRef& rhs) const {
+        return m_objp < rhs.m_objp;
+    };
+};
+
+template 
+static inline bool VL_CAST_DYNAMIC(VlClassRef in, VlClassRef& outr) {
+    if (!in) {
+        outr = VlNull{};
+        return true;
+    }
+    VlClassRef casted = in.template dynamicCast();
+    if (VL_LIKELY(casted)) {
+        outr = casted;
+        return true;
+    } else {
+        return false;
+    }
+}
+
+template 
+static inline bool VL_CAST_DYNAMIC(VlNull in, VlClassRef& outr) {
+    outr = VlNull{};
+    return true;
+}
+
+// For printing class references under a container, several choices:
+// 1. Dump recursively the pointed-to object.  Can be huge.  Might be circular.
+// 2. Print object type and pointer as C pointer.  Astable when rerun.
+// 3. Print object type and pointer as an incrementing number.  Needs num storage.
+// 4. Print object type alone.  Avoids above issues.
+template 
+inline std::string VL_TO_STRING(const VlClassRef& obj) {
+    return obj ? obj->typeName() : "null";
+}
+// Entry point for string conversion (called from not under a container);
+// dereference VlClassRef objects to print members
+template   // Default if no specialization
+inline std::string VL_TO_STRING_DEREF(T_Lhs obj) {
+    return VL_TO_STRING(obj);
+}
+template   // Specialization
+inline std::string VL_TO_STRING_DEREF(const VlClassRef& obj) {
+    return obj ? obj->to_string() : "null";
+}
+template   // Specialization
+inline std::string VL_TO_STRING_DEREF(VlClassRef& obj) {
+    return obj ? obj->to_string() : "null";
+}
+
+//=============================================================================
+// VlSampleQueue stores samples for input clockvars in clocking blocks. At a clocking event,
+// samples from this queue should be written to the correct input clockvar.
+
+template 
+class VlSampleQueue final {
+    // TYPES
+    // Type representing a single value sample at a point in time
+    struct VlSample final {
+        uint64_t m_timestamp;  // Timestamp at which the value was sampled
+        T_Sampled m_value;  // The sampled value
+    };
+
+    // MEMBERS
+    std::deque m_queue;  // Queue of samples with timestamps
+
+public:
+    // METHODS
+    // Push a new sample with the given timestamp to the end of the queue
+    void push(uint64_t time, const T_Sampled& value) { m_queue.push_back({time, value}); }
+    // Get the latest sample with its timestamp less than or equal to the given skew
+    void pop(uint64_t time, uint64_t skew, T_Sampled& value) {
+        if (time < skew) return;
+        // Find the last element not greater than (time - skew). Do a binary search, as the queue
+        // should be ordered.
+        auto it = std::lower_bound(m_queue.rbegin(), m_queue.rend(), VlSample{time - skew, {}},
+                                   [](const VlSample& sample, const VlSample& skewed) {
+                                       return sample.m_timestamp > skewed.m_timestamp;
+                                   });
+        if (it != m_queue.rend()) {
+            value = it->m_value;
+            m_queue.erase(m_queue.begin(), it.base());
+        }
+    }
+};
+
+//======================================================================
+
+#define VL_NEW(Class, ...) \
+    VlClassRef { vlSymsp->__Vm_deleter, __VA_ARGS__ }
+
+#define VL_KEEP_THIS \
+    VlClassRef::type> __Vthisref { this }
+
+template   // T typically of type VlClassRef
+inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
+    if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
+    return t;
+}
+
+//======================================================================
+
+#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.cpp
new file mode 100644
index 00000000000..7ceb04fc3cc
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.cpp
@@ -0,0 +1,685 @@
+// -*- mode: C++; c-file-style: "cc-mode" -*-
+//=============================================================================
+//
+// Code available from: https://verilator.org
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of either the GNU Lesser General Public License Version 3
+// or the Perl Artistic License Version 2.0.
+// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
+// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+//
+//=============================================================================
+///
+/// \file
+/// \brief Verilated C++ tracing in VCD format implementation code
+///
+/// This file must be compiled and linked against all Verilated objects
+/// that use --trace-vcd.
+///
+/// Use "verilator --trace-vcd" to add this to the Makefile for the linker.
+///
+//=============================================================================
+
+// clang-format off
+
+#include "verilatedos.h"
+#include "verilated.h"
+#include "verilated_vcd_c.h"
+
+#include 
+#include 
+#include 
+
+#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
+# include 
+#else
+# include 
+#endif
+
+#ifndef O_LARGEFILE  // WIN32 headers omit this
+# define O_LARGEFILE 0
+#endif
+#ifndef O_NONBLOCK  // WIN32 headers omit this
+# define O_NONBLOCK 0
+#endif
+#ifndef O_CLOEXEC  // WIN32 headers omit this
+# define O_CLOEXEC 0
+#endif
+
+// clang-format on
+
+// This size comes form VCD allowing use of printable ASCII characters between
+// '!' and '~' inclusive, which are a total of 94 different values. Encoding a
+// 32 bit code hence needs a maximum of std::ceil(log94(2**32-1)) == 5 bytes.
+constexpr unsigned VL_TRACE_MAX_VCD_CODE_SIZE = 5;  // Maximum length of a VCD string code
+
+// We use 8 bytes per code in a suffix buffer array.
+// 1 byte optional separator + VL_TRACE_MAX_VCD_CODE_SIZE bytes for code
+// + 1 byte '\n' + 1 byte suffix size. This luckily comes out to a power of 2,
+// meaning the array can be aligned such that entries never straddle multiple
+// cache-lines.
+constexpr unsigned VL_TRACE_SUFFIX_ENTRY_SIZE = 8;  // Size of a suffix entry
+
+//=============================================================================
+// Specialization of the generics for this trace format
+
+#define VL_SUB_T VerilatedVcd
+#define VL_BUF_T VerilatedVcdBuffer
+#include "verilated_trace_imp.h"
+#undef VL_SUB_T
+#undef VL_BUF_T
+
+//=============================================================================
+//=============================================================================
+//=============================================================================
+// VerilatedVcdFile
+
+bool VerilatedVcdFile::open(const std::string& name) VL_MT_UNSAFE {
+    m_fd = ::open(name.c_str(),
+                  O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE | O_NONBLOCK | O_CLOEXEC, 0666);
+    return m_fd >= 0;
+}
+
+void VerilatedVcdFile::close() VL_MT_UNSAFE { ::close(m_fd); }
+
+ssize_t VerilatedVcdFile::write(const char* bufp, ssize_t len) VL_MT_UNSAFE {
+    return ::write(m_fd, bufp, len);
+}
+
+//=============================================================================
+//=============================================================================
+//=============================================================================
+// Opening/Closing
+
+VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
+    // Not in header to avoid link issue if header is included without this .cpp file
+    m_fileNewed = (filep == nullptr);
+    m_filep = m_fileNewed ? new VerilatedVcdFile : filep;
+    m_wrChunkSize = 8 * 1024;
+    m_wrBufp = new char[m_wrChunkSize * 8];
+    m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
+    m_writep = m_wrBufp;
+    m_wrTimeBeginp = nullptr;
+    m_wrTimeEndp = nullptr;
+}
+
+void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
+    const VerilatedLockGuard lock{m_mutex};
+    if (isOpen()) return;
+
+    // Set member variables
+    m_filename = filename;  // "" is ok, as someone may overload open
+
+    openNextImp(m_rolloverSize != 0);
+    if (!isOpen()) return;
+
+    printStr("$version Generated by VerilatedVcd $end\n");
+    printStr("$timescale ");
+    printStr(timeResStr().c_str());  // lintok-begin-on-ref
+    printStr(" $end\n");
+
+    // Scope and signal definitions
+    assert(m_indent >= 0);
+    ++m_indent;
+    Super::traceInit();
+    --m_indent;
+    assert(m_indent >= 0);
+
+    printStr("$enddefinitions $end\n\n\n");
+
+    // When using rollover, the first chunk contains the header only.
+    if (m_rolloverSize) openNextImp(true);
+}
+
+void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
+    // Open next filename in concat sequence, mangle filename if
+    // incFilename is true.
+    const VerilatedLockGuard lock{m_mutex};
+    openNextImp(incFilename);
+}
+
+void VerilatedVcd::openNextImp(bool incFilename) {
+    closePrev();  // Close existing
+    if (incFilename) {
+        // Find _0000.{ext} in filename
+        std::string name = m_filename;
+        const size_t pos = name.rfind('.');
+        if (pos > 8 && 0 == std::strncmp("_cat", name.c_str() + pos - 8, 4)
+            && std::isdigit(name.c_str()[pos - 4]) && std::isdigit(name.c_str()[pos - 3])
+            && std::isdigit(name.c_str()[pos - 2]) && std::isdigit(name.c_str()[pos - 1])) {
+            // Increment code.
+            if ((++(name[pos - 1])) > '9') {
+                name[pos - 1] = '0';
+                if ((++(name[pos - 2])) > '9') {
+                    name[pos - 2] = '0';
+                    if ((++(name[pos - 3])) > '9') {
+                        name[pos - 3] = '0';
+                        if ((++(name[pos - 4])) > '9') {  //
+                            name[pos - 4] = '0';
+                        }
+                    }
+                }
+            }
+        } else {
+            // Append _cat0000
+            name.insert(pos, "_cat0000");
+        }
+        m_filename = name;
+    }
+    if (VL_UNCOVERABLE(m_filename[0] == '|')) {
+        assert(0);  // LCOV_EXCL_LINE // Not supported yet.
+    } else {
+        // cppcheck-suppress duplicateExpression
+        if (!m_filep->open(m_filename)) {
+            // User code can check isOpen()
+            m_isOpen = false;
+            return;
+        }
+    }
+    m_isOpen = true;
+    constDump(true);  // First dump must contain the const signals
+    fullDump(true);  // First dump must be full
+    m_wroteBytes = 0;
+}
+
+bool VerilatedVcd::preChangeDump() {
+    if (VL_UNLIKELY(m_rolloverSize && m_wroteBytes > m_rolloverSize)) openNextImp(true);
+    return isOpen();
+}
+
+void VerilatedVcd::emitTimeChange(uint64_t timeui) {
+    // Remember pointers when last emitted time stamp; if last output was
+    // timestamp backup and overwrite it.
+    // This is faster then checking on every signal change if time needs to
+    // be emitted.  Note buffer flushes may still emit a rare duplicate.
+    if (m_wrTimeBeginp && m_wrTimeEndp == m_writep) m_writep = m_wrTimeBeginp;
+    m_wrTimeBeginp = m_writep;
+    {
+        printStr("#");
+        const std::string str = std::to_string(timeui);
+        printStr(str.c_str());
+        printStr("\n");
+    }
+    m_wrTimeEndp = m_writep;
+}
+
+VerilatedVcd::~VerilatedVcd() {
+    close();
+    if (m_wrBufp) VL_DO_CLEAR(delete[] m_wrBufp, m_wrBufp = nullptr);
+    if (m_filep && m_fileNewed) VL_DO_CLEAR(delete m_filep, m_filep = nullptr);
+    if (parallel()) {
+        assert(m_numBuffers == m_freeBuffers.size());
+        for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
+    }
+}
+
+void VerilatedVcd::closePrev() {
+    // This function is on the flush() call path
+    if (!isOpen()) return;
+
+    Super::flushBase();
+    bufferFlush();
+    m_isOpen = false;
+    m_filep->close();
+}
+
+void VerilatedVcd::closeErr() {
+    // This function is on the flush() call path
+    // Close due to an error.  We might abort before even getting here,
+    // depending on the definition of vl_fatal.
+    if (!isOpen()) return;
+
+    // No buffer flush, just fclose
+    m_isOpen = false;
+    m_filep->close();  // May get error, just ignore it
+}
+
+void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
+    // This function is on the flush() call path
+    const VerilatedLockGuard lock{m_mutex};
+    if (!isOpen()) return;
+    closePrev();
+    // closePrev() called Super::flush(), so we just
+    // need to shut down the tracing thread here.
+    Super::closeBase();
+}
+
+void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
+    const VerilatedLockGuard lock{m_mutex};
+    Super::flushBase();
+    bufferFlush();
+}
+
+void VerilatedVcd::printStr(const char* str) {
+    // Not fast...
+    while (*str) {
+        *m_writep++ = *str++;
+        bufferCheck();
+    }
+}
+
+void VerilatedVcd::bufferResize(size_t minsize) {
+    // minsize is size of largest write.  We buffer at least 8 times as much data,
+    // writing when we are 3/4 full (with thus 2*minsize remaining free)
+    if (VL_UNLIKELY(minsize > m_wrChunkSize)) {
+        const char* oldbufp = m_wrBufp;
+        m_wrChunkSize = vlstd::roundUpToMultipleOf<1024>(minsize * 2);
+        m_wrBufp = new char[m_wrChunkSize * 8];
+        std::memcpy(m_wrBufp, oldbufp, m_writep - oldbufp);
+        m_writep = m_wrBufp + (m_writep - oldbufp);
+        if (m_wrTimeBeginp) {
+            m_wrTimeBeginp = m_wrBufp + (m_wrTimeBeginp - oldbufp);
+            m_wrTimeEndp = m_wrBufp + (m_wrTimeEndp - oldbufp);
+        }
+        m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
+        VL_DO_CLEAR(delete[] oldbufp, oldbufp = nullptr);
+    }
+}
+
+void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
+    // This function can be called from the trace offload thread
+    // This function is on the flush() call path
+    // We add output data to m_writep.
+    // When it gets nearly full we dump it using this routine which calls write()
+    // This is much faster than using buffered I/O
+    if (VL_UNLIKELY(!m_isOpen)) return;
+    const char* wp = m_wrBufp;
+    while (true) {
+        const ssize_t remaining = (m_writep - wp);
+        if (remaining == 0) break;
+        errno = 0;
+        const ssize_t got = m_filep->write(wp, remaining);
+        if (got > 0) {
+            wp += got;
+            m_wroteBytes += got;
+        } else if (VL_UNCOVERABLE(got < 0)) {
+            if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
+                // LCOV_EXCL_START
+                // write failed, presume error (perhaps out of disk space)
+                const std::string msg = "VerilatedVcd::bufferFlush: "s + std::strerror(errno);
+                VL_FATAL_MT("", 0, "", msg.c_str());
+                closeErr();
+                break;
+                // LCOV_EXCL_STOP
+            }
+        }
+    }
+
+    // Reset buffer
+    m_writep = m_wrBufp;
+    m_wrTimeBeginp = nullptr;
+    m_wrTimeEndp = nullptr;
+}
+
+//=============================================================================
+// Definitions
+
+void VerilatedVcd::printIndent(int level_change) {
+    if (level_change < 0) m_indent += level_change;
+    for (int i = 0; i < m_indent; ++i) printStr(" ");
+    if (level_change > 0) m_indent += level_change;
+}
+
+void VerilatedVcd::pushPrefix(const char* namep, VerilatedTracePrefixType type) {
+    assert(!m_prefixStack.empty());  // Constructor makes an empty entry
+    const std::string name{namep};
+    // An empty name means this is the root of a model created with
+    // name()=="".  The tools get upset if we try to pass this as empty, so
+    // we put the signals under a new $rootio scope, but the signals
+    // further down will be peers, not children (as usual for name()!="").
+    const std::string prevPrefix = m_prefixStack.back().first;
+    if (name == "$rootio" && !prevPrefix.empty()) {
+        // Upper has name, we can suppress inserting $rootio, but still push so popPrefix works
+        m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
+        return;
+    } else if (name.empty()) {
+        m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
+        return;
+    }
+
+    const std::string newPrefix = prevPrefix + name;
+    bool properScope = false;
+    switch (type) {
+    case VerilatedTracePrefixType::SCOPE_MODULE:
+    case VerilatedTracePrefixType::SCOPE_INTERFACE:
+    case VerilatedTracePrefixType::STRUCT_PACKED:
+    case VerilatedTracePrefixType::STRUCT_UNPACKED:
+    case VerilatedTracePrefixType::UNION_PACKED: {
+        properScope = true;
+        break;
+    }
+    default: break;
+    }
+    if (properScope) {
+        printIndent(1);
+        printStr("$scope module ");
+        const std::string n = lastWord(newPrefix);
+        printStr(n.c_str());
+        printStr(" $end\n");
+    }
+    m_prefixStack.emplace_back(newPrefix + (properScope ? " " : ""), type);
+}
+
+void VerilatedVcd::popPrefix() {
+    assert(!m_prefixStack.empty());
+    switch (m_prefixStack.back().second) {
+    case VerilatedTracePrefixType::SCOPE_MODULE:
+    case VerilatedTracePrefixType::SCOPE_INTERFACE:
+    case VerilatedTracePrefixType::STRUCT_PACKED:
+    case VerilatedTracePrefixType::STRUCT_UNPACKED:
+    case VerilatedTracePrefixType::UNION_PACKED:
+        printIndent(-1);
+        printStr("$upscope $end\n");
+        break;
+    default: break;
+    }
+    m_prefixStack.pop_back();
+    assert(!m_prefixStack.empty());  // Always one left, the constructor's initial one
+}
+
+void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, bool array,
+                           int arraynum, bool bussed, int msb, int lsb) {
+    const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
+
+    const std::string hierarchicalName = m_prefixStack.back().first + name;
+
+    const bool enabled = Super::declCode(code, hierarchicalName, bits);
+
+    if (m_suffixes.size() <= nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE) {
+        m_suffixes.resize(nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE * 2, 0);
+    }
+
+    // Keep upper bound on bytes a single signal can emit into the buffer
+    m_maxSignalBytes = std::max(m_maxSignalBytes, bits + 32);
+    // Make sure write buffer is large enough, plus header
+    bufferResize(m_maxSignalBytes + 1024);
+
+    if (!enabled) return;
+
+    // Create the VCD code and build the suffix array entry
+    char vcdCode[VL_TRACE_SUFFIX_ENTRY_SIZE];
+    {
+        // Render the VCD code
+        char* vcdCodeWritep = vcdCode;
+        uint32_t codeEnc = code;
+        do {
+            *vcdCodeWritep++ = static_cast('!' + codeEnc % 94);
+            codeEnc /= 94;
+        } while (codeEnc--);
+        *vcdCodeWritep = '\0';
+        const size_t vcdCodeLength = vcdCodeWritep - vcdCode;
+        assert(vcdCodeLength <= VL_TRACE_MAX_VCD_CODE_SIZE);
+        // Build suffix array entry
+        char* const entryBeginp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
+        entryBeginp[0] = ' ';  // Separator
+        // 1 bit values don't have a ' ' separator between value and string code
+        char* entryWritep = bits == 1 ? entryBeginp : entryBeginp + 1;
+        // Use memcpy as we know the size, and strcpy is flagged unsafe
+        std::memcpy(entryWritep, vcdCode, vcdCodeLength);
+        entryWritep += vcdCodeLength;
+        // Line terminator
+        *entryWritep++ = '\n';
+        // Set length of suffix (used to increment write pointer)
+        assert(entryWritep <= entryBeginp + VL_TRACE_SUFFIX_ENTRY_SIZE - 1);
+        entryBeginp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = static_cast(entryWritep - entryBeginp);
+    }
+
+    // Assemble the declaration
+    std::string decl = "$var ";
+    decl += wirep;
+    decl += ' ';
+    decl += std::to_string(bits);
+    decl += ' ';
+    decl += vcdCode;
+    decl += ' ';
+    decl += lastWord(hierarchicalName);
+    if (array) {
+        decl += '[';
+        decl += std::to_string(arraynum);
+        decl += ']';
+    }
+    if (bussed) {
+        decl += " [";
+        decl += std::to_string(msb);
+        decl += ':';
+        decl += std::to_string(lsb);
+        decl += ']';
+    }
+    decl += " $end\n";
+    printIndent(0);
+    printStr(decl.c_str());
+}
+
+void VerilatedVcd::declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                             VerilatedTraceSigDirection, VerilatedTraceSigKind,
+                             VerilatedTraceSigType, bool array, int arraynum) {
+    declare(code, name, "event", array, arraynum, false, 0, 0);
+}
+void VerilatedVcd::declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                           VerilatedTraceSigDirection, VerilatedTraceSigKind,
+                           VerilatedTraceSigType, bool array, int arraynum) {
+    declare(code, name, "wire", array, arraynum, false, 0, 0);
+}
+void VerilatedVcd::declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                           VerilatedTraceSigDirection, VerilatedTraceSigKind,
+                           VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
+    declare(code, name, "wire", array, arraynum, true, msb, lsb);
+}
+void VerilatedVcd::declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                            VerilatedTraceSigDirection, VerilatedTraceSigKind,
+                            VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
+    declare(code, name, "wire", array, arraynum, true, msb, lsb);
+}
+void VerilatedVcd::declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                             VerilatedTraceSigDirection, VerilatedTraceSigKind,
+                             VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
+    declare(code, name, "wire", array, arraynum, true, msb, lsb);
+}
+void VerilatedVcd::declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                              VerilatedTraceSigDirection, VerilatedTraceSigKind,
+                              VerilatedTraceSigType, bool array, int arraynum) {
+    declare(code, name, "real", array, arraynum, false, 63, 0);
+}
+
+//=============================================================================
+// Get/commit trace buffer
+
+VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer(uint32_t fidx) {
+    VerilatedVcd::Buffer* const bufp = new Buffer{*this};
+    if (parallel()) {
+        // Note: This is called from VerilatedVcd::dump, which already holds the lock
+        // If no buffer available, allocate a new one
+        if (m_freeBuffers.empty()) {
+            // cppcheck-suppress unreadVariable  // cppcheck bug, used below
+            constexpr size_t pageSize = 4096;
+            // 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
+            size_t startingSize = vlstd::roundUpToMultipleOf(4 * m_maxSignalBytes);
+            m_freeBuffers.emplace_back(new char[startingSize], startingSize);
+            ++m_numBuffers;
+        }
+        // Grab a buffer
+        const auto pair = m_freeBuffers.back();
+        m_freeBuffers.pop_back();
+        // Initialize
+        bufp->m_writep = bufp->m_bufp = pair.first;
+        bufp->m_size = pair.second;
+        bufp->adjustGrowp();
+    }
+    // Return the buffer
+    return bufp;
+}
+
+void VerilatedVcd::commitTraceBuffer(VerilatedVcd::Buffer* bufp) {
+    if (parallel()) {
+        // Note: This is called from VerilatedVcd::dump, which already holds the lock
+        // Resize output buffer. Note, we use the full size of the trace buffer, as
+        // this is a lot more stable than the actual occupancy of the trace buffer.
+        // This helps us to avoid re-allocations due to small size changes.
+        bufferResize(bufp->m_size);
+        // Compute occupancy of buffer
+        const size_t usedSize = bufp->m_writep - bufp->m_bufp;
+        // Copy to output buffer
+        std::memcpy(m_writep, bufp->m_bufp, usedSize);
+        // Adjust write pointer
+        m_writep += usedSize;
+        // Flush if necessary
+        bufferCheck();
+        // Put buffer back on free list
+        m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
+    } else {
+        // Needs adjusting for emitTimeChange
+        m_writep = bufp->m_writep;
+    }
+    delete bufp;
+}
+
+//=============================================================================
+// VerilatedVcdBuffer implementation
+
+//=============================================================================
+// Trace rendering primitives
+
+static void VerilatedVcdCCopyAndAppendNewLine(char* writep,
+                                              const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
+
+static void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
+    // Copy the whole suffix (this avoid having hard to predict branches which
+    // helps a lot). Note: The maximum length of the suffix is
+    // VL_TRACE_MAX_VCD_CODE_SIZE + 2 == 7, but we unroll this here for speed.
+#ifdef VL_X86_64
+    // Copy the whole 8 bytes in one go, this works on little-endian machines
+    // supporting unaligned stores.
+    *reinterpret_cast(writep) = *reinterpret_cast(suffixp);
+#else
+    // Portable variant
+    writep[0] = suffixp[0];
+    writep[1] = suffixp[1];
+    writep[2] = suffixp[2];
+    writep[3] = suffixp[3];
+    writep[4] = suffixp[4];
+    writep[5] = suffixp[5];
+    writep[6] = '\n';  // The 6th index is always '\n' if it's relevant, no need to fetch it.
+#endif
+}
+
+void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) {
+    const char* const suffixp = m_suffixes + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
+    VL_DEBUG_IFDEF(assert(suffixp[0]););
+    VerilatedVcdCCopyAndAppendNewLine(writep, suffixp);
+
+    // Now write back the write pointer incremented by the actual size of the
+    // suffix, which was stored in the last byte of the suffix buffer entry.
+    m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
+
+    if (m_owner.parallel()) {
+        // Double the size of the buffer if necessary
+        if (VL_UNLIKELY(m_writep >= m_growp)) {
+            // Compute occupied size of current buffer
+            const size_t usedSize = m_writep - m_bufp;
+            // We are always doubling the size
+            m_size *= 2;
+            // Allocate the new buffer
+            char* const newBufp = new char[m_size];
+            // Copy from current buffer to new buffer
+            std::memcpy(newBufp, m_bufp, usedSize);
+            // Delete current buffer
+            delete[] m_bufp;
+            // Make new buffer the current buffer
+            m_bufp = newBufp;
+            // Adjust write pointer
+            m_writep = m_bufp + usedSize;
+            // Adjust resize limit
+            adjustGrowp();
+        }
+    } else {
+        // Flush the write buffer if there's not enough space left for new information
+        // We only call this once per vector, so we need enough slop for a very wide "b###" line
+        if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
+            m_owner.m_writep = m_writep;
+            m_owner.bufferFlush();
+            m_writep = m_owner.m_writep;
+        }
+    }
+}
+
+//=============================================================================
+// emit* trace routines
+
+// Note: emit* are only ever called from one place (full* in
+// verilated_trace_imp.h, which is included in this file at the top),
+// so always inline them.
+
+VL_ATTR_ALWINLINE
+void VerilatedVcdBuffer::emitEvent(uint32_t code) {
+    // Don't prefetch suffix as it's a bit too late;
+    char* wp = m_writep;
+    *wp++ = '1';
+    finishLine(code, wp);
+}
+
+VL_ATTR_ALWINLINE
+void VerilatedVcdBuffer::emitBit(uint32_t code, CData newval) {
+    // Don't prefetch suffix as it's a bit too late;
+    char* wp = m_writep;
+    *wp++ = '0' | static_cast(newval);
+    finishLine(code, wp);
+}
+
+VL_ATTR_ALWINLINE
+void VerilatedVcdBuffer::emitCData(uint32_t code, CData newval, int bits) {
+    char* wp = m_writep;
+    *wp++ = 'b';
+    cvtCDataToStr(wp, newval << (VL_BYTESIZE - bits));
+    finishLine(code, wp + bits);
+}
+
+VL_ATTR_ALWINLINE
+void VerilatedVcdBuffer::emitSData(uint32_t code, SData newval, int bits) {
+    char* wp = m_writep;
+    *wp++ = 'b';
+    cvtSDataToStr(wp, newval << (VL_SHORTSIZE - bits));
+    finishLine(code, wp + bits);
+}
+
+VL_ATTR_ALWINLINE
+void VerilatedVcdBuffer::emitIData(uint32_t code, IData newval, int bits) {
+    char* wp = m_writep;
+    *wp++ = 'b';
+    cvtIDataToStr(wp, newval << (VL_IDATASIZE - bits));
+    finishLine(code, wp + bits);
+}
+
+VL_ATTR_ALWINLINE
+void VerilatedVcdBuffer::emitQData(uint32_t code, QData newval, int bits) {
+    char* wp = m_writep;
+    *wp++ = 'b';
+    cvtQDataToStr(wp, newval << (VL_QUADSIZE - bits));
+    finishLine(code, wp + bits);
+}
+
+VL_ATTR_ALWINLINE
+void VerilatedVcdBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) {
+    int words = VL_WORDS_I(bits);
+    char* wp = m_writep;
+    *wp++ = 'b';
+    // Handle the most significant word
+    const int bitsInMSW = VL_BITBIT_E(bits) ? VL_BITBIT_E(bits) : VL_EDATASIZE;
+    cvtEDataToStr(wp, newvalp[--words] << (VL_EDATASIZE - bitsInMSW));
+    wp += bitsInMSW;
+    // Handle the remaining words
+    while (words > 0) {
+        cvtEDataToStr(wp, newvalp[--words]);
+        wp += VL_EDATASIZE;
+    }
+    finishLine(code, wp);
+}
+
+VL_ATTR_ALWINLINE
+void VerilatedVcdBuffer::emitDouble(uint32_t code, double newval) {
+    char* wp = m_writep;
+    // Buffer can't overflow before VL_SNPRINTF; we sized during declaration
+    VL_SNPRINTF(wp, m_maxSignalBytes, "r%.16g", newval);
+    wp += std::strlen(wp);
+    finishLine(code, wp);
+}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.h
new file mode 100644
index 00000000000..3942055a105
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.h
@@ -0,0 +1,329 @@
+// -*- mode: C++; c-file-style: "cc-mode" -*-
+//=============================================================================
+//
+// Code available from: https://verilator.org
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of either the GNU Lesser General Public License Version 3
+// or the Perl Artistic License Version 2.0.
+// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
+// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+//
+//=============================================================================
+///
+/// \file
+/// \brief Verilated tracing in VCD format header
+///
+/// User wrapper code should use this header when creating VCD traces.
+///
+//=============================================================================
+
+#ifndef VERILATOR_VERILATED_VCD_C_H_
+#define VERILATOR_VERILATED_VCD_C_H_
+
+#include "verilated.h"
+#include "verilated_trace.h"
+
+#include 
+#include 
+
+class VerilatedVcdBuffer;
+class VerilatedVcdFile;
+
+//=============================================================================
+// VerilatedVcd
+// Base class to create a Verilator VCD dump
+// This is an internally used class - see VerilatedVcdC for what to call from applications
+
+class VerilatedVcd VL_NOT_FINAL : public VerilatedTrace {
+public:
+    using Super = VerilatedTrace;
+
+private:
+    friend VerilatedVcdBuffer;  // Give the buffer access to the private bits
+
+    //=========================================================================
+    // VCD-specific internals
+
+    VerilatedVcdFile* m_filep;  // File we're writing to
+    bool m_fileNewed;  // m_filep needs destruction
+    bool m_isOpen = false;  // True indicates open file
+    std::string m_filename;  // Filename we're writing to (if open)
+    uint64_t m_rolloverSize = 0;  // File size to rollover at
+    int m_indent = 0;  // Indentation depth
+
+    char* m_wrBufp;  // Output buffer
+    char* m_wrFlushp;  // Output buffer flush trigger location
+    char* m_writep;  // Write pointer into output buffer
+    char* m_wrTimeBeginp = nullptr;  // Write pointer for last time dump
+    char* m_wrTimeEndp = nullptr;  // Write pointer for last time dump
+    size_t m_wrChunkSize;  // Output buffer size
+    size_t m_maxSignalBytes = 0;  // Upper bound on number of bytes a single signal can generate
+    uint64_t m_wroteBytes = 0;  // Number of bytes written to this file
+
+    std::vector m_suffixes;  // VCD line end string codes + metadata
+
+    // Prefixes to add to signal names/scope types
+    std::vector> m_prefixStack{
+        {"", VerilatedTracePrefixType::SCOPE_MODULE}};
+
+    // Vector of free trace buffers as (pointer, size) pairs.
+    std::vector> m_freeBuffers;
+    size_t m_numBuffers = 0;  // Number of trace buffers allocated
+
+    void bufferResize(size_t minsize);
+    void bufferFlush() VL_MT_UNSAFE_ONE;
+    void bufferCheck() {
+        // Flush the write buffer if there's not enough space left for new information
+        // We only call this once per vector, so we need enough slop for a very wide "b###" line
+        if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush();
+    }
+    void openNextImp(bool incFilename);
+    void closePrev();
+    void closeErr();
+    void printIndent(int level_change);
+    void printStr(const char* str);
+    void declare(uint32_t code, const char* name, const char* wirep, bool array, int arraynum,
+                 bool bussed, int msb, int lsb);
+
+    // CONSTRUCTORS
+    VL_UNCOPYABLE(VerilatedVcd);
+
+protected:
+    //=========================================================================
+    // Implementation of VerilatedTrace interface
+
+    // Called when the trace moves forward to a new time point
+    void emitTimeChange(uint64_t timeui) override;
+
+    // Hooks called from VerilatedTrace
+    bool preFullDump() override { return isOpen(); }
+    bool preChangeDump() override;
+
+    // Trace buffer management
+    Buffer* getTraceBuffer(uint32_t fidx) override;
+    void commitTraceBuffer(Buffer*) override;
+
+    // Configure sub-class
+    void configure(const VerilatedTraceConfig&) override {};
+
+public:
+    //=========================================================================
+    // External interface to client code
+
+    // CONSTRUCTOR
+    explicit VerilatedVcd(VerilatedVcdFile* filep = nullptr);
+    ~VerilatedVcd();
+
+    // ACCESSORS
+    // Set size in bytes after which new file should be created.
+    void rolloverSize(uint64_t size) VL_MT_SAFE { m_rolloverSize = size; }
+
+    // METHODS - All must be thread safe
+    // Open the file; call isOpen() to see if errors
+    void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
+    // Open next data-only file
+    void openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex);
+    // Close the file
+    void close() VL_MT_SAFE_EXCLUDES(m_mutex);
+    // Flush any remaining data to this file
+    void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
+    // Return if file is open
+    bool isOpen() const VL_MT_SAFE { return m_isOpen; }
+
+    //=========================================================================
+    // Internal interface to Verilator generated code
+
+    void pushPrefix(const char*, VerilatedTracePrefixType);
+    void popPrefix();
+
+    void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                   VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
+                   bool array, int arraynum);
+    void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                 VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
+                 bool array, int arraynum);
+    void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                 VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
+                 bool array, int arraynum, int msb, int lsb);
+    void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                  VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
+                  bool array, int arraynum, int msb, int lsb);
+    void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                   VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
+                   bool array, int arraynum, int msb, int lsb);
+    void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
+                    VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
+                    bool array, int arraynum);
+};
+
+#ifndef DOXYGEN
+// Declare specialization here as it's used in VerilatedFstC just below
+template <>
+void VerilatedVcd::Super::dump(uint64_t time);
+template <>
+void VerilatedVcd::Super::set_time_unit(const char* unitp);
+template <>
+void VerilatedVcd::Super::set_time_unit(const std::string& unit);
+template <>
+void VerilatedVcd::Super::set_time_resolution(const char* unitp);
+template <>
+void VerilatedVcd::Super::set_time_resolution(const std::string& unit);
+template <>
+void VerilatedVcd::Super::dumpvars(int level, const std::string& hier);
+#endif  // DOXYGEN
+
+//=============================================================================
+// VerilatedVcdBuffer
+
+class VerilatedVcdBuffer VL_NOT_FINAL {
+    // Give the trace file and sub-classes access to the private bits
+    friend VerilatedVcd;
+    friend VerilatedVcd::Super;
+    friend VerilatedVcd::Buffer;
+    friend VerilatedVcd::OffloadBuffer;
+
+    VerilatedVcd& m_owner;  // Trace file owning this buffer. Required by subclasses.
+
+    // Write pointer into output buffer (in parallel mode, this is set up in 'getTraceBuffer')
+    char* m_writep = m_owner.parallel() ? nullptr : m_owner.m_writep;
+    // Output buffer flush trigger location (only used when not parallel)
+    char* const m_wrFlushp = m_owner.parallel() ? nullptr : m_owner.m_wrFlushp;
+
+    // VCD line end string codes + metadata
+    const char* const m_suffixes = m_owner.m_suffixes.data();
+    // The maximum number of bytes a single signal can emit
+    const size_t m_maxSignalBytes = m_owner.m_maxSignalBytes;
+
+    // Additional data for parallel tracing only
+    char* m_bufp = nullptr;  // The beginning of the trace buffer
+    size_t m_size = 0;  // The size of the buffer at m_bufp
+    char* m_growp = nullptr;  // Resize limit pointer
+
+    void adjustGrowp() {
+        m_growp = (m_bufp + m_size) - (2 * m_maxSignalBytes);
+        assert(m_growp >= m_bufp + m_maxSignalBytes);
+    }
+
+    void finishLine(uint32_t code, char* writep);
+
+    // CONSTRUCTOR
+    explicit VerilatedVcdBuffer(VerilatedVcd& owner)
+        : m_owner{owner} {}
+    virtual ~VerilatedVcdBuffer() = default;
+
+    //=========================================================================
+    // Implementation of VerilatedTraceBuffer interface
+    // Implementations of duck-typed methods for VerilatedTraceBuffer. These are
+    // called from only one place (the full* methods), so always inline them.
+    VL_ATTR_ALWINLINE void emitEvent(uint32_t code);
+    VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
+    VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
+    VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
+    VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
+    VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
+    VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
+    VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
+};
+
+//=============================================================================
+// VerilatedFile
+/// Class representing a file to write to. These virtual methods can be
+/// overrode for e.g. socket I/O.
+
+class VerilatedVcdFile VL_NOT_FINAL {
+private:
+    int m_fd = 0;  // File descriptor we're writing to
+public:
+    // METHODS
+    /// Construct a (as yet) closed file
+    VerilatedVcdFile() = default;
+    /// Close and destruct
+    virtual ~VerilatedVcdFile() = default;
+    /// Open a file with given filename
+    virtual bool open(const std::string& name) VL_MT_UNSAFE;
+    /// Close object's file
+    virtual void close() VL_MT_UNSAFE;
+    /// Write data to file (if it is open)
+    virtual ssize_t write(const char* bufp, ssize_t len) VL_MT_UNSAFE;
+};
+
+//=============================================================================
+// VerilatedVcdC
+/// Class representing a VCD dump file in C standalone (no SystemC)
+/// simulations.  Also derived for use in SystemC simulations.
+
+class VerilatedVcdC VL_NOT_FINAL : public VerilatedTraceBaseC {
+    VerilatedVcd m_sptrace;  // Trace file being created
+
+    // CONSTRUCTORS
+    VL_UNCOPYABLE(VerilatedVcdC);
+
+public:
+    /// Construct the dump. Optional argument is a preconstructed file.
+    explicit VerilatedVcdC(VerilatedVcdFile* filep = nullptr)
+        : m_sptrace{filep} {}
+    /// Destruct, flush, and close the dump
+    virtual ~VerilatedVcdC() { close(); }
+
+    // METHODS - User called
+
+    /// Return if file is open
+    bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); }
+    /// Open a new VCD file
+    /// This includes a complete header dump each time it is called,
+    /// just as if this object was deleted and reconstructed.
+    virtual void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); }
+    /// Continue a VCD dump by rotating to a new file name
+    /// The header is only in the first file created, this allows
+    /// "cat" to be used to combine the header plus any number of data files.
+    void openNext(bool incFilename = true) VL_MT_SAFE { m_sptrace.openNext(incFilename); }
+    /// Set size in bytes after which new file should be created
+    /// This will create a header file, followed by each separate file
+    /// which might be larger than the given size (due to chunking and
+    /// alignment to a start of a given time's dump).  Any file but the
+    /// first may be removed.  Cat files together to create viewable vcd.
+    void rolloverSize(size_t size) VL_MT_SAFE { m_sptrace.rolloverSize(size); }
+    /// Close dump
+    void close() VL_MT_SAFE {
+        m_sptrace.close();
+        modelConnected(false);
+    }
+    /// Flush dump
+    void flush() VL_MT_SAFE { m_sptrace.flush(); }
+    /// Write one cycle of dump data
+    /// Call with the current context's time just after eval'ed,
+    /// e.g. ->dump(contextp->time())
+    void dump(uint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
+    /// Write one cycle of dump data - backward compatible and to reduce
+    /// conversion warnings.  It's better to use a uint64_t time instead.
+    void dump(double timestamp) { dump(static_cast(timestamp)); }
+    void dump(uint32_t timestamp) { dump(static_cast(timestamp)); }
+    void dump(int timestamp) { dump(static_cast(timestamp)); }
+
+    // METHODS - Internal/backward compatible
+    // \protectedsection
+
+    // Set time units (s/ms, defaults to ns)
+    // Users should not need to call this, as for Verilated models, these
+    // propagate from the Verilated default timeunit
+    void set_time_unit(const char* unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); }
+    void set_time_unit(const std::string& unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); }
+    // Set time resolution (s/ms, defaults to ns)
+    // Users should not need to call this, as for Verilated models, these
+    // propagate from the Verilated default timeprecision
+    void set_time_resolution(const char* unit) VL_MT_SAFE { m_sptrace.set_time_resolution(unit); }
+    void set_time_resolution(const std::string& unit) VL_MT_SAFE {
+        m_sptrace.set_time_resolution(unit);
+    }
+    // Set variables to dump, using $dumpvars format
+    // If level = 0, dump everything and hier is then ignored
+    void dumpvars(int level, const std::string& hier) VL_MT_SAFE {
+        m_sptrace.dumpvars(level, hier);
+    }
+
+    // Internal class access
+    VerilatedVcd* spTrace() { return &m_sptrace; }
+};
+
+#endif  // guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.cpp
new file mode 100644
index 00000000000..e367eabc59e
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.cpp
@@ -0,0 +1,24 @@
+// -*- mode: C++; c-file-style: "cc-mode" -*-
+//=============================================================================
+//
+// Code available from: https://verilator.org
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of either the GNU Lesser General Public License Version 3
+// or the Perl Artistic License Version 2.0.
+// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
+// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+//
+//=============================================================================
+///
+/// \file
+/// \brief Verilated tracing in VCD Format implementation code
+///
+/// This file is deprecated, only verilated_vcd_sc.h is needed.
+/// It is provided only for backward compatibility with user's linker scripts.
+///
+//=============================================================================
+
+#ifdef VL_NO_LEGACY
+#error "verilated_vcd_sc.cpp is deprecated; verilated_vcd_sc.h is self-sufficient"
+#endif
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.h
new file mode 100644
index 00000000000..36e4e80edff
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.h
@@ -0,0 +1,57 @@
+// -*- mode: C++; c-file-style: "cc-mode" -*-
+//=============================================================================
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of either the GNU Lesser General Public License Version 3
+// or the Perl Artistic License Version 2.0.
+// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
+// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+//
+//=============================================================================
+///
+/// \file
+/// \brief Verilated tracing in VCD format for SystemC header
+///
+/// User wrapper code should use this header when creating VCD SystemC traces.
+///
+/// This class is not threadsafe, as the SystemC kernel is not threadsafe.
+///
+//=============================================================================
+
+#ifndef VERILATOR_VERILATED_VCD_SC_H_
+#define VERILATOR_VERILATED_VCD_SC_H_
+
+#include "verilatedos.h"
+
+#include "verilated_sc_trace.h"
+#include "verilated_vcd_c.h"
+
+//=============================================================================
+// VerilatedVcdSc
+/// Trace file used to create VCD dump for SystemC version of Verilated models. It's very similar
+/// to its C version (see the class VerilatedVcdC)
+
+class VerilatedVcdSc final : VerilatedScTraceBase, public VerilatedVcdC {
+    // CONSTRUCTORS
+    VL_UNCOPYABLE(VerilatedVcdSc);
+
+public:
+    VerilatedVcdSc() {
+        spTrace()->set_time_unit(VerilatedScTraceBase::getScTimeUnit());
+        spTrace()->set_time_resolution(VerilatedScTraceBase::getScTimeResolution());
+        VerilatedScTraceBase::enableDeltaCycles(false);
+    }
+
+    // METHODS
+    // Override VerilatedVcdC. Must be called after starting simulation.
+    void open(const char* filename) override VL_MT_SAFE {
+        VerilatedScTraceBase::checkScElaborationDone();
+        VerilatedVcdC::open(filename);
+    }
+
+    // METHODS - for SC kernel
+    // Called from SystemC kernel
+    void cycle() override { VerilatedVcdC::dump(sc_core::sc_time_stamp().to_double()); }
+};
+
+#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.cpp
new file mode 100644
index 00000000000..7bcee1bdc62
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.cpp
@@ -0,0 +1,4029 @@
+// -*- mode: C++; c-file-style: "cc-mode" -*-
+//*************************************************************************
+//
+// Code available from: https://verilator.org
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of either the GNU Lesser General Public License Version 3
+// or the Perl Artistic License Version 2.0.
+// SPDX-FileCopyrightText: 2009-2026 Wilson Snyder
+// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+//
+//=========================================================================
+///
+/// \file
+/// \brief Verilated VPI implementation code
+///
+/// This file must be compiled and linked against all Verilated objects
+/// that use the VPI.
+///
+/// Use "verilator --vpi" to add this to the Makefile for the linker.
+///
+/// For documentation on the exported functions (named vpi_*) that are
+/// implemented here, refer to the IEEE DPI chapter.
+///
+//=========================================================================
+
+#include "verilatedos.h"
+#define VERILATOR_VERILATED_VPI_CPP_
+
+#include "verilated.h"
+#include "verilated_imp.h"
+#include "verilated_vpi.h"
+
+#include "vltstd/vpi_user.h"
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+//======================================================================
+// Internal constants
+
+#define VL_DEBUG_IF_PLI VL_DEBUG_IF
+constexpr unsigned VL_VPI_LINE_SIZE_ = 8192;
+
+//======================================================================
+// Internal macros
+
+#define VL_VPI_INTERNAL_ VerilatedVpiImp::error_info()->setMessage(vpiInternal)->setMessage
+#define VL_VPI_SYSTEM_ VerilatedVpiImp::error_info()->setMessage(vpiSystem)->setMessage
+#define VL_VPI_ERROR_ VerilatedVpiImp::error_info()->setMessage(vpiError)->setMessage
+#define VL_VPI_WARNING_ VerilatedVpiImp::error_info()->setMessage(vpiWarning)->setMessage
+#define VL_VPI_NOTICE_ VerilatedVpiImp::error_info()->setMessage(vpiNotice)->setMessage
+#define VL_VPI_ERROR_RESET_ VerilatedVpiImp::error_info()->resetError
+
+// Not supported yet
+#define VL_VPI_UNIMP_() \
+    (VL_VPI_ERROR_(__FILE__, __LINE__, Verilated::catName("Unsupported VPI function: ", __func__)))
+
+//======================================================================
+// Implementation
+
+// Base VPI handled object
+class VerilatedVpio VL_NOT_FINAL {
+    // CONSTANTS
+    // Magic value stored in front of object to detect double free etc
+    // Must be odd, as aligned pointer can never be odd
+    static constexpr uint32_t activeMagic() VL_PURE { return 0xfeed100f; }
+
+    // MEM MANGLEMENT
+    // Internal note: Globals may multi-construct, see verilated.cpp top.
+    static thread_local uint8_t* t_freeHeadp;
+
+public:
+    // CONSTRUCTORS
+    VerilatedVpio() = default;
+    virtual ~VerilatedVpio() = default;
+    static void* operator new(size_t size) VL_MT_SAFE {
+        // We new and delete tons of vpi structures, so keep them around
+        // To simplify our free list, we use a size large enough for all derived types
+        // We reserve word zero for the next pointer, as that's safer in case a
+        // dangling reference to the original remains around.
+        static constexpr size_t CHUNK_SIZE = 256;
+        if (VL_UNCOVERABLE(size > CHUNK_SIZE))
+            VL_FATAL_MT(__FILE__, __LINE__, "", "increase CHUNK_SIZE");
+        if (VL_LIKELY(t_freeHeadp)) {
+            uint8_t* const newp = t_freeHeadp;
+            t_freeHeadp = *(reinterpret_cast(newp));
+            *(reinterpret_cast(newp)) = activeMagic();
+            return newp + 8;
+        }
+        // +8: 8 bytes for next
+        uint8_t* newp = reinterpret_cast(::operator new(CHUNK_SIZE + 8));
+        *(reinterpret_cast(newp)) = activeMagic();
+        return newp + 8;
+    }
+    static void operator delete(void* obj, size_t /*size*/) VL_MT_SAFE {
+        uint8_t* const oldp = (static_cast(obj)) - 8;
+        if (VL_UNLIKELY(*(reinterpret_cast(oldp)) != activeMagic())) {
+            VL_FATAL_MT(__FILE__, __LINE__, "",
+                        "vpi_release_handle() called on same object twice, or on non-Verilator "
+                        "VPI object");
+        }
+#ifdef VL_VPI_IMMEDIATE_FREE  // Define to aid in finding leaky handles
+        ::operator delete(oldp);
+#else
+        *(reinterpret_cast(oldp)) = t_freeHeadp;
+        t_freeHeadp = oldp;
+#endif
+    }
+    // MEMBERS
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpio* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    vpiHandle castVpiHandle() { return reinterpret_cast(this); }
+    // ACCESSORS
+    virtual const char* name() const { return ""; }
+    virtual const char* fullname() const { return ""; }
+    virtual const char* defname() const { return ""; }
+    virtual uint32_t type() const { return 0; }
+    virtual uint32_t constType() const { return vpiUndefined; }
+    virtual uint32_t size() const { return 0; }
+    virtual const VerilatedRange* rangep() const { return nullptr; }
+    virtual vpiHandle dovpi_scan() { return nullptr; }
+    virtual PLI_INT32 dovpi_remove_cb() { return 0; }
+};
+
+class VerilatedVpioReasonCb final : public VerilatedVpio {
+    // A handle to a timed or non-timed callback created with vpi_register_cb
+    // User can call vpi_remove_cb or vpi_release_handle on it
+    const uint64_t m_id;  // Unique id/sequence number to find schedule's event
+    const QData m_time;  // Scheduled time, or 0 = not timed
+    const PLI_INT32 m_reason;  // VPI callback reason code
+
+public:
+    // cppcheck-suppress uninitVar  // m_value
+    VerilatedVpioReasonCb(uint64_t id, QData time, PLI_INT32 reason)
+        : m_id{id}
+        , m_time{time}
+        , m_reason{reason} {}
+    ~VerilatedVpioReasonCb() override = default;
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioReasonCb* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiCallback; }
+    PLI_INT32 dovpi_remove_cb() override;
+};
+
+class VerilatedVpioConst final : public VerilatedVpio {
+    const int32_t m_num;
+
+public:
+    explicit VerilatedVpioConst(int32_t num)
+        : m_num{num} {}
+    ~VerilatedVpioConst() override = default;
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioConst* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiConstant; }
+    uint32_t constType() const override { return vpiDecConst; }
+    int32_t num() const { return m_num; }
+};
+
+class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio {
+protected:
+    const VerilatedVar* m_varp = nullptr;
+    const VerilatedScope* m_scopep = nullptr;
+
+    // Usually empty, only gets filled when fullname() is called. Has to be stored as a member so
+    // the char* that fullname() returns is not a temporary.
+    mutable std::string m_fullname;
+
+    int32_t m_indexedDim = -1;
+    const VerilatedRange* get_range() const { return m_varp->range(m_indexedDim + 1); }
+
+public:
+    VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep)
+        : m_varp{varp}
+        , m_scopep{scopep} {}
+    explicit VerilatedVpioVarBase(const VerilatedVpioVarBase* varp) {
+        if (varp) {
+            m_varp = varp->m_varp;
+            m_scopep = varp->m_scopep;
+            m_indexedDim = varp->m_indexedDim;
+        }
+    }
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioVarBase* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    const VerilatedVar* varp() const { return m_varp; }
+    const VerilatedScope* scopep() const { return m_scopep; }
+    // Returns the number of the currently indexed dimension (starting at -1 for none).
+    int32_t indexedDim() const { return m_indexedDim; }
+    // Returns whether the currently indexed dimension is unpacked.
+    bool isIndexedDimUnpacked() const { return indexedDim() + 1 < varp()->udims(); }
+    // Returns a maximum accessible dimension number, counting only unpacked dimensions
+    // (if onlyUnpacked == true), or both unpacked + packed.
+    int32_t maxDim(bool onlyUnpacked) const {
+        return onlyUnpacked ? varp()->udims() - 1 : varp()->dims() - 1;
+    }
+    // Returns a number of elements in the array, stopping at the unpacked-packed boundary.
+    uint32_t size() const override {
+        const int maxDimNum = maxDim(isIndexedDimUnpacked());
+        int size = 1;
+        for (int dim = indexedDim() + 1; dim <= maxDimNum; ++dim)
+            size *= varp()->range(dim)->elements();
+        return size;
+    }
+    // If the array is unpacked, returns the bitsize of a single underlying packed element.
+    // If the array is packed, returns the bitsize of the whole array.
+    uint32_t bitSize() const {
+        if (isIndexedDimUnpacked())
+            return varp()->entBits();
+        else
+            return size();
+    }
+    const VerilatedRange* rangep() const override { return get_range(); }
+    const char* name() const override { return m_varp->name(); }
+    const char* fullname() const override {
+        if (m_fullname.empty()) m_fullname = std::string{m_scopep->name()} + '.' + m_varp->name();
+        return m_fullname.c_str();
+    }
+    virtual void* varDatap() const { return m_varp->datap(); }
+    CData* varCDatap() const {
+        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8););
+        return reinterpret_cast(varDatap());
+    }
+    SData* varSDatap() const {
+        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16););
+        return reinterpret_cast(varDatap());
+    }
+    IData* varIDatap() const {
+        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32););
+        return reinterpret_cast(varDatap());
+    }
+    QData* varQDatap() const {
+        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64););
+        return reinterpret_cast(varDatap());
+    }
+    EData* varEDatap() const {
+        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA););
+        return reinterpret_cast(varDatap());
+    }
+    double* varRealDatap() const {
+        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL););
+        return reinterpret_cast(varDatap());
+    }
+    std::string* varStringDatap() const {
+        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING););
+        return reinterpret_cast(varDatap());
+    }
+    virtual uint32_t bitOffset() const { return 0; }
+};
+
+class VerilatedVpioParam final : public VerilatedVpioVarBase {
+public:
+    VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
+        : VerilatedVpioVarBase{varp, scopep} {}
+    ~VerilatedVpioParam() override = default;
+
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioParam* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiParameter; }
+    uint32_t constType() const override {
+        switch (m_varp->vltype()) {
+        case VLVT_UINT8:
+        case VLVT_UINT16:
+        case VLVT_UINT32:
+        case VLVT_UINT64:
+        case VLVT_WDATA: return vpiDecConst;
+        case VLVT_STRING: return vpiStringConst;
+        case VLVT_REAL: return vpiRealConst;
+        default: return vpiUndefined;
+        }
+    }
+};
+
+class VerilatedVpioRange final : public VerilatedVpio {
+    const VerilatedRange* const m_rangep;
+
+public:
+    explicit VerilatedVpioRange(const VerilatedRange* rangep)
+        : m_rangep{rangep} {}
+    ~VerilatedVpioRange() override = default;
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioRange* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiRange; }
+    uint32_t size() const override { return m_rangep->elements(); }
+    const VerilatedRange* rangep() const override { return m_rangep; }
+};
+
+class VerilatedVpioRangeIter final : public VerilatedVpio {
+    const std::vector m_ranges;
+    std::vector::const_iterator m_iter;
+
+public:
+    explicit VerilatedVpioRangeIter(const std::vector& ranges)
+        : m_ranges{ranges} {
+        m_iter = m_ranges.begin();
+    }
+    ~VerilatedVpioRangeIter() override = default;
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioRangeIter* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiIterator; }
+    vpiHandle dovpi_scan() override {
+        if (VL_UNLIKELY(m_iter == m_ranges.end())) {
+            delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
+            return nullptr;
+        }
+        VerilatedRange* const rangep = new VerilatedRange{*m_iter};
+        ++m_iter;
+        return ((new VerilatedVpioRange{rangep})->castVpiHandle());
+    }
+};
+
+class VerilatedVpioScope VL_NOT_FINAL : public VerilatedVpio {
+protected:
+    const VerilatedScope* const m_scopep;
+    bool m_toplevel = false;
+    const char* m_name;
+    const char* m_fullname;
+    const char* m_defname;
+
+public:
+    explicit VerilatedVpioScope(const VerilatedScope* scopep)
+        : m_scopep{scopep} {
+        m_fullname = m_scopep->name();
+        if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
+        m_name = m_scopep->identifier();
+        m_defname = m_scopep->defname();
+    }
+    ~VerilatedVpioScope() override = default;
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioScope* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiGenScope; }
+    const VerilatedScope* scopep() const { return m_scopep; }
+    const char* name() const override { return m_name; }
+    const char* fullname() const override { return m_fullname; }
+    const char* defname() const override { return m_defname; }
+    bool toplevel() const { return m_toplevel; }
+};
+
+class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase {
+    uint8_t* m_prevDatap = nullptr;  // Previous value of data, for cbValueChange
+    union {
+        uint8_t u8[4];
+        uint32_t u32;
+    } m_mask;  // memoized variable mask
+    uint32_t m_entSize = 0;  // memoized variable size
+    uint32_t m_bitOffset = 0;
+
+protected:
+    void* m_varDatap = nullptr;  // varp()->datap() adjusted for array entries
+    std::vector m_index;
+
+public:
+    VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
+        : VerilatedVpioVarBase{varp, scopep} {
+        m_mask.u32 = VL_MASK_I(varp->entBits());
+        m_entSize = varp->entSize();
+        m_varDatap = varp->datap();
+    }
+    explicit VerilatedVpioVar(const VerilatedVpioVar* varp)
+        : VerilatedVpioVarBase{varp} {
+        if (varp) {
+            m_mask.u32 = varp->m_mask.u32;
+            m_entSize = varp->m_entSize;
+            m_varDatap = varp->m_varDatap;
+            m_index = varp->m_index;
+            // Not copying m_prevDatap, must be nullptr
+        } else {
+            m_mask.u32 = 0;
+        }
+    }
+    ~VerilatedVpioVar() override {
+        if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr);
+    }
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioVar* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t bitOffset() const override { return m_bitOffset; }
+    uint32_t mask() const { return m_mask.u32; }
+    uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
+    uint32_t entSize() const { return m_entSize; }
+    const std::vector& index() const { return m_index; }
+    VerilatedVpioVar* withIndex(int32_t index) const {
+        if (VL_UNLIKELY(indexedDim() + 1 >= varp()->dims())) return nullptr;
+
+        auto ret = new VerilatedVpioVar{this};
+        ret->m_index.push_back(index);
+        ret->m_indexedDim++;
+
+        int chunkSize = 1;
+        for (int dim = maxDim(isIndexedDimUnpacked()); dim > indexedDim() + 1; dim--)
+            chunkSize *= varp()->range(dim)->elements();
+
+        if (isIndexedDimUnpacked())
+            ret->m_varDatap = (static_cast(ret->m_varDatap))
+                              + entSize() * chunkSize * (index - get_range()->low());
+        else
+            ret->m_bitOffset += chunkSize * (index - get_range()->low());
+
+        return ret;
+    }
+    uint32_t type() const override {
+        uint32_t type;
+        // TODO have V3EmitCSyms.cpp put vpiType directly into constant table
+        switch (varp()->vltype()) {
+        case VLVT_REAL: type = vpiRealVar; break;
+        case VLVT_STRING: type = vpiStringVar; break;
+        default: type = varp()->isBitVar() ? vpiBitVar : vpiReg; break;
+        }
+        if (isIndexedDimUnpacked())
+            return vpiRegArray;
+        else
+            return type;
+    }
+    const char* fullname() const override {
+        static thread_local std::string t_out;
+        t_out = std::string{scopep()->name()} + "." + name();
+        for (auto idx : index()) t_out += "[" + std::to_string(idx) + "]";
+        return t_out.c_str();
+    }
+    void* prevDatap() const { return m_prevDatap; }
+    void* varDatap() const override { return m_varDatap; }
+    void createPrevDatap() {
+        if (VL_UNLIKELY(!m_prevDatap)) {
+            m_prevDatap = new uint8_t[entSize()];
+            std::memcpy(prevDatap(), m_varDatap, entSize());
+        }
+    }
+};
+
+class VerilatedVpioVarIter final : public VerilatedVpio {
+    const VerilatedScope* const m_scopep;
+    VerilatedVarNameMap::const_iterator m_it;
+    bool m_started = false;
+    const VerilatedScope* m_topscopep = nullptr;
+    bool m_onlyParams;
+
+public:
+    explicit VerilatedVpioVarIter(const VerilatedVpioScope* vop, bool onlyParams = false)
+        : m_scopep{vop->scopep()}
+        , m_onlyParams{onlyParams} {
+        if (VL_UNLIKELY(vop->toplevel()))
+            // This is a toplevel, so get TOP scope to search for ports during vpi_scan.
+            m_topscopep = Verilated::threadContextp()->scopeFind("TOP");
+    }
+    ~VerilatedVpioVarIter() override = default;
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioVarIter* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiIterator; }
+    vpiHandle dovpi_scan() override {
+        if (VL_UNLIKELY(!m_scopep->varsp())) {
+            delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
+            return nullptr;  // End of list - only one deep
+        }
+        while (true) {
+            const VerilatedVarNameMap* const varsp = m_scopep->varsp();
+            if (VL_UNLIKELY(!m_started)) {
+                m_it = varsp->begin();
+                m_started = true;
+            } else if (VL_UNLIKELY(m_it == varsp->end())) {
+                delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
+                return nullptr;
+            } else {
+                ++m_it;
+            }
+            if (VL_UNLIKELY(m_it == varsp->end())) {
+                delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
+                return nullptr;
+            }
+            if (m_onlyParams && !m_it->second.isParam()) continue;
+            if (VL_UNLIKELY(m_topscopep)) {
+                if (const VerilatedVar* topvarp = m_topscopep->varFind(m_it->second.name())) {
+                    if (topvarp->isParam()) {
+                        return ((new VerilatedVpioParam{topvarp, m_topscopep})->castVpiHandle());
+                    } else {
+                        return ((new VerilatedVpioVar{topvarp, m_topscopep})->castVpiHandle());
+                    }
+                }
+            }
+            if (m_it->second.isParam()) {
+                return ((new VerilatedVpioParam{&(m_it->second), m_scopep})->castVpiHandle());
+            } else {
+                return ((new VerilatedVpioVar{&(m_it->second), m_scopep})->castVpiHandle());
+            }
+        }
+    }
+};
+
+class VerilatedVpioRegIter final : public VerilatedVpio {
+    VerilatedVpioVar* m_var;
+    std::vector m_ranges;
+    std::vector m_nextIndex;
+    const int32_t m_maxDim;
+
+public:
+    explicit VerilatedVpioRegIter(const VerilatedVpioVar* vop)
+        : m_var{new VerilatedVpioVar(vop)}
+        , m_maxDim{vop->varp()->udims() - 1} {
+        for (auto it = vop->indexedDim() + 1; it <= m_maxDim; ++it)
+            m_ranges.push_back(*vop->varp()->range(it));
+        for (auto it : m_ranges) m_nextIndex.push_back(it.right());
+    }
+    ~VerilatedVpioRegIter() override = default;
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioRegIter* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiIterator; }
+    vpiHandle dovpi_scan() override {
+        if (VL_UNLIKELY(m_var->indexedDim() >= m_maxDim)) {
+            // Trying to iterate over a non-array object
+            delete this;
+            return nullptr;
+        }
+        if (m_nextIndex.front() > m_ranges.front().high()
+            || m_nextIndex.front() < m_ranges.front().low()) {
+            // Finished iterating
+            delete this;
+            return nullptr;
+        }
+
+        VerilatedVpioVar* ret = m_var;
+        for (auto it : m_nextIndex) ret = ret->withIndex(it);
+
+        // Increase the index, pretending the dimensions are flattened
+        for (int32_t it = m_ranges.size() - 1; it >= 0; it--) {
+            m_nextIndex.at(it) += m_ranges.at(it).increment();
+            if (m_nextIndex.at(it) <= m_ranges.at(it).high()
+                && m_nextIndex.at(it) >= m_ranges.at(it).low())
+                break;
+            else if (it > 0)
+                m_nextIndex.at(it) = m_ranges.at(it).right();
+        }
+
+        return ret->castVpiHandle();
+    }
+};
+
+class VerilatedVpioModule final : public VerilatedVpioScope {
+
+public:
+    explicit VerilatedVpioModule(const VerilatedScope* modulep)
+        : VerilatedVpioScope{modulep} {
+        // Look for '.' not inside escaped identifier
+        const std::string scopename = m_fullname;
+        std::string::size_type pos = std::string::npos;
+        size_t i = 0;
+        while (i < scopename.length()) {
+            if (scopename[i] == '\\') {
+                while (i < scopename.length() && scopename[i] != ' ') ++i;
+                ++i;  // Proc ' ', it should always be there. Then grab '.' on next cycle
+            } else {
+                while (i < scopename.length() && scopename[i] != '.') ++i;
+                if (i < scopename.length()) pos = i++;
+            }
+        }
+        if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true;
+    }
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioModule* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiModule; }
+};
+
+class VerilatedVpioModuleIter final : public VerilatedVpio {
+    const std::vector* m_vec;
+    std::vector::const_iterator m_it;
+
+public:
+    explicit VerilatedVpioModuleIter(const std::vector& vec)
+        : m_vec{&vec} {
+        m_it = m_vec->begin();
+    }
+    ~VerilatedVpioModuleIter() override = default;
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioModuleIter* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiIterator; }
+    vpiHandle dovpi_scan() override {
+        while (true) {
+            if (m_it == m_vec->end()) {
+                delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
+                return nullptr;
+            }
+            const VerilatedScope* const modp = *m_it++;
+            const VerilatedScope::Type itype = modp->type();
+            if (itype == VerilatedScope::SCOPE_MODULE) {
+                return (new VerilatedVpioModule{modp})->castVpiHandle();
+            }
+        }
+    }
+};
+
+class VerilatedVpioScopeIter final : public VerilatedVpio {
+    const std::vector* m_vec;
+    std::vector::const_iterator m_it;
+
+public:
+    explicit VerilatedVpioScopeIter(const std::vector& vec)
+        : m_vec{&vec} {
+        m_it = m_vec->begin();
+    }
+    ~VerilatedVpioScopeIter() override = default;
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioScopeIter* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiIterator; }
+    vpiHandle dovpi_scan() override {
+        while (true) {
+            if (m_it == m_vec->end()) {
+                delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
+                return nullptr;
+            }
+            const VerilatedScope* const modp = *m_it++;
+            const VerilatedScope::Type itype = modp->type();
+            if (itype == VerilatedScope::SCOPE_OTHER) {
+                return (new VerilatedVpioScope{modp})->castVpiHandle();
+            } else if (itype == VerilatedScope::SCOPE_MODULE) {
+                return (new VerilatedVpioModule{modp})->castVpiHandle();
+            }
+        }
+    }
+};
+
+static const char* d_unit = "$unit";
+class VerilatedVpioPackage final : public VerilatedVpioScope {
+    std::string m_fullname_string;
+
+public:
+    explicit VerilatedVpioPackage(const VerilatedScope* modulep)
+        : VerilatedVpioScope{modulep} {
+        m_fullname_string = std::string{m_fullname} + "::";
+        if (m_fullname_string == "\\$unit ::") m_fullname_string = "$unit::";
+
+        if (strcmp(m_name, "\\$unit ") == 0) m_name = d_unit;
+    }
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioPackage* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    const char* fullname() const override { return m_fullname_string.c_str(); }
+    uint32_t type() const override { return vpiPackage; }
+};
+
+class VerilatedVpioInstanceIter final : public VerilatedVpio {
+    const std::vector* m_vec;
+    std::vector::const_iterator m_it;
+
+public:
+    explicit VerilatedVpioInstanceIter(const std::vector& vec)
+        : m_vec{&vec} {
+        m_it = m_vec->begin();
+    }
+    ~VerilatedVpioInstanceIter() override = default;
+    // cppcheck-suppress duplInheritedMember
+    static VerilatedVpioInstanceIter* castp(vpiHandle h) {
+        return dynamic_cast(reinterpret_cast(h));
+    }
+    uint32_t type() const override { return vpiIterator; }
+    vpiHandle dovpi_scan() override {
+        while (true) {
+            if (m_it == m_vec->end()) {
+                delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
+                return nullptr;
+            }
+            const VerilatedScope::Type itype = (*m_it)->type();
+            const VerilatedScope* const modp = *m_it++;
+            if (itype == VerilatedScope::SCOPE_MODULE) {
+                return (new VerilatedVpioModule{modp})->castVpiHandle();
+            }
+            if (itype == VerilatedScope::SCOPE_PACKAGE) {
+                return (new VerilatedVpioPackage{modp})->castVpiHandle();
+            }
+        }
+    }
+};
+
+//======================================================================
+
+using VerilatedPliCb = PLI_INT32 (*)(struct t_cb_data*);
+
+class VerilatedVpiCbHolder final {
+    // Holds information needed to call a callback
+    uint64_t m_id;  // Unique id/sequence number to find schedule's event, 0 = invalid
+    s_cb_data m_cbData;
+    s_vpi_value m_value;
+    VerilatedVpioVar m_varo;  // If a cbValueChange callback, the object we will return
+
+public:
+    // cppcheck-suppress uninitVar  // m_value
+    VerilatedVpiCbHolder(uint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
+        : m_id{id}
+        , m_cbData{*cbDatap}
+        , m_varo{varop} {
+        m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
+        m_cbData.value = &m_value;
+        if (varop) {
+            m_cbData.obj = m_varo.castVpiHandle();
+            m_varo.createPrevDatap();
+        } else {
+            m_cbData.obj = nullptr;
+        }
+    }
+    ~VerilatedVpiCbHolder() = default;
+    VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
+    s_cb_data* cb_datap() { return &m_cbData; }
+    uint64_t id() const { return m_id; }
+    bool invalid() const { return !m_id; }
+    void invalidate() { m_id = 0; }
+};
+
+class VerilatedVpiPutHolder final {
+    VerilatedVpioVar m_var;
+    s_vpi_value m_value;
+    union Storage {
+        char init = 0;  // to ensure trivial constructor
+        std::string str;
+        std::vector vec;
+        ~Storage() noexcept {/* handled by VerilatedVpiPutHolder */};
+    } m_storage{};
+
+public:
+    VerilatedVpiPutHolder(const VerilatedVpioVar* vop, p_vpi_value valuep)
+        : m_var{vop} {
+        m_value.format = valuep->format;
+        switch (valuep->format) {
+        case vpiBinStrVal:  // FALLTHRU
+        case vpiOctStrVal:  // FALLTHRU
+        case vpiDecStrVal:  // FALLTHRU
+        case vpiHexStrVal:  // FALLTHRU
+        case vpiStringVal: {
+            new (&m_storage.str) std::string{valuep->value.str};
+            m_value.value.str = const_cast(m_storage.str.c_str());
+            break;
+        }
+        case vpiScalarVal: {
+            m_value.value.scalar = valuep->value.scalar;
+            break;
+        }
+        case vpiIntVal: {
+            m_value.value.integer = valuep->value.integer;
+            break;
+        }
+        case vpiRealVal: {
+            m_value.value.real = valuep->value.real;
+            break;
+        }
+        case vpiVectorVal: {
+            size_t words = 0;
+            switch (vop->varp()->vltype()) {
+            case VLVT_UINT8:
+            case VLVT_UINT16:
+            case VLVT_UINT32: {
+                words = 1;
+                break;
+            }
+            case VLVT_UINT64: {
+                words = 2;
+                break;
+            }
+            case VLVT_WDATA: {
+                words = VL_WORDS_I(vop->varp()->entBits());
+                break;
+            }
+            default: break;
+            }
+            new (&m_storage.vec)
+                std::vector{valuep->value.vector, &valuep->value.vector[words]};
+            m_value.value.vector = m_storage.vec.data();
+            break;
+        }
+        }
+    }
+
+    VerilatedVpiPutHolder(VerilatedVpiPutHolder const& o)
+        : m_var{o.m_var}
+        , m_value{o.m_value} {
+        switch (m_value.format) {
+        case vpiBinStrVal:  // FALLTHRU
+        case vpiOctStrVal:  // FALLTHRU
+        case vpiDecStrVal:  // FALLTHRU
+        case vpiHexStrVal:  // FALLTHRU
+        case vpiStringVal: {
+            new (&m_storage.str) std::string{o.m_storage.str};
+            break;
+        }
+        case vpiVectorVal: {
+            new (&m_storage.vec) std::vector{o.m_storage.vec};
+            break;
+        }
+        }
+    }
+
+    VerilatedVpiPutHolder(VerilatedVpiPutHolder&& o) noexcept
+        : m_var{std::move(o.m_var)}
+        , m_value{std::move(o.m_value)} {
+        switch (m_value.format) {
+        case vpiBinStrVal:  // FALLTHRU
+        case vpiOctStrVal:  // FALLTHRU
+        case vpiDecStrVal:  // FALLTHRU
+        case vpiHexStrVal:  // FALLTHRU
+        case vpiStringVal: {
+            new (&m_storage.str) std::string{std::move(o.m_storage.str)};
+            break;
+        }
+        case vpiVectorVal: {
+            new (&m_storage.vec) std::vector{std::move(o.m_storage.vec)};
+            break;
+        }
+        }
+    }
+
+    ~VerilatedVpiPutHolder() noexcept {
+        switch (m_value.format) {
+        case vpiBinStrVal:  // FALLTHRU
+        case vpiOctStrVal:  // FALLTHRU
+        case vpiDecStrVal:  // FALLTHRU
+        case vpiHexStrVal:  // FALLTHRU
+        case vpiStringVal: m_storage.str.~basic_string(); break;
+        case vpiVectorVal: m_storage.vec.~vector(); break;
+        }
+    }
+
+    VerilatedVpioVar* varp() { return &m_var; }
+    p_vpi_value valuep() { return &m_value; }
+
+    static bool canInertialDelay(p_vpi_value valuep) {
+        switch (valuep->format) {
+        case vpiBinStrVal:  // FALLTHRU
+        case vpiOctStrVal:  // FALLTHRU
+        case vpiDecStrVal:  // FALLTHRU
+        case vpiHexStrVal:  // FALLTHRU
+        case vpiStringVal: {
+            if (VL_UNLIKELY(!valuep->value.str)) return false;
+            break;
+        }
+        case vpiScalarVal:  // FALLTHRU
+        case vpiIntVal:  // FALLTHRU
+        case vpiRealVal: break;
+        case vpiVectorVal: {
+            if (VL_UNLIKELY(!valuep->value.vector)) return false;
+            break;
+        }
+        default: {
+            return false;
+        }
+        }
+        return true;
+    }
+};
+
+struct VerilatedVpiTimedCbsCmp final {
+    // Ordering sets keyed by time, then callback unique id
+    bool operator()(const std::pair& a,
+                    const std::pair& b) const {
+        if (a.first < b.first) return true;
+        if (a.first > b.first) return false;
+        return a.second < b.second;
+    }
+};
+
+class VerilatedVpiError;
+void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset);
+
+class VerilatedVpiImp final {
+    enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 };  // Maximum callback reason
+    using VpioCbList = std::list;
+    using VpioFutureCbs = std::map, VerilatedVpiCbHolder>;
+
+    // All only medium-speed, so use singleton function
+    // Callbacks that are past or at current timestamp
+    std::array m_cbCurrentLists;
+    VpioCbList m_cbCallList;  // List of callbacks currently being called by callCbs
+    VpioFutureCbs m_futureCbs;  // Time based callbacks for future timestamps
+    VpioFutureCbs m_nextCbs;  // cbNextSimTime callbacks
+    std::list m_inertialPuts;  // Pending vpi puts due to vpiInertialDelay
+    VerilatedVpiError* m_errorInfop = nullptr;  // Container for vpi error info
+    VerilatedAssertOneThread m_assertOne;  // Assert only called from single thread
+    uint64_t m_nextCallbackId = 1;  // Id to identify callback
+    bool m_evalNeeded = false;  // Model has had signals updated via vpi_put_value()
+
+    static VerilatedVpiImp& s() {  // Singleton
+        static VerilatedVpiImp s_s;
+        return s_s;
+    }
+
+public:
+    static void assertOneCheck() { s().m_assertOne.check(); }
+    static uint64_t nextCallbackId() { return ++s().m_nextCallbackId; }
+
+    static void cbCurrentAdd(uint64_t id, const s_cb_data* cb_data_p) {
+        // The passed cb_data_p was property of the user, so need to recreate
+        if (VL_UNCOVERABLE(cb_data_p->reason >= CB_ENUM_MAX_VALUE)) {
+            VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
+        }
+        VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64 " obj=%p\n",
+                                    cb_data_p->reason, id, cb_data_p->obj););
+        VerilatedVpioVar* varop = nullptr;
+        if (cb_data_p->reason == cbValueChange) varop = VerilatedVpioVar::castp(cb_data_p->obj);
+        s().m_cbCurrentLists[cb_data_p->reason].emplace_back(id, cb_data_p, varop);
+    }
+    static void cbFutureAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
+        // The passed cb_data_p was property of the user, so need to recreate
+        VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64 " time=%" PRIu64
+                                    " obj=%p\n",
+                                    cb_data_p->reason, id, time, cb_data_p->obj););
+        s().m_futureCbs.emplace(std::piecewise_construct, std::forward_as_tuple(time, id),
+                                std::forward_as_tuple(id, cb_data_p, nullptr));
+    }
+    static void cbNextAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
+        // The passed cb_data_p was property of the user, so need to recreate
+        VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d(NEXT) id=%" PRId64
+                                    " time=%" PRIu64 " obj=%p\n",
+                                    cb_data_p->reason, id, time, cb_data_p->obj););
+        s().m_nextCbs.emplace(std::piecewise_construct, std::forward_as_tuple(time, id),
+                              std::forward_as_tuple(id, cb_data_p, nullptr));
+    }
+    static void cbReasonRemove(uint64_t id, uint32_t reason, QData time) {
+        // Id might no longer exist, if already removed due to call after event, or teardown
+        // We do not remove it now as we may be iterating the list,
+        // instead set to nullptr and will cleanup later
+        // Remove from cbCurrent queue
+        for (auto& ir : s().m_cbCurrentLists[reason]) {
+            if (ir.id() == id) {
+                ir.invalidate();
+                return;  // Once found, it won't also be in m_cbCallList, m_futureCbs, or m_nextCbs
+            }
+        }
+        for (auto& ir : s().m_cbCallList) {
+            if (ir.id() == id) {
+                ir.invalidate();
+                return;  // Once found, it won't also be in m_futureCbs or m_nextCbs
+            }
+        }
+        {  // Remove from cbFuture queue
+            const auto it = s().m_futureCbs.find(std::make_pair(time, id));
+            if (it != s().m_futureCbs.end()) {
+                it->second.invalidate();
+                return;
+            }
+        }
+        {  // Remove from cbNext
+            const auto it = s().m_nextCbs.find(std::make_pair(time, id));
+            if (it != s().m_nextCbs.end()) {
+                it->second.invalidate();
+                return;
+            }
+        }
+    }
+    static void moveFutureCbs() VL_MT_UNSAFE_ONE {
+        // For any events past current time, move from cbFuture queue to cbCurrent queue
+        if (s().m_futureCbs.empty() && s().m_nextCbs.empty()) return;
+        // VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs\n"); dumpCbs(); );
+        const QData time = VL_TIME_Q();
+        for (auto it = s().m_futureCbs.begin();  //
+             VL_UNLIKELY(it != s().m_futureCbs.end() && it->first.first <= time);) {
+            VerilatedVpiCbHolder& hor = it->second;
+            const auto last_it = it;
+            ++it;
+            if (VL_UNLIKELY(!hor.invalid())) {
+                VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs id=%" PRId64 "\n", hor.id()););
+                s().m_cbCurrentLists[hor.cb_datap()->reason].emplace_back(hor);
+            }
+            s().m_futureCbs.erase(last_it);
+        }
+        for (auto it = s().m_nextCbs.begin();  //
+             VL_UNLIKELY(it != s().m_nextCbs.end() && it->first.first < time);) {
+            VerilatedVpiCbHolder& hor = it->second;
+            const auto last_it = it;
+            ++it;
+            if (VL_UNLIKELY(!hor.invalid())) {
+                VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs id=%" PRId64 "\n", hor.id()););
+                s().m_cbCurrentLists[hor.cb_datap()->reason].emplace_back(hor);
+            }
+            s().m_nextCbs.erase(last_it);
+        }
+    }
+    static QData cbNextDeadline() {
+        const auto it = s().m_futureCbs.cbegin();
+        if (VL_LIKELY(it != s().m_futureCbs.cend())) return it->first.first;
+        return ~0ULL;  // maxquad
+    }
+    static bool hasCbs(const uint32_t reason) VL_MT_UNSAFE_ONE {
+        return !s().m_cbCurrentLists[reason].empty();
+    }
+    static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE {
+        VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: callCbs reason=%u\n", reason););
+        assertOneCheck();
+        moveFutureCbs();
+        if (s().m_cbCurrentLists[reason].empty()) return false;
+        // Iterate on old list, making new list empty, to prevent looping over newly added elements
+        std::swap(s().m_cbCurrentLists[reason], s().m_cbCallList);
+        bool called = false;
+        for (VerilatedVpiCbHolder& ihor : s().m_cbCallList) {
+            // cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
+            if (VL_LIKELY(!ihor.invalid())) {  // Not deleted earlier
+                VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback reason=%d id=%" PRId64 "\n",
+                                            reason, ihor.id()););
+                ihor.invalidate();  // Timed callbacks are one-shot
+                (ihor.cb_rtnp())(ihor.cb_datap());
+                called = true;
+            }
+        }
+        s().m_cbCallList.clear();
+        return called;
+    }
+    static bool callValueCbs() VL_MT_UNSAFE_ONE {
+        assertOneCheck();
+        VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
+        bool called = false;
+        std::set update;  // set of objects to update after callbacks
+        if (cbObjList.empty()) return called;
+        const auto last = std::prev(cbObjList.end());  // prevent looping over newly added elements
+        for (auto it = cbObjList.begin(); true;) {
+            // cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
+            const bool was_last = it == last;
+            if (VL_UNLIKELY(it->invalid())) {  // Deleted earlier, cleanup
+                it = cbObjList.erase(it);
+                if (was_last) break;
+                continue;
+            }
+            VerilatedVpiCbHolder& ho = *it++;
+            VerilatedVpioVar* const varop
+                = reinterpret_cast(ho.cb_datap()->obj);
+            void* const newDatap = varop->varDatap();
+            void* const prevDatap = varop->prevDatap();  // Was malloced when we added the callback
+            VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
+                                        varop->fullname(), *(static_cast(newDatap)),
+                                        *(static_cast(prevDatap)), newDatap, prevDatap););
+            if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
+                VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
+                                            ho.id(), varop->fullname(),
+                                            *(static_cast(newDatap))););
+                update.insert(varop);
+                vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
+                (ho.cb_rtnp())(ho.cb_datap());
+                called = true;
+            }
+            if (was_last) break;
+        }
+        for (const VerilatedVpioVar* const ip : update) {
+            std::memcpy(ip->prevDatap(), ip->varDatap(), ip->entSize());
+        }
+        return called;
+    }
+    static void dumpCbs() VL_MT_UNSAFE_ONE;
+    static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE;  // getter for vpi error info
+    static bool evalNeeded() { return s().m_evalNeeded; }
+    static void evalNeeded(bool evalNeeded) { s().m_evalNeeded = evalNeeded; }
+    static void inertialDelay(const VerilatedVpioVar* vop, p_vpi_value valuep) {
+        s().m_inertialPuts.emplace_back(vop, valuep);
+    }
+    static void doInertialPuts() {
+        for (auto& it : s().m_inertialPuts) {
+            vpi_put_value(it.varp()->castVpiHandle(), it.valuep(), nullptr, vpiNoDelay);
+        }
+        s().m_inertialPuts.clear();
+    }
+    static auto getForceControlSignals(const VerilatedVpioVarBase* vop);
+
+    // Used in the deleter of vopGuard_t, which is invoked upon
+    // destruction of the return value of getForceControlSignals.
+    // This means that it is called at the end of vpi_get_value whenever the signal
+    // is forceable and at the end of vpi_put_value whenever the signal is both forceable and
+    // either vpiForceFlag or vpiReleaseFlag is used.
+    // Because it is always automatically called at the end, it should not
+    // erase any previously issued errors or warnings.
+    static void releaseWithoutErrorReset(vpiHandle object) {
+        VerilatedVpiImp::assertOneCheck();
+        VerilatedVpio* const vop = VerilatedVpio::castp(object);
+        VL_DO_DANGLING(delete vop, vop);
+    }
+
+    static void releaseVop(VerilatedVpioVar* vop) {
+        releaseWithoutErrorReset(vop->castVpiHandle());
+    }
+
+    using vopGuard_t = std::unique_ptr;
+
+    static std::size_t vlTypeSize(VerilatedVarType vltype);
+    static void setAllBitsToValue(const VerilatedVpioVar* vop, uint8_t bitValue) {
+        assert(bitValue == 0 || bitValue == 1);
+        const uint64_t word = (bitValue == 1) ? -1ULL : 0ULL;
+        const std::size_t wordSize = vlTypeSize(vop->varp()->vltype());
+        assert(wordSize > 0);
+        const uint32_t varBits = vop->bitSize();
+        const std::size_t numChunks = (varBits / wordSize);
+        for (std::size_t i{0}; i < numChunks; ++i) {
+            vl_vpi_put_word(vop, word, wordSize, i * wordSize);
+        }
+        // addOffset == varBits would trigger assertion in vl_vpi_var_access_info even if
+        // bitCount == 0, so first check if there is a remainder
+        if (varBits % wordSize != 0)
+            vl_vpi_put_word(vop, word, varBits % wordSize, numChunks * wordSize);
+    }
+
+    // Recreates the __VforceRd signal's data vector, since __VforceRd is not publicly accessible
+    // in Verilated code.
+    template 
+    static std::vector
+    createReadDataVector(const void* const baseSignalDatap,
+                         const std::pair forceControlDatap,
+                         const std::size_t bitCount) {
+        const void* const forceEnableDatap = forceControlDatap.first;
+        const void* const forceValueDatap = forceControlDatap.second;
+        assert(bitCount > 0);
+        const std::size_t numWords = (bitCount + (8 * sizeof(T)) - 1) / (8 * sizeof(T));  // Ceil
+        std::vector readData(numWords);
+        for (std::size_t i{0}; i < numWords; ++i) {
+            const T forceEnableWord = reinterpret_cast(forceEnableDatap)[i];
+            const T forceValueWord = reinterpret_cast(forceValueDatap)[i];
+            const T baseSignalWord = reinterpret_cast(baseSignalDatap)[i];
+            const T readDataWord
+                = (forceEnableWord & forceValueWord) | (~forceEnableWord & baseSignalWord);
+            readData[i] = readDataWord;
+        }
+        return readData;
+    }
+};
+
+//======================================================================
+// Statics
+// Internal note: Globals may multi-construct, see verilated.cpp top.
+
+thread_local uint8_t* VerilatedVpio::t_freeHeadp = nullptr;
+
+//======================================================================
+// VerilatedVpiError
+// Internal container for vpi error info
+
+class VerilatedVpiError final {
+    t_vpi_error_info m_errorInfo;
+    bool m_flag = false;
+    char m_buff[VL_VPI_LINE_SIZE_];
+    void setError(PLI_BYTE8* message, PLI_BYTE8* code, PLI_BYTE8* file, PLI_INT32 line) {
+        m_errorInfo.message = message;
+        m_errorInfo.file = file;
+        m_errorInfo.line = line;
+        m_errorInfo.code = code;
+        do_callbacks();
+    }
+    void do_callbacks() {
+        if (getError()->level >= vpiError && Verilated::threadContextp()->fatalOnVpiError()) {
+            // Stop on vpi error/unsupported
+            vpi_unsupported();
+        }
+        // We need to run above code first because in the case that the
+        // callback executes further vpi functions we will loose the error
+        // as it will be overwritten.
+        VerilatedVpiImp::callCbs(cbPLIError);
+    }
+
+public:
+    VerilatedVpiError() {
+        m_buff[0] = '\0';
+        m_errorInfo.product = const_cast(Verilated::productName());
+    }
+    ~VerilatedVpiError() = default;
+    static void selfTest() VL_MT_UNSAFE_ONE;
+    VerilatedVpiError* setMessage(PLI_INT32 level) {
+        m_flag = true;
+        m_errorInfo.level = level;
+        return this;
+    }
+    void setMessage(const std::string& file, PLI_INT32 line, const char* message, ...) {
+        // message cannot be a const string& as va_start cannot use a reference
+        static thread_local std::string t_filehold;
+        va_list args;
+        va_start(args, message);
+        VL_VSNPRINTF(m_buff, sizeof(m_buff), message, args);
+        va_end(args);
+        m_errorInfo.state = vpiPLI;
+        t_filehold = file;
+        setError(static_cast(m_buff), nullptr,
+                 const_cast(t_filehold.c_str()), line);
+    }
+    p_vpi_error_info getError() {
+        if (m_flag) return &m_errorInfo;
+        return nullptr;
+    }
+    void resetError() { m_flag = false; }
+    static void vpi_unsupported() {
+        // Not supported yet
+        const p_vpi_error_info error_info_p = VerilatedVpiImp::error_info()->getError();
+        if (error_info_p) {
+            VL_FATAL_MT(error_info_p->file, error_info_p->line, "", error_info_p->message);
+            return;
+        }
+        VL_FATAL_MT(__FILE__, __LINE__, "", "vpi_unsupported called without error info set");
+    }
+    static const char* strFromVpiVal(PLI_INT32 vpiVal) VL_PURE;
+    static const char* strFromVpiObjType(PLI_INT32 vpiVal) VL_PURE;
+    static const char* strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE;
+    static const char* strFromVpiCallbackReason(PLI_INT32 vpiVal) VL_PURE;
+    static const char* strFromVpiProp(PLI_INT32 vpiVal) VL_PURE;
+    static const char* strFromVpiConstType(PLI_INT32 vpiVal) VL_PURE;
+};
+
+//======================================================================
+// VerilatedVpi implementation
+
+bool VerilatedVpi::callCbs(uint32_t reason) VL_MT_UNSAFE_ONE {
+    return VerilatedVpiImp::callCbs(reason);
+}
+
+bool VerilatedVpi::hasCbs(uint32_t reason) VL_MT_UNSAFE_ONE {
+    return VerilatedVpiImp::hasCbs(reason);
+}
+
+// Historical, before we had multiple kinds of timed callbacks
+void VerilatedVpi::callTimedCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::callCbs(cbAfterDelay); }
+
+bool VerilatedVpi::callValueCbs() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::callValueCbs(); }
+
+QData VerilatedVpi::cbNextDeadline() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::cbNextDeadline(); }
+
+void VerilatedVpi::dumpCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::dumpCbs(); }
+
+PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
+    VerilatedVpiImp::cbReasonRemove(m_id, m_reason, m_time);
+    delete this;  // IEEE 37.2.2 a vpi_remove_cb does a vpi_release_handle
+    return 1;
+}
+
+void VerilatedVpi::clearEvalNeeded() VL_MT_UNSAFE_ONE { VerilatedVpiImp::evalNeeded(false); }
+bool VerilatedVpi::evalNeeded() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::evalNeeded(); }
+
+void VerilatedVpi::doInertialPuts() VL_MT_UNSAFE_ONE { VerilatedVpiImp::doInertialPuts(); }
+
+//======================================================================
+// VerilatedVpiImp implementation
+
+void VerilatedVpiImp::dumpCbs() VL_MT_UNSAFE_ONE {
+    assertOneCheck();
+    VL_DBG_MSGF("- vpi: dumpCbs\n");
+    for (uint32_t reason = 0; reason < CB_ENUM_MAX_VALUE; ++reason) {
+        VpioCbList& cbObjList = s().m_cbCurrentLists[reason];
+        for (auto& ho : cbObjList) {
+            if (VL_UNLIKELY(!ho.invalid())) {
+                VL_DBG_MSGF("- vpi:   reason=%d=%s  id=%" PRId64 "\n", reason,
+                            VerilatedVpiError::strFromVpiCallbackReason(reason), ho.id());
+            }
+        }
+    }
+    for (auto& ifuture : s().m_nextCbs) {
+        const QData time = ifuture.first.first;
+        VerilatedVpiCbHolder& ho = ifuture.second;
+        if (VL_UNLIKELY(!ho.invalid())) {
+            VL_DBG_MSGF("- vpi:   time=%" PRId64 "(NEXT) reason=%d=%s  id=%" PRId64 "\n", time,
+                        ho.cb_datap()->reason,
+                        VerilatedVpiError::strFromVpiCallbackReason(ho.cb_datap()->reason),
+                        ho.id());
+        }
+    }
+    for (auto& ifuture : s().m_futureCbs) {
+        const QData time = ifuture.first.first;
+        VerilatedVpiCbHolder& ho = ifuture.second;
+        if (VL_UNLIKELY(!ho.invalid())) {
+            VL_DBG_MSGF("- vpi:   time=%" PRId64 " reason=%d=%s  id=%" PRId64 "\n", time,
+                        ho.cb_datap()->reason,
+                        VerilatedVpiError::strFromVpiCallbackReason(ho.cb_datap()->reason),
+                        ho.id());
+        }
+    }
+}
+
+VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
+    VerilatedVpiImp::assertOneCheck();
+    if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError;
+    return s().m_errorInfop;
+}
+
+auto VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVarBase* const vop) {
+    const std::string signalName = vop->fullname();
+    const std::string forceEnableSignalName = signalName + "__VforceEn";
+    const std::string forceValueSignalName = signalName + "__VforceVal";
+
+    vpiHandle const forceEnableSignalp  // NOLINT(misc-misplaced-const)
+        = vpi_handle_by_name(const_cast(forceEnableSignalName.c_str()), nullptr);
+    vpiHandle const forceValueSignalp  // NOLINT(misc-misplaced-const)
+        = vpi_handle_by_name(const_cast(forceValueSignalName.c_str()), nullptr);
+    VerilatedVpioVar* forceEnableSignalVop = VerilatedVpioVar::castp(forceEnableSignalp);
+    VerilatedVpioVar* forceValueSignalVop = VerilatedVpioVar::castp(forceValueSignalp);
+    if (VL_UNLIKELY(!forceEnableSignalVop)) {
+        VL_VPI_ERROR_(__FILE__, __LINE__,
+                      "%s: VPI force or release requested for '%s', but vpiHandle '%p' of enable "
+                      "signal '%s' could not be cast to VerilatedVpioVar*. Ensure signal is "
+                      "marked as forceable",
+                      __func__, signalName.c_str(), forceEnableSignalp,
+                      forceEnableSignalName.c_str());
+    }
+    if (VL_UNLIKELY(!forceValueSignalVop)) {
+        VL_VPI_ERROR_(__FILE__, __LINE__,
+                      "%s: VPI force or release requested for '%s', but vpiHandle '%p' of value "
+                      "signal '%s' could not be cast to VerilatedVpioVar*. Ensure signal is "
+                      "marked as forceable",
+                      __func__, signalName.c_str(), forceValueSignalp,
+                      forceValueSignalName.c_str());
+    }
+    return std::pair{vopGuard_t{forceEnableSignalVop, releaseVop},
+                                             vopGuard_t{forceValueSignalVop, releaseVop}};
+}
+
+std::size_t VerilatedVpiImp::vlTypeSize(const VerilatedVarType vltype) {
+    switch (vltype) {
+    case VLVT_UINT8: return sizeof(CData); break;
+    case VLVT_UINT16: return sizeof(SData); break;
+    case VLVT_UINT32: return sizeof(IData); break;
+    case VLVT_UINT64: return sizeof(QData); break;
+    case VLVT_WDATA: return sizeof(EData); break;
+    default:  // LCOV_EXCL_START
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, vltype);
+        return 0;
+    }  // LCOV_EXCL_STOP
+}
+//======================================================================
+// VerilatedVpiError Methods
+
+const char* VerilatedVpiError::strFromVpiVal(PLI_INT32 vpiVal) VL_PURE {
+    // clang-format off
+    static const char* const names[] = {
+        "*undefined*",
+        "vpiBinStrVal",
+        "vpiOctStrVal",
+        "vpiDecStrVal",
+        "vpiHexStrVal",
+        "vpiScalarVal",
+        "vpiIntVal",
+        "vpiRealVal",
+        "vpiStringVal",
+        "vpiVectorVal",
+        "vpiStrengthVal",
+        "vpiTimeVal",
+        "vpiObjTypeVal",
+        "vpiSuppressVal",
+        "vpiShortIntVal",
+        "vpiLongIntVal",
+        "vpiShortRealVal",
+        "vpiRawTwoStateVal",
+        "vpiRawFourStateVal",
+    };
+    // clang-format on
+    if (VL_UNCOVERABLE(vpiVal < 0)) return names[0];
+    return names[(vpiVal <= vpiRawFourStateVal) ? vpiVal : 0];
+}
+const char* VerilatedVpiError::strFromVpiObjType(PLI_INT32 vpiVal) VL_PURE {
+    // clang-format off
+    static const char* const names[] = {
+        "*undefined*",
+        "vpiAlways",
+        "vpiAssignStmt",
+        "vpiAssignment",
+        "vpiBegin",
+        "vpiCase",
+        "vpiCaseItem",
+        "vpiConstant",
+        "vpiContAssign",
+        "vpiDeassign",
+        "vpiDefParam",
+        "vpiDelayControl",
+        "vpiDisable",
+        "vpiEventControl",
+        "vpiEventStmt",
+        "vpiFor",
+        "vpiForce",
+        "vpiForever",
+        "vpiFork",
+        "vpiFuncCall",
+        "vpiFunction",
+        "vpiGate",
+        "vpiIf",
+        "vpiIfElse",
+        "vpiInitial",
+        "vpiIntegerVar",
+        "vpiInterModPath",
+        "vpiIterator",
+        "vpiIODecl",
+        "vpiMemory",
+        "vpiMemoryWord",
+        "vpiModPath",
+        "vpiModule",
+        "vpiNamedBegin",
+        "vpiNamedEvent",
+        "vpiNamedFork",
+        "vpiNet",
+        "vpiNetBit",
+        "vpiNullStmt",
+        "vpiOperation",
+        "vpiParamAssign",
+        "vpiParameter",
+        "vpiPartSelect",
+        "vpiPathTerm",
+        "vpiPort",
+        "vpiPortBit",
+        "vpiPrimTerm",
+        "vpiRealVar",
+        "vpiReg",
+        "vpiRegBit",
+        "vpiRelease",
+        "vpiRepeat",
+        "vpiRepeatControl",
+        "vpiSchedEvent",
+        "vpiSpecParam",
+        "vpiSwitch",
+        "vpiSysFuncCall",
+        "vpiSysTaskCall",
+        "vpiTableEntry",
+        "vpiTask",
+        "vpiTaskCall",
+        "vpiTchk",
+        "vpiTchkTerm",
+        "vpiTimeVar",
+        "vpiTimeQueue",
+        "vpiUdp",
+        "vpiUdpDefn",
+        "vpiUserSystf",
+        "vpiVarSelect",
+        "vpiWait",
+        "vpiWhile",
+        "vpiCondition",
+        "vpiDelay",
+        "vpiElseStmt",
+        "vpiForIncStmt",
+        "vpiForInitStmt",
+        "vpiHighConn",
+        "vpiLhs",
+        "vpiIndex",
+        "vpiLeftRange",
+        "vpiLowConn",
+        "vpiParent",
+        "vpiRhs",
+        "vpiRightRange",
+        "vpiScope",
+        "vpiSysTfCall",
+        "vpiTchkDataTerm",
+        "vpiTchkNotifier",
+        "vpiTchkRefTerm",
+        "vpiArgument",
+        "vpiBit",
+        "vpiDriver",
+        "vpiInternalScope",
+        "vpiLoad",
+        "vpiModDataPathIn",
+        "vpiModPathIn",
+        "vpiModPathOut",
+        "vpiOperand",
+        "vpiPortInst",
+        "vpiProcess",
+        "vpiVariables",
+        "vpiUse",
+        "vpiExpr",
+        "vpiPrimitive",
+        "vpiStmt",
+        "vpiAttribute",
+        "vpiBitSelect",
+        "vpiCallback",
+        "vpiDelayTerm",
+        "vpiDelayDevice",
+        "vpiFrame",
+        "vpiGateArray",
+        "vpiModuleArray",
+        "vpiPrimitiveArray",
+        "vpiNetArray",
+        "vpiRange",
+        "vpiRegArray",
+        "vpiSwitchArray",
+        "vpiUdpArray",
+        "vpiActiveTimeFormat",
+        "vpiInTerm",
+        "vpiInstanceArray",
+        "vpiLocalDriver",
+        "vpiLocalLoad",
+        "vpiOutTerm",
+        "vpiPorts",
+        "vpiSimNet",
+        "vpiTaskFunc",
+        "vpiContAssignBit",
+        "vpiNamedEventArray",
+        "vpiIndexedPartSelect",
+        "vpiBaseExpr",
+        "vpiWidthExpr",
+        "vpiGenScopeArray",
+        "vpiGenScope",
+        "vpiGenVar",
+        "vpiAutomatics"
+    };
+    static const char* const sv_names1[] = {
+        "vpiPackage",
+        "vpiInterface",
+        "vpiProgram",
+        "vpiInterfaceArray",
+        "vpiProgramArray",
+        "vpiTypespec",
+        "vpiModport",
+        "vpiInterfaceTfDecl",
+        "vpiRefObj",
+        "vpiTypeParameter",
+        "vpiLongIntVar",
+        "vpiShortIntVar",
+        "vpiIntVar",
+        "vpiShortRealVar",
+        "vpiByteVar",
+        "vpiClassVar",
+        "vpiStringVar",
+        "vpiEnumVar",
+        "vpiStructVar",
+        "vpiUnionVar",
+        "vpiBitVar",
+        "vpiClassObj",
+        "vpiChandleVar",
+        "vpiPackedArrayVar",
+        "*undefined*",  // 624 is not defined for object types
+        "vpiLongIntTypespec",
+        "vpiShortRealTypespec",
+        "vpiByteTypespec",
+        "vpiShortIntTypespec",
+        "vpiIntTypespec",
+        "vpiClassTypespec",
+        "vpiStringTypespec",
+        "vpiChandleTypespec",
+        "vpiEnumTypespec",
+        "vpiEnumConst",
+        "vpiIntegerTypespec",
+        "vpiTimeTypespec",
+        "vpiRealTypespec",
+        "vpiStructTypespec",
+        "vpiUnionTypespec",
+        "vpiBitTypespec",
+        "vpiLogicTypespec",
+        "vpiArrayTypespec",
+        "vpiVoidTypespec",
+        "vpiTypespecMember",
+        "vpiDistItem",
+        "vpiAliasStmt",
+        "vpiThread",
+        "vpiMethodFuncCall",
+        "vpiMethodTaskCall",
+        "vpiClockingBlock",
+        "vpiClockingIODecl",
+        "vpiClassDefn",
+        "vpiConstraint",
+        "vpiConstraintOrdering",
+        "vpiPropertyDecl",
+        "vpiPropertySpec",
+        "vpiPropertyExpr",
+        "vpiMulticlockSequenceExpr",
+        "vpiClockedSeq",
+        "vpiPropertyInst",
+        "vpiSequenceDecl",
+        "vpiCaseProperty",
+        "*undefined*", // 663 is not defined for object types
+        "vpiSequenceInst",
+        "vpiImmediateAssert",
+        "vpiReturn",
+        "vpiAnyPattern",
+        "vpiTaggedPattern",
+        "vpiStructPattern",
+        "vpiDoWhile",
+        "vpiOrderedWait",
+        "vpiWaitFork",
+        "vpiDisableFork",
+        "vpiExpectStmt",
+        "vpiForeachStmt",
+        "vpiFinal",
+        "vpiExtends",
+        "vpiDistribution",
+        "vpiSeqFormalDecl",
+        "vpiEnumNet",
+        "vpiIntegerNet",
+        "vpiTimeNet",
+        "vpiStructNet",
+        "vpiBreak",
+        "vpiContinue",
+        "vpiAssert",
+        "vpiAssume",
+        "vpiCover",
+        "vpiDisableCondition",
+        "vpiClockingEvent",
+        "vpiReturnStmt",
+        "vpiPackedArrayTypespec",
+        "vpiPackedArrayNet",
+        "vpiImmediateAssume",
+        "vpiImmediateCover",
+        "vpiSequenceTypespec",
+        "vpiPropertyTypespec",
+        "vpiEventTypespec",
+        "vpiPropFormalDecl",
+    };
+    // clang-format on
+    if (VL_UNCOVERABLE(vpiVal < 0))
+        return names[0];
+    else if (vpiVal <= vpiAutomatics)
+        return names[vpiVal];
+    else if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl)
+        return sv_names1[(vpiVal - vpiPackage)];
+    else
+        return names[0];
+}
+const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE {
+    // clang-format off
+    static const char* const names[] = {
+        "vpiCondition",
+        "vpiDelay",
+        "vpiElseStmt",
+        "vpiForIncStmt",
+        "vpiForInitStmt",
+        "vpiHighConn",
+        "vpiLhs",
+        "vpiIndex",
+        "vpiLeftRange",
+        "vpiLowConn",
+        "vpiParent",
+        "vpiRhs",
+        "vpiRightRange",
+        "vpiScope",
+        "vpiSysTfCall",
+        "vpiTchkDataTerm",
+        "vpiTchkNotifier",
+        "vpiTchkRefTerm",
+        "vpiArgument",
+        "vpiBit",
+        "vpiDriver",
+        "vpiInternalScope",
+        "vpiLoad",
+        "vpiModDataPathIn",
+        "vpiModPathIn",
+        "vpiModPathOut",
+        "vpiOperand",
+        "vpiPortInst",
+        "vpiProcess",
+        "vpiVariables",
+        "vpiUse",
+        "vpiExpr",
+        "vpiPrimitive",
+        "vpiStmt"
+    };
+    // clang-format on
+    if (vpiVal > vpiStmt || vpiVal < vpiCondition) return "*undefined*";
+    return names[vpiVal - vpiCondition];
+}
+
+const char* VerilatedVpiError::strFromVpiCallbackReason(PLI_INT32 vpiVal) VL_PURE {
+    // clang-format off
+    static const char* const names[] = {
+        "*undefined*",
+        "cbValueChange",
+        "cbStmt",
+        "cbForce",
+        "cbRelease",
+        "cbAtStartOfSimTime",
+        "cbReadWriteSynch",
+        "cbReadOnlySynch",
+        "cbNextSimTime",
+        "cbAfterDelay",
+        "cbEndOfCompile",
+        "cbStartOfSimulation",
+        "cbEndOfSimulation",
+        "cbError",
+        "cbTchkViolation",
+        "cbStartOfSave",
+        "cbEndOfSave",
+        "cbStartOfRestart",
+        "cbEndOfRestart",
+        "cbStartOfReset",
+        "cbEndOfReset",
+        "cbEnterInteractive",
+        "cbExitInteractive",
+        "cbInteractiveScopeChange",
+        "cbUnresolvedSystf",
+        "cbAssign",
+        "cbDeassign",
+        "cbDisable",
+        "cbPLIError",
+        "cbSignal",
+        "cbNBASynch",
+        "cbAtEndOfSimTime"
+    };
+    // clang-format on
+    if (VL_UNCOVERABLE(vpiVal < 0)) return names[0];
+    return names[(vpiVal <= cbAtEndOfSimTime) ? vpiVal : 0];
+}
+
+const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_PURE {
+    // clang-format off
+    static const char* const names[] = {
+        "*undefined or other*",
+        "vpiType",
+        "vpiName",
+        "vpiFullName",
+        "vpiSize",
+        "vpiFile",
+        "vpiLineNo",
+        "vpiTopModule",
+        "vpiCellInstance",
+        "vpiDefName",
+        "vpiProtected",
+        "vpiTimeUnit",
+        "vpiTimePrecision",
+        "vpiDefNetType",
+        "vpiUnconnDrive",
+        "vpiDefFile",
+        "vpiDefLineNo",
+        "vpiScalar",
+        "vpiVector",
+        "vpiExplicitName",
+        "vpiDirection",
+        "vpiConnByName",
+        "vpiNetType",
+        "vpiExplicitScalared",
+        "vpiExplicitVectored",
+        "vpiExpanded",
+        "vpiImplicitDecl",
+        "vpiChargeStrength",
+        "vpiArray",
+        "vpiPortIndex",
+        "vpiTermIndex",
+        "vpiStrength0",
+        "vpiStrength1",
+        "vpiPrimType",
+        "vpiPolarity",
+        "vpiDataPolarity",
+        "vpiEdge",
+        "vpiPathType",
+        "vpiTchkType",
+        "vpiOpType",
+        "vpiConstType",
+        "vpiBlocking",
+        "vpiCaseType",
+        "vpiFuncType",
+        "vpiNetDeclAssign",
+        "vpiUserDefn",
+        "vpiScheduled",
+        "*undefined*",
+        "*undefined*",
+        "vpiActive",
+        "vpiAutomatic",
+        "vpiCell",
+        "vpiConfig",
+        "vpiConstantSelect",
+        "vpiDecompile",
+        "vpiDefAttribute",
+        "vpiDelayType",
+        "vpiIteratorType",
+        "vpiLibrary",
+        "*undefined*",
+        "vpiOffset",
+        "vpiResolvedNetType",
+        "vpiSaveRestartID",
+        "vpiSaveRestartLocation",
+        "vpiValid",
+        "vpiSigned",
+        "vpiStop",
+        "vpiFinish",
+        "vpiReset",
+        "vpiSetInteractiveScope",
+        "vpiLocalParam",
+        "vpiModPathHasIfNone",
+        "vpiIndexedPartSelectType",
+        "vpiIsMemory",
+        "vpiIsProtected"
+    };
+    // clang-format on
+    if (vpiVal == vpiUndefined) return "vpiUndefined";
+    return names[(vpiVal <= vpiIsProtected) ? vpiVal : 0];
+}
+const char* VerilatedVpiError::strFromVpiConstType(PLI_INT32 constType) VL_PURE {
+    // clang-format off
+    static const char* const names[] = {
+        "*undefined*",
+        "vpiDecConst",
+        "vpiRealConst",
+        "vpiBinaryConst",
+        "vpiOctConst",
+        "vpiHexConst",
+        "vpiStringConst",
+        "vpiIntConst",
+        "vpiTimeConst",
+    };
+    // clang-format on
+    if (VL_UNCOVERABLE(constType < 0)) return names[0];
+    return names[(constType <= vpiTimeConst) ? constType : 0];
+}
+
+#define SELF_CHECK_RESULT_CSTR(got, exp) \
+    if (0 != std::strcmp((got), (exp))) { \
+        const std::string msg = "%Error: GOT = '"s + (got) + "'" + "  EXP = '" + (exp) + "'"; \
+        VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); \
+    }
+
+#define SELF_CHECK_ENUM_STR(fn, enumn) \
+    do { \
+        const char* const strVal = VerilatedVpiError::fn(enumn); \
+        SELF_CHECK_RESULT_CSTR(strVal, #enumn); \
+    } while (0)
+
+void VerilatedVpi::selfTest() VL_MT_UNSAFE_ONE { VerilatedVpiError::selfTest(); }
+void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
+    VerilatedVpiImp::assertOneCheck();
+
+    SELF_CHECK_ENUM_STR(strFromVpiVal, vpiBinStrVal);
+    SELF_CHECK_ENUM_STR(strFromVpiVal, vpiRawFourStateVal);
+
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAlways);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssignStmt);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssignment);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBegin);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCase);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCaseItem);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiConstant);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiContAssign);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDeassign);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDefParam);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelayControl);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDisable);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEventControl);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEventStmt);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFor);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForce);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForever);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFork);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFuncCall);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFunction);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGate);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIf);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIfElse);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInitial);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntegerVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterModPath);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIterator);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIODecl);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMemory);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMemoryWord);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModPath);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModule);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedBegin);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedEvent);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedFork);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNet);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNetBit);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNullStmt);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOperation);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiParamAssign);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiParameter);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPartSelect);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPathTerm);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPort);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPortBit);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPrimTerm);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRealVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiReg);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRegBit);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRelease);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRepeat);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRepeatControl);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSchedEvent);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSpecParam);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSwitch);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSysFuncCall);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSysTaskCall);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTableEntry);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTask);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTaskCall);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchk);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkTerm);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeQueue);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUdp);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUdpDefn);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUserSystf);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiVarSelect);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWait);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWhile);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCondition);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelay);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiElseStmt);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForIncStmt);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForInitStmt);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiHighConn);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLhs);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIndex);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLeftRange);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLowConn);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiParent);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRhs);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRightRange);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiScope);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSysTfCall);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkDataTerm);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkNotifier);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkRefTerm);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiArgument);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBit);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDriver);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInternalScope);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLoad);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModDataPathIn);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModPathIn);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModPathOut);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOperand);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPortInst);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiProcess);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiVariables);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUse);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiExpr);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPrimitive);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStmt);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAttribute);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitSelect);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCallback);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelayTerm);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelayDevice);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFrame);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGateArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModuleArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPrimitiveArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNetArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRange);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRegArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSwitchArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUdpArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiActiveTimeFormat);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInTerm);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInstanceArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLocalDriver);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLocalLoad);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOutTerm);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPorts);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSimNet);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTaskFunc);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiContAssignBit);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedEventArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIndexedPartSelect);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBaseExpr);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWidthExpr);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGenScopeArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGenScope);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGenVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAutomatics);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackage);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterface);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiProgram);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterfaceArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiProgramArray);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModport);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterfaceTfDecl);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRefObj);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypeParameter);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLongIntVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortIntVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortRealVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiByteVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStringVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassObj);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayVar);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLongIntTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortRealTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiByteTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortIntTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStringTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumConst);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntegerTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRealTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLogicTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiArrayTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiVoidTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypespecMember);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDistItem);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAliasStmt);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiThread);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMethodFuncCall);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMethodTaskCall);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingBlock);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingIODecl);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassDefn);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiConstraint);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiConstraintOrdering);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyDecl);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertySpec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyExpr);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMulticlockSequenceExpr);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockedSeq);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyInst);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceDecl);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCaseProperty);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceInst);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateAssert);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiReturn);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAnyPattern);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTaggedPattern);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructPattern);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDoWhile);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOrderedWait);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWaitFork);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDisableFork);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiExpectStmt);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForeachStmt);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFinal);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiExtends);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDistribution);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSeqFormalDecl);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumNet);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntegerNet);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeNet);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructNet);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBreak);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiContinue);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssert);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssume);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCover);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDisableCondition);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingEvent);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiReturnStmt);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayNet);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateAssume);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateCover);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEventTypespec);
+    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropFormalDecl);
+
+    SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiCondition);
+    SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiStmt);
+
+    SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbValueChange);
+    SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbAtEndOfSimTime);
+
+    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiType);
+    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiProtected);
+    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiDirection);
+    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiTermIndex);
+    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiConstType);
+    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiAutomatic);
+    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiOffset);
+    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiStop);
+    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiIsProtected);
+
+    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiDecConst);
+    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiRealConst);
+    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiBinaryConst);
+    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiOctConst);
+    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiHexConst);
+    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiStringConst);
+    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiIntConst);
+    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiTimeConst);
+}
+
+#undef SELF_CHECK_ENUM_STR
+#undef SELF_CHECK_RESULT_CSTR
+
+//======================================================================
+// callback related
+
+vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
+    // Returns handle so user can remove the callback, user must vpi_release_handle it
+    // Don't confuse with the callback-activated t_cb_data object handle
+    // which is the object causing the callback rather than the callback itself
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    // cppcheck-suppress nullPointer
+    if (VL_UNLIKELY(!cb_data_p)) {
+        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: VPI callback data pointer is null", __func__);
+        return nullptr;
+    }
+    const PLI_INT32 reason = cb_data_p->reason;
+    switch (reason) {
+    case cbAfterDelay:  // FALLTHRU // One-shot; time relative
+    case cbAtEndOfSimTime:  // FALLTHRU // One-shot; time absolute; supported via vlt_main.cpp
+    case cbAtStartOfSimTime:  // FALLTHRU // One-shot; time absolute; supported via vlt_main.cpp
+    case cbReadOnlySynch:  // FALLTHRU // One-shot; time relative; supported via vlt_main.cpp
+    case cbReadWriteSynch: {  // One-shot; time relative; supported via vlt_main.cpp
+        const bool abs = reason == cbAtStartOfSimTime || reason == cbAtEndOfSimTime;
+        const QData time = VL_TIME_Q();
+        QData abstime = 0;
+        if (cb_data_p->time) {
+            if (abs) {
+                abstime = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
+            } else {
+                abstime = time + VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
+            }
+        }
+        const uint64_t id = VerilatedVpiImp::nextCallbackId();
+        VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, abstime, reason};
+        if (abstime <= time) {
+            VerilatedVpiImp::cbCurrentAdd(id, cb_data_p);
+        } else {
+            VerilatedVpiImp::cbFutureAdd(id, cb_data_p, abstime);
+        }
+        return vop->castVpiHandle();
+    }
+    case cbNextSimTime: {  // One-shot; time always next; supported via vlt_main.cpp
+        const QData time = VL_TIME_Q();
+        const uint64_t id = VerilatedVpiImp::nextCallbackId();
+        VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, 0, reason};
+        VerilatedVpiImp::cbNextAdd(id, cb_data_p, time);
+        return vop->castVpiHandle();
+    }
+    case cbEndOfSimulation:  // FALLTHRU // One-shot; time ignored; supported via vlt_main.cpp
+    case cbEnterInteractive:  // FALLTHRU // NOP, but need to return handle, so make object
+    case cbExitInteractive:  // FALLTHRU // NOP, but need to return handle, so make object
+    case cbInteractiveScopeChange:  // FALLTHRU // NOP, but need to return handle, so make object
+    case cbPLIError:  // FALLTHRU // NOP, but need to return handle, so make object
+    case cbStartOfSimulation:  // FALLTHRU // One-shot; time ignored; supported via vlt_main.cpp
+    case cbValueChange: {  // Multi-shot; supported via vlt_main.cpp
+        const uint64_t id = VerilatedVpiImp::nextCallbackId();
+        VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, 0, reason};
+        VerilatedVpiImp::cbCurrentAdd(id, cb_data_p);
+        return vop->castVpiHandle();
+    }
+    default:
+        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported callback type %s", __func__,
+                        VerilatedVpiError::strFromVpiCallbackReason(reason));
+        return nullptr;
+    }
+}
+
+PLI_INT32 vpi_remove_cb(vpiHandle cb_obj) {
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n", cb_obj););
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    VerilatedVpio* const vop = VerilatedVpio::castp(cb_obj);
+    if (VL_UNLIKELY(!vop)) return 0;
+    return vop->dovpi_remove_cb();
+}
+
+void vpi_get_cb_info(vpiHandle /*object*/, p_cb_data /*cb_data_p*/) { VL_VPI_UNIMP_(); }
+vpiHandle vpi_register_systf(p_vpi_systf_data /*systf_data_p*/) {
+    VL_VPI_UNIMP_();
+    return nullptr;
+}
+void vpi_get_systf_info(vpiHandle /*object*/, p_vpi_systf_data /*systf_data_p*/) {
+    VL_VPI_UNIMP_();
+}
+
+// for obtaining handles
+
+vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    if (VL_UNLIKELY(!namep)) return nullptr;
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n", namep, scope););
+    const VerilatedVar* varp = nullptr;
+    const VerilatedScope* scopep;
+    const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
+    std::string scopeAndName = namep;
+    if (0 == std::strncmp(namep, "$root.", std::strlen("$root."))) {
+        namep += std::strlen("$root.");
+        scopeAndName = namep;
+    } else if (voScopep) {
+        const bool scopeIsPackage = VerilatedVpioPackage::castp(scope) != nullptr;
+        scopeAndName = std::string{voScopep->fullname()} + (scopeIsPackage ? "" : ".") + namep;
+        namep = const_cast(scopeAndName.c_str());
+    }
+    {
+        // This doesn't yet follow the hierarchy in the proper way
+        bool isPackage = false;
+        scopep = Verilated::threadContextp()->scopeFind(namep);
+        if (scopep) {  // Whole thing found as a scope
+            if (scopep->type() == VerilatedScope::SCOPE_MODULE) {
+                return (new VerilatedVpioModule{scopep})->castVpiHandle();
+            } else if (scopep->type() == VerilatedScope::SCOPE_PACKAGE) {
+                return (new VerilatedVpioPackage{scopep})->castVpiHandle();
+            } else {
+                return (new VerilatedVpioScope{scopep})->castVpiHandle();
+            }
+        }
+        std::string basename = scopeAndName;
+        std::string scopename;
+        std::string::size_type prevpos = std::string::npos;
+        std::string::size_type pos = std::string::npos;
+        // Split hierarchical names at last '.' not inside escaped identifier
+        size_t i = 0;
+        while (i < scopeAndName.length()) {
+            if (scopeAndName[i] == '\\') {
+                while (i < scopeAndName.length() && scopeAndName[i] != ' ') ++i;
+                ++i;  // Proc ' ', it should always be there. Then grab '.' on next cycle
+            } else {
+                while (i < scopeAndName.length()
+                       && (scopeAndName[i] != '.'
+                           && (i + 1 >= scopeAndName.length() || scopeAndName[i] != ':'
+                               || scopeAndName[i + 1] != ':')))
+                    ++i;
+                if (i < scopeAndName.length()) {
+                    prevpos = pos;
+                    pos = i++;
+                    if (scopeAndName[i - 1] == ':') isPackage = true;
+                }
+            }
+        }
+        // Do the split
+        if (VL_LIKELY(pos != std::string::npos)) {
+            basename.erase(0, pos + (isPackage ? 2 : 1));
+            scopename = scopeAndName.substr(0, pos);
+            if (scopename == "$unit") scopename = "\\$unit ";
+        }
+        if (prevpos == std::string::npos) {
+            // scopename is a toplevel (no '.' separator), so search in our TOP ports first.
+            scopep = Verilated::threadContextp()->scopeFind("TOP");
+            if (scopep) varp = scopep->varFind(basename.c_str());
+        }
+        if (!varp) {
+            scopep = Verilated::threadContextp()->scopeFind(scopename.c_str());
+            if (!scopep) return nullptr;
+            varp = scopep->varFind(basename.c_str());
+        }
+    }
+    if (!varp) return nullptr;
+
+    if (varp->isParam()) {
+        return (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
+    } else {
+        return (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
+    }
+}
+
+vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
+    // Used to get array entries
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_index %p %d\n", object, indx););
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object);
+    if (VL_LIKELY(varop)) {
+        // Case: no dimensions left to index
+        if (VL_UNLIKELY(varop->indexedDim() + 1 > varop->varp()->dims() - 1)) return nullptr;
+
+        // Case: index out of range
+        if (VL_UNLIKELY(indx < varop->rangep()->low() || indx > varop->rangep()->high()))
+            return nullptr;
+
+        return varop->withIndex(indx)->castVpiHandle();
+    }
+    VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__);
+    return nullptr;
+}
+
+// for traversing relationships
+
+vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle %d %p\n", type, object););
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    switch (type) {
+    case vpiLeftRange: {
+        if (const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
+            if (VL_UNLIKELY(!vop->rangep())) return nullptr;
+            return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
+        } else if (const VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
+            if (VL_UNLIKELY(!vop->rangep())) return nullptr;
+            return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
+        }
+        VL_VPI_WARNING_(__FILE__, __LINE__,
+                        "%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
+                        __func__, object, VerilatedVpiError::strFromVpiMethod(type));
+        return nullptr;
+    }
+    case vpiRightRange: {
+        if (const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
+            if (VL_UNLIKELY(!vop->rangep())) return nullptr;
+            return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
+        } else if (const VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
+            if (VL_UNLIKELY(!vop->rangep())) return nullptr;
+            return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
+        }
+        VL_VPI_WARNING_(__FILE__, __LINE__,
+                        "%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
+                        __func__, object, VerilatedVpiError::strFromVpiMethod(type));
+        return nullptr;
+    }
+    case vpiIndex: {
+        const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
+        if (VL_UNLIKELY(!vop)) return nullptr;
+        const int32_t val = vop->index().back();
+        return (new VerilatedVpioConst{val})->castVpiHandle();
+    }
+    case vpiScope: {
+        const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
+        if (VL_UNLIKELY(!vop)) return nullptr;
+        return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle();
+    }
+    case vpiParent: {
+        const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
+        if (VL_UNLIKELY(!vop)) return nullptr;
+        return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle();
+    }
+    default:
+        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
+                        __func__, VerilatedVpiError::strFromVpiMethod(type));
+        return nullptr;
+    }
+}
+
+vpiHandle vpi_handle_multi(PLI_INT32 /*type*/, vpiHandle /*refHandle1*/, vpiHandle /*refHandle2*/,
+                           ...) {
+    VL_VPI_UNIMP_();
+    return nullptr;
+}
+
+vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_iterate %d %p\n", type, object););
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    switch (type) {
+    case vpiRange: {
+        const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
+        if (VL_UNLIKELY(!vop)) return nullptr;
+
+        std::vector ranges;
+        const int maxDim = vop->maxDim(vop->isIndexedDimUnpacked());
+        for (int dim = vop->indexedDim() + 1; dim <= maxDim; ++dim)
+            ranges.emplace_back(*vop->varp()->range(dim));
+
+        // allow one more range layer (regbit)
+        if (ranges.empty()) ranges.emplace_back(VerilatedRange(0, 0));
+        return ((new VerilatedVpioRangeIter{ranges})->castVpiHandle());
+    }
+    case vpiReg: {
+        const VerilatedVpioScope* const vscopep = VerilatedVpioScope::castp(object);
+        if (vscopep) return ((new VerilatedVpioVarIter{vscopep, false})->castVpiHandle());
+        const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
+        if (vop) return ((new VerilatedVpioRegIter{vop})->castVpiHandle());
+        return nullptr;
+    }
+    case vpiParameter: {
+        const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
+        if (VL_UNLIKELY(!vop)) return nullptr;
+        return ((new VerilatedVpioVarIter{vop, true})->castVpiHandle());
+    }
+    case vpiModule: {
+        const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
+        const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
+        const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
+        const auto it = vlstd::as_const(map)->find(const_cast(modp));
+        if (it == map->end()) return nullptr;
+        return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
+    }
+    case vpiInternalScope: {
+        const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
+        const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
+        const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
+        const auto it = vlstd::as_const(map)->find(const_cast(modp));
+        if (it == map->end()) return nullptr;
+        return ((new VerilatedVpioScopeIter{it->second})->castVpiHandle());
+    }
+    case vpiInstance: {
+        if (object) return nullptr;
+        const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
+        const auto it = vlstd::as_const(map)->find(nullptr);
+        if (it == map->end()) return nullptr;
+        return ((new VerilatedVpioInstanceIter{it->second})->castVpiHandle());
+    }
+    default:
+        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
+                        __func__, VerilatedVpiError::strFromVpiObjType(type));
+        return nullptr;
+    }
+}
+vpiHandle vpi_scan(vpiHandle object) {
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n", object););
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    VerilatedVpio* const vop = VerilatedVpio::castp(object);
+    if (VL_UNLIKELY(!vop)) return nullptr;
+    return vop->dovpi_scan();
+}
+
+// for processing properties
+
+PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
+    // Leave this in the header file - in many cases the compiler can constant propagate "object"
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get %d %p\n", property, object););
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    switch (property) {
+    case vpiTimePrecision: {
+        return Verilated::threadContextp()->timeprecision();
+    }
+    case vpiTimeUnit: {
+        const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
+        if (!vop)
+            return Verilated::threadContextp()->timeunit();  // Null asks for global, not unlikely
+        return vop->scopep()->timeunit();
+    }
+    case vpiType: {
+        const VerilatedVpio* const vop = VerilatedVpio::castp(object);
+        if (VL_UNLIKELY(!vop)) return vpiUndefined;
+        return vop->type();
+    }
+    case vpiConstType: {
+        const VerilatedVpio* const vop = VerilatedVpio::castp(object);
+        if (VL_UNLIKELY(!vop)) return vpiUndefined;
+        return vop->constType();
+    }
+    case vpiDirection: {
+        // By forethought, the directions already are vpi enumerated
+        const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
+        if (VL_UNLIKELY(!vop)) return vpiUndefined;
+        return vop->varp()->vldir();
+    }
+    case vpiScalar:  // FALLTHRU
+    case vpiVector: {
+        const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
+        if (VL_UNLIKELY(!vop)) return vpiUndefined;
+        return (property == vpiVector) ^ (vop->varp()->packedRanges().empty() || !vop->rangep());
+    }
+    case vpiSize: {
+        const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
+        if (VL_UNLIKELY(!vop)) return vpiUndefined;
+        return vop->size();
+    }
+    case vpiSigned: {
+        const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
+        if (VL_UNLIKELY(!vop)) return vpiUndefined;
+        return vop->varp()->isSigned();
+    }
+    default:
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
+                      __func__, VerilatedVpiError::strFromVpiProp(property));
+        return vpiUndefined;
+    }
+}
+
+PLI_INT64 vpi_get64(PLI_INT32 /*property*/, vpiHandle /*object*/) {
+    VL_VPI_UNIMP_();
+    return vpiUndefined;
+}
+
+PLI_BYTE8* vpi_get_str(PLI_INT32 property, vpiHandle object) {
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_str %d %p\n", property, object););
+    VerilatedVpiImp::assertOneCheck();
+    const VerilatedVpio* const vop = VerilatedVpio::castp(object);
+    VL_VPI_ERROR_RESET_();
+    if (VL_UNLIKELY(!vop)) return nullptr;
+    switch (property) {
+    case vpiName: {
+        return const_cast(vop->name());
+    }
+    case vpiFullName: {
+        return const_cast(vop->fullname());
+    }
+    case vpiDefName: {
+        return const_cast(vop->defname());
+    }
+    case vpiType: {
+        return const_cast(VerilatedVpiError::strFromVpiObjType(vop->type()));
+    }
+    case vpiConstType: {
+        const PLI_INT32 constType = vpi_get(vpiConstType, object);
+        VL_VPI_ERROR_RESET_();
+        return const_cast(VerilatedVpiError::strFromVpiConstType(constType));
+    }
+    default:
+        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
+                        __func__, VerilatedVpiError::strFromVpiProp(property));
+        return nullptr;
+    }
+}
+
+// delay processing
+
+void vpi_get_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { VL_VPI_UNIMP_(); }
+void vpi_put_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { VL_VPI_UNIMP_(); }
+
+// value processing
+bool vl_check_format(const VerilatedVpioVarBase* vop, const p_vpi_value valuep, bool isGetValue) {
+    const VerilatedVar* varp = vop->varp();
+    bool status = true;
+    if ((valuep->format == vpiVectorVal) || (valuep->format == vpiBinStrVal)
+        || (valuep->format == vpiOctStrVal) || (valuep->format == vpiHexStrVal)) {
+        switch (varp->vltype()) {
+        case VLVT_UINT8:
+        case VLVT_UINT16:
+        case VLVT_UINT32:
+        case VLVT_UINT64:
+        case VLVT_WDATA: return status;
+        default: status = false;  // LCOV_EXCL_LINE
+        }
+    } else if (valuep->format == vpiDecStrVal) {
+        switch (varp->vltype()) {
+        case VLVT_UINT8:
+        case VLVT_UINT16:
+        case VLVT_UINT32:
+        case VLVT_UINT64: return status;
+        default: status = false;  // LCOV_EXCL_LINE
+        }
+    } else if (valuep->format == vpiStringVal) {
+        switch (varp->vltype()) {
+        case VLVT_UINT8:
+        case VLVT_UINT16:
+        case VLVT_UINT32:
+        case VLVT_UINT64:
+        case VLVT_WDATA: return status;
+        case VLVT_STRING:
+            // string parameter values can't be changed
+            if (isGetValue || !varp->isParam()) {
+                return status;
+            } else {
+                status = false;
+                break;
+            }
+        default: status = false;  // LCOV_EXCL_LINE
+        }
+    } else if (valuep->format == vpiIntVal) {
+        switch (varp->vltype()) {
+        case VLVT_UINT8:
+        case VLVT_UINT16:
+        case VLVT_UINT32:
+        case VLVT_UINT64:
+        case VLVT_WDATA: return status;
+        default: status = false;  // LCOV_EXCL_LINE
+        }
+    } else if (valuep->format == vpiRealVal) {
+        switch (varp->vltype()) {
+        case VLVT_REAL: return status;
+        default: status = false;  // LCOV_EXCL_LINE
+        }
+    } else if (valuep->format == vpiScalarVal) {
+        switch (varp->vltype()) {
+        case VLVT_UINT8:
+        case VLVT_UINT16:
+        case VLVT_UINT32:
+        case VLVT_UINT64:
+        case VLVT_WDATA: return status;
+        default: status = false;  // LCOV_EXCL_LINE
+        }
+    } else if (valuep->format == vpiSuppressVal) {
+        return status;
+    } else {
+        status = false;
+    }
+    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", __func__,
+                  VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
+    return status;
+}
+
+static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
+    va_list args, args_copy;
+    va_start(args, fmt);
+    buffer.clear();
+    // Make copy of args since we may need to call VL_VSNPRINTF more than once
+    va_copy(args_copy, args);
+    // Try VL_VSNPRINTF in existing buffer
+    const int result
+        = VL_VSNPRINTF(const_cast(buffer.data()), buffer.capacity(), fmt, args_copy);
+    va_end(args_copy);
+    const int required = result + 1;  // Returned size doesn't include NUL terminator
+    // If there wasn't enough space, reallocate and try again
+    if (buffer.capacity() < required) {
+        buffer.reserve(required * 2);
+        VL_VSNPRINTF(const_cast(buffer.data()), buffer.capacity(), fmt, args);
+    }
+    va_end(args);
+}
+
+// Information about how to access packed array data.
+// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word
+// boundaries, in which case m_maskHi != 0.
+template 
+struct VarAccessInfo final {
+    T* m_datap;  // Typed pointer to packed array base address
+    size_t m_bitOffset;  // Data start location (bit offset)
+    size_t m_wordOffset;  // Data start location (word offset, VLVT_WDATA only)
+    T m_maskLo;  // Access mask for m_datap[m_wordOffset]
+    T m_maskHi;  // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only)
+};
+
+template 
+VarAccessInfo vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount,
+                                        size_t addOffset) {
+    // VarAccessInfo generation
+    // vop - variable to access (already indexed)
+    // bitCount - how many bits to write/read
+    // addOffset - additional offset to apply (within the packed array element)
+
+    const size_t wordBits = sizeof(T) * 8;
+    uint32_t varBits = vop->bitSize();
+
+    if (vop->varp()->vltype() == VLVT_REAL) varBits *= sizeof(double) * 8;
+
+    // make sure we're not trying to write outside var bounds
+    assert(varBits > addOffset);
+    bitCount = std::min(bitCount, varBits - addOffset);
+
+    VarAccessInfo info;
+    info.m_datap = reinterpret_cast(vop->varDatap());
+    if (vop->varp()->vltype() == VLVT_WDATA) {
+        assert(sizeof(T) == sizeof(EData));
+        assert(bitCount <= wordBits);
+        info.m_wordOffset = (vop->bitOffset() + addOffset) / wordBits;
+        info.m_bitOffset = (vop->bitOffset() + addOffset) % wordBits;
+        if (bitCount + info.m_bitOffset <= wordBits) {
+            // within single word
+            if (bitCount == wordBits)
+                info.m_maskLo = ~static_cast(0);
+            else
+                info.m_maskLo = (static_cast(1) << bitCount) - 1;
+            info.m_maskLo = info.m_maskLo << info.m_bitOffset;
+            info.m_maskHi = 0;
+        } else {
+            // straddles word boundary
+            info.m_maskLo = (static_cast(1) << (wordBits - info.m_bitOffset)) - 1;
+            info.m_maskLo = info.m_maskLo << info.m_bitOffset;
+            info.m_maskHi = (static_cast(1) << (bitCount + info.m_bitOffset - wordBits)) - 1;
+        }
+    } else {
+        info.m_wordOffset = 0;
+        info.m_bitOffset = vop->bitOffset() + addOffset;
+        assert(bitCount + info.m_bitOffset <= wordBits);
+        if (bitCount < wordBits) {
+            info.m_maskLo = (static_cast(1) << bitCount) - 1;
+            info.m_maskLo = info.m_maskLo << info.m_bitOffset;
+        } else {
+            info.m_maskLo = ~static_cast(0);
+        }
+        info.m_maskHi = 0;
+    }
+    return info;
+}
+
+template 
+T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
+    const size_t wordBits = sizeof(T) * 8;
+    const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset);
+    if (info.m_maskHi)
+        return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset)
+               | ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi)
+                  << (wordBits - info.m_bitOffset));
+    else
+        return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset;
+}
+
+template 
+void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) {
+    const size_t wordBits = sizeof(T) * 8;
+    const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset);
+
+    if (info.m_maskHi) {
+        info.m_datap[info.m_wordOffset + 1]
+            = (info.m_datap[info.m_wordOffset + 1] & ~info.m_maskHi)
+              | ((word >> (wordBits - info.m_bitOffset)) & info.m_maskHi);
+    }
+    // cppcheck-suppress unreadVariable
+    info.m_datap[info.m_wordOffset] = (info.m_datap[info.m_wordOffset] & ~info.m_maskLo)
+                                      | ((word << info.m_bitOffset) & info.m_maskLo);
+}
+
+// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds
+// addOffset: additional read bitoffset
+QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
+    switch (vop->varp()->vltype()) {
+    case VLVT_UINT8: return vl_vpi_get_word_gen(vop, bitCount, addOffset);
+    case VLVT_UINT16: return vl_vpi_get_word_gen(vop, bitCount, addOffset);
+    case VLVT_UINT32: return vl_vpi_get_word_gen(vop, bitCount, addOffset);
+    case VLVT_UINT64: return vl_vpi_get_word_gen(vop, bitCount, addOffset);
+    case VLVT_WDATA: return vl_vpi_get_word_gen(vop, bitCount, addOffset);
+    default:
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__,
+                      vop->varp()->vltype());
+        return 0;
+    }
+}
+
+// word: data to be written
+// bitCount: maximum number of bits to write, will stop earlier if it reaches the var bounds
+// addOffset: additional write bitoffset
+void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset) {
+    switch (vop->varp()->vltype()) {
+    case VLVT_UINT8: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break;
+    case VLVT_UINT16: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break;
+    case VLVT_UINT32: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break;
+    case VLVT_UINT64: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break;
+    case VLVT_WDATA: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break;
+    default:
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__,
+                      vop->varp()->vltype());
+    }
+}
+
+void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
+    const VerilatedVar* const varp = vop->varp();
+    void* const varDatap = vop->varDatap();
+
+    if (!vl_check_format(vop, valuep, true)) return;
+    // string data type is dynamic and may vary in size during simulation
+    static thread_local std::string t_outDynamicStr;
+
+    const int varBits = vop->bitSize();
+
+    // __VforceRd already has the correct value, but that signal is not public and thus not
+    // present in the scope's m_varsp map, so its value has to be recreated using the __VforceEn
+    // and __VforceVal signals.
+    // TODO: Implement a way to retrieve __VforceRd, rather than needing to recreate it.
+    const auto forceControlSignals
+        = vop->varp()->isForceable()
+              ? VerilatedVpiImp::getForceControlSignals(vop)
+              : std::pair{
+                    VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop},
+                    VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop}};
+    const VerilatedVpioVarBase* const forceEnableSignalVop = forceControlSignals.first.get();
+    const VerilatedVpioVarBase* const forceValueSignalVop = forceControlSignals.second.get();
+    t_vpi_error_info getForceControlSignalsError{};
+    const bool errorOccurred = vpi_chk_error(&getForceControlSignalsError);
+    // LCOV_EXCL_START - Cannot test, since getForceControlSignals does not (currently) produce
+    // any notices or warnings.
+    if (errorOccurred && getForceControlSignalsError.level < vpiError) {
+        vpi_printf(getForceControlSignalsError.message);
+        VL_VPI_ERROR_RESET_();
+    }  // LCOV_EXCL_STOP
+    // NOLINTNEXTLINE(readability-simplify-boolean-expr);
+    if (VL_UNLIKELY(
+            (errorOccurred && getForceControlSignalsError.level >= vpiError)
+            || (vop->varp()->isForceable() && (!forceEnableSignalVop || !forceValueSignalVop)))) {
+
+        // Check if getForceControlSignals provided any additional error info
+        const bool gotErrorMessage = vpi_chk_error(&getForceControlSignalsError);
+        const std::string previousErrorMessage
+            = gotErrorMessage
+                  ? std::string{" Error message: "} + getForceControlSignalsError.message
+                  : "";
+
+        VL_VPI_ERROR_(__FILE__, __LINE__,
+                      "%s: Signal '%s' is marked forceable, but force "
+                      "control signals could not be retrieved.%s",
+                      __func__, vop->fullname(),
+                      gotErrorMessage ? previousErrorMessage.c_str() : "");
+        return;
+    }
+
+    const std::function getForceableSignalWord
+        = [forceEnableSignalVop, forceValueSignalVop](const VerilatedVpioVarBase* baseSignalVop,
+                                                      size_t bitCount, size_t addOffset) -> QData {
+        // variables are QData, even though signals may have different representation, because any
+        // extraneous bits are simply truncated upon implicit casting when this function is called.
+        const QData baseSignalData = vl_vpi_get_word(baseSignalVop, bitCount, addOffset);
+        const QData forceEnableData = vl_vpi_get_word(forceEnableSignalVop, bitCount, addOffset);
+        const QData forceValueData = vl_vpi_get_word(forceValueSignalVop, bitCount, addOffset);
+        const QData readData
+            = (forceEnableData & forceValueData) | (~forceEnableData & baseSignalData);
+        return readData;
+    };
+
+    const std::function get_word
+        = vop->varp()->isForceable() ? getForceableSignalWord : vl_vpi_get_word;
+
+    // We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
+    // This may cause backward compatibility issues with older code.
+    if (valuep->format == vpiVectorVal) {
+        // Vector pointer must come from our memory pool
+        // It only needs to persist until the next vpi_get_value
+        static thread_local t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2];
+        valuep->value.vector = t_out;
+        if (varp->vltype() == VLVT_WDATA) {
+            const int words = VL_WORDS_I(varBits);
+            if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) {
+                VL_VPI_ERROR_(
+                    __FILE__, __LINE__,
+                    "vpi_get_value with more than VL_VALUE_STRING_MAX_WORDS; increase and "
+                    "recompile");
+                return;
+            }
+            for (int i = 0; i < words; ++i) {
+                t_out[i].aval = get_word(vop, 32, i * 32);
+                t_out[i].bval = 0;
+            }
+            return;
+        } else if (varp->vltype() == VLVT_UINT64 && varBits > 32) {
+            const QData data = get_word(vop, 64, 0);
+            t_out[1].aval = static_cast(data >> 32ULL);
+            t_out[1].bval = 0;
+            t_out[0].aval = static_cast(data);
+            t_out[0].bval = 0;
+            return;
+        } else {
+            t_out[0].aval = get_word(vop, 32, 0);
+            t_out[0].bval = 0;
+            return;
+        }
+    } else if (valuep->format == vpiBinStrVal) {
+        t_outDynamicStr.resize(varBits);
+
+        static thread_local std::vector forceReadCData;
+        forceReadCData
+            = vop->varp()->isForceable()
+                  ? VerilatedVpiImp::createReadDataVector(
+                        varDatap,
+                        {forceEnableSignalVop->varDatap(), forceValueSignalVop->varDatap()},
+                        vop->bitSize())
+                  : std::vector{};
+        const uint8_t* const varCDatap = vop->varp()->isForceable()
+                                             ? forceReadCData.data()
+                                             : reinterpret_cast(varDatap);
+
+        const CData* datap = varCDatap;
+        for (size_t i = 0; i < varBits; ++i) {
+            const size_t pos = i + vop->bitOffset();
+            const char val = (datap[pos >> 3] >> (pos & 7)) & 1;
+            t_outDynamicStr[varBits - i - 1] = val ? '1' : '0';
+        }
+        valuep->value.str = const_cast(t_outDynamicStr.c_str());
+        return;
+    } else if (valuep->format == vpiOctStrVal) {
+        const int chars = (varBits + 2) / 3;
+        t_outDynamicStr.resize(chars);
+        for (size_t i = 0; i < chars; ++i) {
+            const char val = get_word(vop, 3, i * 3);
+            t_outDynamicStr[chars - i - 1] = '0' + val;
+        }
+        valuep->value.str = const_cast(t_outDynamicStr.c_str());
+        return;
+    } else if (valuep->format == vpiDecStrVal) {
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_strprintf(t_outDynamicStr, "%hhu", static_cast(get_word(vop, 8, 0)));
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_strprintf(t_outDynamicStr, "%hu",
+                         static_cast(get_word(vop, 16, 0)));
+        } else if (varp->vltype() == VLVT_UINT32) {
+            vl_strprintf(t_outDynamicStr, "%u", static_cast(get_word(vop, 32, 0)));
+        } else if (varp->vltype() == VLVT_UINT64) {
+            vl_strprintf(t_outDynamicStr, "%llu",  // lintok-format-ll
+                         static_cast(get_word(vop, 64, 0)));
+        }
+        valuep->value.str = const_cast(t_outDynamicStr.c_str());
+        return;
+    } else if (valuep->format == vpiHexStrVal) {
+        const int chars = (varBits + 3) >> 2;
+        t_outDynamicStr.resize(chars);
+        for (size_t i = 0; i < chars; ++i) {
+            const char val = get_word(vop, 4, i * 4);
+            t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast(val)];
+        }
+        valuep->value.str = const_cast(t_outDynamicStr.c_str());
+        return;
+    } else if (valuep->format == vpiStringVal) {
+        if (varp->vltype() == VLVT_STRING) {
+            if (varp->isParam()) {
+                valuep->value.str = reinterpret_cast(varDatap);
+                return;
+            } else {
+                t_outDynamicStr = *vop->varStringDatap();
+                valuep->value.str = const_cast(t_outDynamicStr.c_str());
+                return;
+            }
+        } else {
+            const int chars = VL_BYTES_I(varBits);
+            t_outDynamicStr.resize(chars);
+            for (size_t i = 0; i < chars; ++i) {
+                const char val = get_word(vop, 8, i * 8);
+                // other simulators replace [leading?] zero chars with spaces, replicate here.
+                t_outDynamicStr[chars - i - 1] = val ? val : ' ';
+            }
+            valuep->value.str = const_cast(t_outDynamicStr.c_str());
+            return;
+        }
+    } else if (valuep->format == vpiIntVal) {
+        valuep->value.integer = get_word(vop, 32, 0);
+        return;
+    } else if (valuep->format == vpiRealVal) {
+        // Only cover the scalar case, since reals cannot be packed (IEEE 1800, section 7.4.1), and
+        // unpacked arrays are not supported for forcing in Verilator (#4735).
+        if (vop->varp()->isForceable() && *forceEnableSignalVop->varCDatap())
+            valuep->value.real = *forceValueSignalVop->varRealDatap();
+        else
+            valuep->value.real = *vop->varRealDatap();
+
+        return;
+    } else if (valuep->format == vpiScalarVal) {
+        valuep->value.scalar = get_word(vop, 32, 0) ? vpi1 : vpi0;
+        return;
+    } else if (valuep->format == vpiSuppressVal) {
+        return;
+    }
+    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
+                  VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
+}
+
+void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value %p\n", object););
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    if (VL_UNLIKELY(!valuep)) return;
+
+    if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
+        vl_vpi_get_value(vop, valuep);
+        return;
+    } else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
+        vl_vpi_get_value(vop, valuep);
+        return;
+    } else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
+        if (valuep->format == vpiIntVal) {
+            valuep->value.integer = vop->num();
+            return;
+        }
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", __func__,
+                      VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
+        return;
+    }
+    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
+}
+
+vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/,
+                        PLI_INT32 flags) {
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep););
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    if (VL_UNLIKELY(!valuep)) {
+        VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
+        return nullptr;
+    }
+    const PLI_INT32 delay_mode = flags & 0xfff;
+    const PLI_INT32 forceFlag = flags & 0xfff;
+    if (const VerilatedVpioVar* const baseSignalVop = VerilatedVpioVar::castp(object)) {
+        VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi:   vpi_put_value name=%s fmt=%d vali=%d\n",
+                                    baseSignalVop->fullname(), valuep->format,
+                                    valuep->value.integer);
+                        VL_DBG_MSGF("- vpi:   varp=%p  putatp=%p\n",
+                                    baseSignalVop->varp()->datap(), baseSignalVop->varDatap()););
+
+        if (VL_UNLIKELY(!baseSignalVop->varp()->isPublicRW())) {
+            VL_VPI_ERROR_(__FILE__, __LINE__,
+                          "vpi_put_value was used on signal marked read-only,"
+                          " use public_flat_rw instead for '%s'",
+                          baseSignalVop->fullname());
+            return nullptr;
+        }
+
+        // NOLINTNEXTLINE(readability-simplify-boolean-expr);
+        if (VL_UNLIKELY((forceFlag == vpiForceFlag || forceFlag == vpiReleaseFlag)
+                        && !baseSignalVop->varp()->isForceable())) {
+            VL_VPI_ERROR_("", 0, "vpi_put_value used with %s on non-forceable signal '%s'",
+                          forceFlag == vpiForceFlag ? "vpiForceFlag" : "vpiReleaseFlag",
+                          baseSignalVop->fullname());
+            return nullptr;
+        }
+        if (!vl_check_format(baseSignalVop, valuep, false)) return nullptr;
+        if (delay_mode == vpiInertialDelay) {
+            if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
+                VL_VPI_WARNING_(
+                    __FILE__, __LINE__,
+                    "%s: Unsupported p_vpi_value as requested for '%s' with vpiInertialDelay",
+                    __func__, baseSignalVop->fullname());
+                return nullptr;
+            }
+            VerilatedVpiImp::inertialDelay(baseSignalVop, valuep);
+            return object;
+        }
+        VerilatedVpiImp::evalNeeded(true);
+        const int varBits = baseSignalVop->bitSize();
+
+        const auto forceControlSignals
+            = baseSignalVop->varp()->isForceable()
+                      && (forceFlag == vpiForceFlag || forceFlag == vpiReleaseFlag)
+                  ? VerilatedVpiImp::getForceControlSignals(baseSignalVop)
+                  : std::pair{
+                        VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop},
+                        VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop}};
+        const VerilatedVpioVar* const forceEnableSignalVop = forceControlSignals.first.get();
+        const VerilatedVpioVar* const forceValueSignalVop = forceControlSignals.second.get();
+        t_vpi_error_info getForceControlSignalsError{};
+        bool errorOccurred = vpi_chk_error(&getForceControlSignalsError);
+        // LCOV_EXCL_START - Cannot test, since getForceControlSignals does not (currently) produce
+        // any notices or warnings.
+        if (errorOccurred && getForceControlSignalsError.level < vpiError) {
+            vpi_printf(getForceControlSignalsError.message);
+            VL_VPI_ERROR_RESET_();
+        }  // LCOV_EXCL_STOP
+        // NOLINTNEXTLINE(readability-simplify-boolean-expr);
+        if (VL_UNLIKELY(baseSignalVop->varp()->isForceable()
+                        && (forceFlag == vpiForceFlag || forceFlag == vpiReleaseFlag)
+                        && (!forceEnableSignalVop || !forceValueSignalVop))) {
+
+            // Check if getForceControlSignals provided any additional error info
+            const bool gotErrorMessage = vpi_chk_error(&getForceControlSignalsError);
+            const std::string previousErrorMessage
+                = gotErrorMessage
+                      ? std::string{" Error message: "} + getForceControlSignalsError.message
+                      : "";
+
+            VL_VPI_ERROR_(__FILE__, __LINE__,
+                          "%s: Signal '%s' with vpiHandle '%p' is marked forceable, but force "
+                          "control signals could not be retrieved.%s",
+                          __func__, baseSignalVop->fullname(), object,
+                          gotErrorMessage ? previousErrorMessage.c_str() : "");
+            return nullptr;
+        }
+
+        const VerilatedVpioVar* const valueVop
+            = (forceFlag == vpiForceFlag) ? forceValueSignalVop : baseSignalVop;
+
+        if (forceFlag == vpiForceFlag) {
+            // Enable __VforceEn
+            VerilatedVpiImp::setAllBitsToValue(forceEnableSignalVop, 1);
+        }
+        if (forceFlag == vpiReleaseFlag) {
+            // If signal is continuously assigned, first clear the force enable bits, then get the
+            // (non-forced) value. Else, get the (still forced) value first, then clear the force
+            // enable bits.
+
+            if (baseSignalVop->varp()->isContinuously())
+                VerilatedVpiImp::setAllBitsToValue(forceEnableSignalVop, 0);
+
+            vl_vpi_get_value(baseSignalVop, valuep);
+
+            t_vpi_error_info baseValueGetError{};
+            errorOccurred = vpi_chk_error(&baseValueGetError);
+            // LCOV_EXCL_START - Cannot test, because missing signal would already trigger error
+            // earlier, at the getForceControlSignals stage
+            // NOLINTNEXTLINE(readability-simplify-boolean-expr);
+            if (VL_UNLIKELY(errorOccurred && baseValueGetError.level >= vpiError)) {
+                const std::string baseValueSignalName = baseSignalVop->fullname();
+                const std::string previousErrorMessage = baseValueGetError.message;
+                VL_VPI_ERROR_(__FILE__, __LINE__,
+                              "%s: Could not retrieve value of signal '%s' with "
+                              "vpiHandle '%p'. Error message: %s",
+                              __func__, baseValueSignalName.c_str(), object,
+                              previousErrorMessage.c_str());
+                return nullptr;
+            }
+            // NOLINTNEXTLINE(readability-simplify-boolean-expr);
+            if (VL_UNCOVERABLE(errorOccurred && baseValueGetError.level < vpiError)) {
+                vpi_printf(baseValueGetError.message);
+                VL_VPI_ERROR_RESET_();
+            }  // LCOV_EXCL_STOP
+
+            if (!baseSignalVop->varp()->isContinuously())
+                VerilatedVpiImp::setAllBitsToValue(forceEnableSignalVop, 0);
+
+            return nullptr;
+        }
+
+        if (valuep->format == vpiVectorVal) {
+            if (VL_UNLIKELY(!valuep->value.vector)) return nullptr;
+            if (valueVop->varp()->vltype() == VLVT_WDATA) {
+                const int words = VL_WORDS_I(varBits);
+                for (int i = 0; i < words; ++i)
+                    vl_vpi_put_word(valueVop, valuep->value.vector[i].aval, 32, i * 32);
+                return object;
+            } else if (valueVop->varp()->vltype() == VLVT_UINT64 && varBits > 32) {
+                const QData val = (static_cast(valuep->value.vector[1].aval) << 32)
+                                  | static_cast(valuep->value.vector[0].aval);
+                vl_vpi_put_word(valueVop, val, 64, 0);
+                return object;
+            } else {
+                vl_vpi_put_word(valueVop, valuep->value.vector[0].aval, 32, 0);
+                return object;
+            }
+        } else if (valuep->format == vpiBinStrVal) {
+            const int len = std::strlen(valuep->value.str);
+            CData* const datap = reinterpret_cast(valueVop->varDatap());
+            for (int i = 0; i < varBits; ++i) {
+                const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1');
+                const size_t pos = valueVop->bitOffset() + i;
+
+                if (set)
+                    datap[pos >> 3] |= 1 << (pos & 7);
+                else
+                    datap[pos >> 3] &= ~(1 << (pos & 7));
+            }
+            return object;
+        } else if (valuep->format == vpiOctStrVal) {
+            const int len = std::strlen(valuep->value.str);
+            for (int i = 0; i < len; ++i) {
+                unsigned char digit = valuep->value.str[len - i - 1] - '0';
+                if (digit > 7) {  // If str was < '0', then as unsigned, digit > 7
+                    VL_VPI_WARNING_(__FILE__, __LINE__,
+                                    "%s: Non octal character '%c' in '%s' as value %s for %s",
+                                    __func__, digit + '0', valuep->value.str,
+                                    VerilatedVpiError::strFromVpiVal(valuep->format),
+                                    valueVop->fullname());
+                    digit = 0;
+                }
+                vl_vpi_put_word(valueVop, digit, 3, i * 3);
+            }
+            return object;
+        } else if (valuep->format == vpiDecStrVal) {
+            char remainder[16];
+            unsigned long long val;
+            const int success = std::sscanf(valuep->value.str, "%30llu%15s",  // lintok-format-ll
+                                            &val, remainder);
+            if (success < 1) {
+                VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s",
+                              __func__, valuep->value.str,
+                              VerilatedVpiError::strFromVpiVal(valuep->format),
+                              valueVop->fullname());
+                return nullptr;
+            }
+            if (success > 1) {
+                VL_VPI_WARNING_(
+                    __FILE__, __LINE__, "%s: Trailing garbage '%s' in '%s' as value %s for %s",
+                    __func__, remainder, valuep->value.str,
+                    VerilatedVpiError::strFromVpiVal(valuep->format), valueVop->fullname());
+            }
+            vl_vpi_put_word(valueVop, val, 64, 0);
+            return object;
+        } else if (valuep->format == vpiHexStrVal) {
+            const int chars = (varBits + 3) >> 2;
+            const char* val = valuep->value.str;
+            // skip hex ident if one is detected at the start of the string
+            if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2;
+            const int len = std::strlen(val);
+            for (int i = 0; i < chars; ++i) {
+                char hex;
+                // compute hex digit value
+                if (i < len) {
+                    const char digit = val[len - i - 1];
+                    if (digit >= '0' && digit <= '9') {
+                        hex = digit - '0';
+                    } else if (digit >= 'a' && digit <= 'f') {
+                        hex = digit - 'a' + 10;
+                    } else if (digit >= 'A' && digit <= 'F') {
+                        hex = digit - 'A' + 10;
+                    } else {
+                        VL_VPI_WARNING_(__FILE__, __LINE__,
+                                        "%s: Non hex character '%c' in '%s' as value %s for %s",
+                                        __func__, digit, valuep->value.str,
+                                        VerilatedVpiError::strFromVpiVal(valuep->format),
+                                        valueVop->fullname());
+                        hex = 0;
+                    }
+                } else {
+                    hex = 0;
+                }
+                // assign hex digit value to destination
+                vl_vpi_put_word(valueVop, hex, 4, i * 4);
+            }
+            return object;
+        } else if (valuep->format == vpiStringVal) {
+            if (valueVop->varp()->vltype() == VLVT_STRING) {
+                // Does not use valueVop, because strings are not forceable anyway
+                *(baseSignalVop->varStringDatap()) = valuep->value.str;
+                return object;
+            } else {
+                const int chars = VL_BYTES_I(varBits);
+                const int len = std::strlen(valuep->value.str);
+                for (int i = 0; i < chars; ++i) {
+                    // prepend with 0 values before placing string the least significant bytes
+                    const char c = (i < len) ? valuep->value.str[len - i - 1] : 0;
+                    vl_vpi_put_word(valueVop, c, 8, i * 8);
+                }
+            }
+            return object;
+        } else if (valuep->format == vpiIntVal) {
+            vl_vpi_put_word(valueVop, valuep->value.integer, 64, 0);
+            return object;
+        } else if (valuep->format == vpiRealVal) {
+            if (valueVop->varp()->vltype() == VLVT_REAL) {
+                *(valueVop->varRealDatap()) = valuep->value.real;
+                return object;
+            }
+        } else if (valuep->format == vpiScalarVal) {
+            vl_vpi_put_word(valueVop, (valuep->value.scalar == vpi1 ? 1 : 0), 1, 0);
+            return object;
+        }
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s",
+                      __func__, VerilatedVpiError::strFromVpiVal(valuep->format),
+                      valueVop->fullname());
+        return nullptr;
+    } else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
+        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiParameter: %s",
+                        __func__, vop->fullname());
+        return nullptr;
+    } else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
+        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiConstant: %s",
+                        __func__, vop->fullname());
+        return nullptr;
+    }
+    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
+    return nullptr;
+}
+
+bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arrayvalue_p,
+                           const char* fullname) {
+    if (arrayvalue_p->format == vpiVectorVal) {
+        switch (varp->vltype()) {
+        case VLVT_UINT8:
+        case VLVT_UINT16:
+        case VLVT_UINT32:
+        case VLVT_UINT64:
+        case VLVT_WDATA: return true;
+        default:;  // LCOV_EXCL_LINE
+        }
+    } else if (arrayvalue_p->format == vpiIntVal) {
+        switch (varp->vltype()) {
+        case VLVT_UINT8:
+        case VLVT_UINT16:
+        case VLVT_UINT32: return true;
+        default:;  // LCOV_EXCL_LINE
+        }
+    } else if ((arrayvalue_p->format == vpiRawTwoStateVal)
+               || (arrayvalue_p->format == vpiRawFourStateVal)) {
+        switch (varp->vltype()) {
+        case VLVT_UINT8:
+        case VLVT_UINT16:
+        case VLVT_UINT32:
+        case VLVT_UINT64:
+        case VLVT_WDATA: return true;
+        default:;  // LCOV_EXCL_LINE
+        }
+    } else if (arrayvalue_p->format == vpiShortIntVal) {
+        switch (varp->vltype()) {
+        case VLVT_UINT8:
+        case VLVT_UINT16: return true;
+        default:;  // LCOV_EXCL_LINE
+        }
+    } else if (arrayvalue_p->format == vpiLongIntVal) {
+        switch (varp->vltype()) {
+        case VLVT_UINT8:
+        case VLVT_UINT16:
+        case VLVT_UINT32:
+        case VLVT_UINT64: return true;
+        default:;  // LCOV_EXCL_LINE
+        }
+    }
+
+    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
+                  VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), fullname);
+
+    return false;
+}
+
+template 
+void vl_get_value_array_integrals(unsigned index, const unsigned num, const unsigned size,
+                                  const unsigned packedSize, const bool leftIsLow, const T* src,
+                                  K* dst) {
+    static_assert(sizeof(K) >= sizeof(T), "size of type K is less than size of type T");
+    for (int i = 0; i < num; ++i) {
+        dst[i] = src[index];
+        index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
+                : index == 0 ? size - 1
+                             : index - 1;
+    }
+}
+
+template 
+void vl_put_value_array_integrals(unsigned index, const unsigned num, const unsigned size,
+                                  const unsigned packedSize, const bool leftIsLow, const T* src,
+                                  K* dst) {
+    static_assert(std::is_integral::value, "type T is not an integral type");
+    static_assert(std::is_unsigned::value, "type T is not unsigned");
+    static_assert(sizeof(T) >= sizeof(K), "size of type T is less than size of type K");
+    const unsigned element_size_bytes = VL_BYTES_I(packedSize);
+    const T mask = element_size_bytes == sizeof(T)
+                       ? static_cast(-1)
+                       : ~(static_cast(-1) << (element_size_bytes * 8));
+    for (unsigned i = 0; i < num; ++i) {
+        dst[index] = src[i] & static_cast(mask);
+        index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
+                : index == 0 ? size - 1
+                             : index - 1;
+    }
+}
+
+template 
+void vl_get_value_array_vectors(unsigned index, const unsigned num, const unsigned size,
+                                const unsigned packedSize, const bool leftIsLow, const T* src,
+                                p_vpi_vecval dst) {
+    static_assert(std::is_unsigned::value,
+                  "type T is not unsigned");  // ensure logical right shift
+    const unsigned element_size_words = VL_WORDS_I(packedSize);
+    if (sizeof(T) == sizeof(QData)) {
+        for (unsigned i = 0; i < num; ++i) {
+            dst[i * 2].aval = static_cast(src[index]);
+            dst[i * 2].bval = 0;
+            dst[(i * 2) + 1].aval = static_cast(src[index]) >> 32;
+            dst[(i * 2) + 1].bval = 0;
+            index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
+                    : index == 0 ? size - 1
+                                 : index - 1;
+        }
+    } else {
+        for (unsigned i = 0; i < num; ++i) {
+            const size_t dst_index = i * element_size_words;
+            const size_t src_index = index * element_size_words;
+            for (unsigned j = 0; j < element_size_words; ++j) {
+                dst[dst_index + j].aval = src[src_index + j];
+                dst[dst_index + j].bval = 0;
+            }
+            index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
+                    : index == 0 ? size - 1
+                                 : index - 1;
+        }
+    }
+}
+
+template 
+void vl_put_value_array_vectors(unsigned index, const unsigned num, const unsigned size,
+                                const unsigned packedSize, const bool leftIsLow,
+                                const bool fourState, const p_vpi_vecval src, T* dst) {
+    static_assert(std::is_unsigned::value, "type T is not unsigned");
+    static_assert(std::is_integral::value, "type T is not an integral type");
+    const unsigned element_size_bytes VL_BYTES_I(packedSize);
+    const unsigned element_size_words VL_WORDS_I(packedSize);
+    if (sizeof(T) == sizeof(QData)) {  //destination is QDATA
+        const QData mask = element_size_bytes == sizeof(T)
+                               ? static_cast(-1)
+                               : ~(static_cast(-1) << (element_size_bytes * 8));
+        for (unsigned i = 0; i < num; ++i) {
+            dst[index] = src[i * 2].aval;
+            dst[index]
+                |= (static_cast(src[(i * 2) + 1].aval) << (sizeof(PLI_UINT32) * 8)) & mask;
+            index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
+                    : index == 0 ? size - 1
+                                 : index - 1;
+        }
+    } else {
+        for (unsigned i = 0; i < num; ++i) {
+            unsigned bytes_stored = 0;
+            for (unsigned j = 0; j < element_size_words; ++j) {
+                if (bytes_stored >= element_size_bytes) break;
+                const T mask
+                    = (element_size_bytes - bytes_stored) >= sizeof(PLI_UINT32)
+                          ? static_cast(-1)
+                          : ~(static_cast(-1) << ((element_size_bytes - bytes_stored) * 8));
+                dst[(index * element_size_words) + j]
+                    = static_cast(src[(i * element_size_words) + j].aval) & mask;
+                bytes_stored += sizeof(PLI_UINT32);
+            }
+            index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
+                    : index == 0 ? size - 1
+                                 : index - 1;
+        }
+    }
+}
+
+template 
+void vl_get_value_array_rawvals(unsigned index, unsigned num, const unsigned size,
+                                const unsigned packedSize, const bool leftIsLow,
+                                const bool fourState, const T* src, PLI_BYTE8* dst) {
+    static_assert(std::is_unsigned::value,
+                  "type T is not unsigned");  //ensure logical right shift
+    const unsigned element_size_bytes VL_BYTES_I(packedSize);
+    const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T);
+    size_t dst_index = 0;
+    while (num-- > 0) {
+        const size_t src_offset = index * element_size_repr;
+        unsigned bytes_copied = 0;
+        for (unsigned j = 0; j < element_size_repr; ++j) {
+            const T& src_data = src[src_offset + j];
+            for (unsigned k = 0; k < sizeof(T); ++k) {
+                if (bytes_copied++ == element_size_bytes) break;
+                dst[dst_index++] = src_data >> (k * 8);
+            }
+        }
+        if (fourState) {
+            std::fill(dst + dst_index, dst + dst_index + element_size_bytes, 0);
+            dst_index += element_size_bytes;
+        }
+        index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
+                : index == 0 ? size - 1
+                             : index - 1;
+    }
+}
+
+template 
+void vl_put_value_array_rawvals(unsigned index, const unsigned num, const unsigned size,
+                                const unsigned packedSize, const bool leftIsLow,
+                                const bool fourState, const PLI_UBYTE8* src, T* dst) {
+    const unsigned element_size_bytes VL_BYTES_I(packedSize);
+    const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T);
+    for (unsigned i = 0; i < num; ++i) {
+        unsigned bytes_copied = 0;
+        const size_t dst_offset = index * element_size_repr;
+        const size_t src_offset = i * element_size_bytes;
+        for (unsigned j = 0; j < element_size_repr; ++j) {
+            T& dst_data = dst[dst_offset + j];
+            for (unsigned k = 0; k < sizeof(T); ++k) {
+                if (bytes_copied == element_size_bytes) break;
+                const unsigned src_index
+                    = fourState ? (src_offset * 2) + bytes_copied : (src_offset) + bytes_copied;
+                dst_data &= ~((static_cast(0xFF) & 0xFF) << (k * 8));
+                dst_data |= ((static_cast(src[src_index]) & 0xFF) << (k * 8));
+                bytes_copied++;
+            }
+        }
+        index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
+                : index == 0 ? size - 1
+                             : index - 1;
+    }
+}
+
+void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const PLI_INT32* index_p,
+                        PLI_UINT32 num) {
+    const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
+    if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
+
+    const VerilatedVar* const varp = vop->varp();
+
+    static thread_local EData t_out_data[VL_VALUE_STRING_MAX_WORDS * 2];
+
+    const unsigned size = vop->size();
+    if (VL_UNCOVERABLE(num > size)) {
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Requested elements (%u) exceed array size (%u)",
+                      __func__, num, size);
+        return;
+    }
+
+    const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low();
+    const int index
+        = leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0];
+
+    if (arrayvalue_p->format == vpiShortIntVal) {
+        if (VL_UNCOVERABLE((sizeof(PLI_INT16) * num) >= VL_VALUE_STRING_MAX_CHARS)) {
+            VL_FATAL_MT(__FILE__, __LINE__, "",
+                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
+                        "increase and recompile");
+        }
+
+        PLI_INT16* shortintsp = reinterpret_cast(t_out_data);
+        arrayvalue_p->value.shortints = shortintsp;
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
+                                         vop->varCDatap(), shortintsp);
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
+                                         vop->varSDatap(), shortintsp);
+        }
+
+        return;
+    } else if (arrayvalue_p->format == vpiIntVal) {
+        if (VL_UNCOVERABLE(num >= VL_VALUE_STRING_MAX_WORDS)) {
+            VL_FATAL_MT(__FILE__, __LINE__, "",
+                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
+                        "increase and recompile");
+        }
+
+        PLI_INT32* integersp = reinterpret_cast(t_out_data);
+        arrayvalue_p->value.integers = integersp;
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
+                                         vop->varCDatap(), integersp);
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
+                                         vop->varSDatap(), integersp);
+        } else if (varp->vltype() == VLVT_UINT32) {
+            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
+                                         vop->varIDatap(), integersp);
+        }
+
+        return;
+    } else if (arrayvalue_p->format == vpiLongIntVal) {
+        if (VL_UNCOVERABLE((sizeof(PLI_INT64) * num) >= VL_VALUE_STRING_MAX_CHARS)) {
+            VL_FATAL_MT(__FILE__, __LINE__, "",
+                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
+                        "increase and recompile");
+        }
+
+        PLI_INT64* longintsp = reinterpret_cast(t_out_data);
+        arrayvalue_p->value.longints = longintsp;
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
+                                         vop->varCDatap(), longintsp);
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
+                                         vop->varSDatap(), longintsp);
+        } else if (varp->vltype() == VLVT_UINT32) {
+            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
+                                         vop->varIDatap(), longintsp);
+        } else if (varp->vltype() == VLVT_UINT64) {
+            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
+                                         vop->varQDatap(), longintsp);
+        }
+
+        return;
+    } else if (arrayvalue_p->format == vpiVectorVal) {
+        if (VL_UNCOVERABLE((VL_WORDS_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_WORDS)) {
+            VL_FATAL_MT(__FILE__, __LINE__, "",
+                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
+                        "increase and recompile");
+        }
+
+        p_vpi_vecval vectorsp = reinterpret_cast(t_out_data);
+        arrayvalue_p->value.vectors = vectorsp;
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
+                                       vop->varCDatap(), vectorsp);
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
+                                       vop->varSDatap(), vectorsp);
+        } else if (varp->vltype() == VLVT_UINT32) {
+            vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
+                                       vop->varIDatap(), vectorsp);
+        } else if (varp->vltype() == VLVT_UINT64) {
+            vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
+                                       vop->varQDatap(), vectorsp);
+        } else if (varp->vltype() == VLVT_WDATA) {
+            vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
+                                       vop->varEDatap(), vectorsp);
+        }
+
+        return;
+    } else if (arrayvalue_p->format == vpiRawFourStateVal) {
+        if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_CHARS)) {
+            VL_FATAL_MT(__FILE__, __LINE__, "",
+                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
+                        "increase and recompile");
+        }
+
+        PLI_BYTE8* valuep = reinterpret_cast(t_out_data);
+        arrayvalue_p->value.rawvals = valuep;
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
+                                       vop->varCDatap(), valuep);
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
+                                       vop->varSDatap(), valuep);
+        } else if (varp->vltype() == VLVT_UINT32) {
+            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
+                                       vop->varIDatap(), valuep);
+        } else if (varp->vltype() == VLVT_UINT64) {
+            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
+                                       vop->varQDatap(), valuep);
+        } else if (varp->vltype() == VLVT_WDATA) {
+            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
+                                       vop->varEDatap(), valuep);
+        }
+
+        return;
+    } else if (arrayvalue_p->format == vpiRawTwoStateVal) {
+        if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * num) >= VL_VALUE_STRING_MAX_CHARS)) {
+            VL_FATAL_MT(__FILE__, __LINE__, "",
+                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
+                        "increase and recompile");
+        }
+
+        PLI_BYTE8* valuep = reinterpret_cast(t_out_data);
+        arrayvalue_p->value.rawvals = valuep;
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
+                                       vop->varCDatap(), valuep);
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
+                                       vop->varSDatap(), valuep);
+        } else if (varp->vltype() == VLVT_UINT32) {
+            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
+                                       vop->varIDatap(), valuep);
+        } else if (varp->vltype() == VLVT_UINT64) {
+            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
+                                       vop->varQDatap(), valuep);
+        } else if (varp->vltype() == VLVT_WDATA) {
+            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
+                                       vop->varEDatap(), valuep);
+        }
+
+        return;
+    }
+
+    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
+                  VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname());
+}
+
+void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p,
+                         PLI_UINT32 num) {
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value_array %p\n", object););
+    VerilatedVpiImp::assertOneCheck();
+
+    VL_VPI_ERROR_RESET_();
+    if (VL_UNLIKELY(!object)) return;
+
+    if (VL_UNLIKELY(!arrayvalue_p)) {
+        VL_VPI_WARNING_(__FILE__, __LINE__,
+                        "Ignoring vpi_get_value_array with null value pointer");
+        return;
+    }
+
+    if (VL_UNLIKELY(!index_p)) {
+        VL_VPI_WARNING_(__FILE__, __LINE__,
+                        "Ignoring vpi_get_value_array with null index pointer");
+        return;
+    }
+
+    const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
+    if (VL_UNLIKELY(!vop)) {
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
+        return;
+    }
+
+    if (vop->type() != vpiRegArray) {
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
+                      VerilatedVpiError::strFromVpiObjType(vop->type()));
+        return;
+    }
+
+    const int lowRange = vop->rangep()->low();
+    const int highRange = vop->rangep()->high();
+    if ((index_p[0] > highRange) || (index_p[0] < lowRange)) {
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Index %u for object '%s' is out of bounds [%u,%u]",
+                      __func__, index_p[0], vop->fullname(), lowRange, highRange);
+        return;
+    }
+
+    if (arrayvalue_p->flags & vpiUserAllocFlag) {
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiUserAllocFlag (%x)", __func__,
+                      arrayvalue_p->flags);
+        return;
+    }
+
+    vl_get_value_array(object, arrayvalue_p, index_p, num);
+}
+
+void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const PLI_INT32* index_p,
+                        PLI_UINT32 num) {
+    const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
+    if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
+
+    const VerilatedVar* const varp = vop->varp();
+
+    const int size = vop->size();
+    if (VL_UNCOVERABLE(num > size)) {
+        VL_VPI_ERROR_(__FILE__, __LINE__,
+                      "%s: Requested elements to set (%u) exceed array size (%u)", __func__, num,
+                      size);
+        return;
+    }
+
+    const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low();
+    const int index
+        = leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0];
+
+    if (arrayvalue_p->format == vpiShortIntVal) {
+        const PLI_UINT16* shortintsp
+            = reinterpret_cast(arrayvalue_p->value.shortints);
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
+                                         vop->varCDatap());
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
+                                         vop->varSDatap());
+        }
+
+        return;
+    } else if (arrayvalue_p->format == vpiIntVal) {
+        const PLI_UINT32* integersp = reinterpret_cast(arrayvalue_p->value.integers);
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
+                                         vop->varCDatap());
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
+                                         vop->varSDatap());
+        } else if (varp->vltype() == VLVT_UINT32) {
+            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
+                                         vop->varIDatap());
+        }
+
+        return;
+    } else if (arrayvalue_p->format == vpiLongIntVal) {
+        const PLI_UINT64* longintsp = reinterpret_cast(arrayvalue_p->value.longints);
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
+                                         vop->varCDatap());
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
+                                         vop->varSDatap());
+        } else if (varp->vltype() == VLVT_UINT32) {
+            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
+                                         vop->varIDatap());
+        } else if (varp->vltype() == VLVT_UINT64) {
+            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
+                                         vop->varQDatap());
+        }
+
+        return;
+    } else if (arrayvalue_p->format == vpiVectorVal) {
+        const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors;
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
+                                       vectorsp, vop->varCDatap());
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
+                                       vectorsp, vop->varSDatap());
+        } else if (varp->vltype() == VLVT_UINT32) {
+            vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
+                                       vectorsp, vop->varIDatap());
+        } else if (varp->vltype() == VLVT_UINT64) {
+            vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
+                                       vectorsp, vop->varQDatap());
+        } else if (varp->vltype() == VLVT_WDATA) {
+            vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
+                                       vectorsp, vop->varEDatap());
+        }
+
+        return;
+    } else if (arrayvalue_p->format == vpiRawFourStateVal) {
+        const PLI_UBYTE8* valuep = reinterpret_cast(arrayvalue_p->value.rawvals);
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
+                                       vop->varCDatap());
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
+                                       vop->varSDatap());
+        } else if (varp->vltype() == VLVT_UINT32) {
+            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
+                                       vop->varIDatap());
+        } else if (varp->vltype() == VLVT_UINT64) {
+            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
+                                       vop->varQDatap());
+        } else if (varp->vltype() == VLVT_WDATA) {
+            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
+                                       vop->varEDatap());
+        }
+
+        return;
+    } else if (arrayvalue_p->format == vpiRawTwoStateVal) {
+        const PLI_UBYTE8* valuep = reinterpret_cast(arrayvalue_p->value.rawvals);
+
+        if (varp->vltype() == VLVT_UINT8) {
+            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
+                                       vop->varCDatap());
+        } else if (varp->vltype() == VLVT_UINT16) {
+            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
+                                       vop->varSDatap());
+        } else if (varp->vltype() == VLVT_UINT32) {
+            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
+                                       vop->varIDatap());
+        } else if (varp->vltype() == VLVT_UINT64) {
+            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
+                                       vop->varQDatap());
+        } else if (varp->vltype() == VLVT_WDATA) {
+            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
+                                       vop->varEDatap());
+        }
+
+        return;
+    }
+
+    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
+                  VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname());
+}
+
+void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p,
+                         PLI_UINT32 num) {
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value_array %p\n", object););
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+
+    if (VL_UNLIKELY(!arrayvalue_p)) {
+        VL_VPI_WARNING_(__FILE__, __LINE__,
+                        "Ignoring vpi_put_value_array with null value pointer");
+        return;
+    }
+
+    if (VL_UNLIKELY(!index_p)) {
+        VL_VPI_WARNING_(__FILE__, __LINE__,
+                        "Ignoring vpi_put_value_array with null index pointer");
+        return;
+    }
+
+    const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
+    if (VL_UNLIKELY(!vop)) {
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
+        return;
+    }
+
+    if (vop->type() != vpiRegArray) {
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
+                      VerilatedVpiError::strFromVpiObjType(vop->type()));
+        return;
+    }
+
+    const int lowRange = vop->rangep()->low();
+    const int highRange = vop->rangep()->high();
+    if ((index_p[0] > highRange) || (index_p[0] < lowRange)) {
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Index %u for object '%s' is out of bounds [%u,%u]",
+                      __func__, index_p[0], vop->fullname(), lowRange, highRange);
+        return;
+    }
+
+    if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
+        VL_VPI_ERROR_(__FILE__, __LINE__,
+                      "Ignoring vpi_put_value_array to signal marked read-only,"
+                      " use public_flat_rw instead: %s",
+                      vop->fullname());
+        return;
+    }
+
+    if (arrayvalue_p->flags & (vpiPropagateOff | vpiOneValue)) {
+        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported flags (%x)", __func__,
+                      arrayvalue_p->flags);
+        return;
+    }
+
+    vl_put_value_array(object, arrayvalue_p, index_p, num);
+}
+
+// time processing
+
+void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    // cppcheck-suppress nullPointer
+    if (VL_UNLIKELY(!time_p)) {
+        VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_get_time with nullptr value pointer");
+        return;
+    }
+    if (time_p->type == vpiSimTime) {
+        const QData qtime = VL_TIME_Q();
+        VlWide<2> itime;
+        VL_SET_WQ(itime, qtime);
+        time_p->low = itime[0];
+        time_p->high = itime[1];
+        return;
+    } else if (time_p->type == vpiScaledRealTime) {
+        double dtime = VL_TIME_D();
+        if (const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object)) {
+            const int scalePow10
+                = Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit();
+            const double scale = vl_time_multiplier(scalePow10);  // e.g. 0.0001
+            dtime *= scale;
+        }
+        time_p->real = dtime;
+        return;
+    }
+    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%d)", __func__, time_p->type);
+}
+
+// I/O routines
+
+PLI_UINT32 vpi_mcd_open(PLI_BYTE8* filenamep) {
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    return VL_FOPEN_NN(filenamep, "wb");
+}
+
+PLI_UINT32 vpi_mcd_close(PLI_UINT32 mcd) {
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    VL_FCLOSE_I(mcd);
+    return 0;
+}
+
+PLI_BYTE8* vpi_mcd_name(PLI_UINT32 /*mcd*/) {
+    VL_VPI_UNIMP_();
+    return nullptr;
+}
+
+PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8* formatp, ...) {
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    va_list ap;
+    va_start(ap, formatp);
+    const int chars = vpi_mcd_vprintf(mcd, formatp, ap);
+    va_end(ap);
+    return chars;
+}
+
+PLI_INT32 vpi_printf(PLI_BYTE8* formatp, ...) {
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    va_list ap;
+    va_start(ap, formatp);
+    const int chars = vpi_vprintf(formatp, ap);
+    va_end(ap);
+    return chars;
+}
+
+// cppcheck-suppress constParameterPointer
+PLI_INT32 vpi_vprintf(PLI_BYTE8* formatp, va_list ap) {
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    return VL_VPRINTF(formatp, ap);
+}
+
+// cppcheck-suppress constParameterPointer
+PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8* format, va_list ap) {
+    VerilatedVpiImp::assertOneCheck();
+    FILE* const fp = VL_CVT_I_FP(mcd);
+    VL_VPI_ERROR_RESET_();
+    // cppcheck-suppress nullPointer
+    if (VL_UNLIKELY(!fp)) return 0;
+    const int chars = vfprintf(fp, format, ap);
+    return chars;
+}
+
+PLI_INT32 vpi_flush(void) {
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    Verilated::runFlushCallbacks();
+    return 0;  // Gcc coverage bug // LCOV_EXCL_LINE
+}
+
+PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
+    VerilatedVpiImp::assertOneCheck();
+    FILE* const fp = VL_CVT_I_FP(mcd);
+    VL_VPI_ERROR_RESET_();
+    if (VL_UNLIKELY(!fp)) return 1;
+    std::fflush(fp);
+    return 0;
+}
+
+// utility routines
+
+PLI_INT32 vpi_compare_objects(vpiHandle /*object1*/, vpiHandle /*object2*/) {
+    VL_VPI_UNIMP_();
+    return 0;
+}
+PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) {
+    // executing vpi_chk_error does not reset error
+    // error_info_p can be nullptr, so only return level in that case
+    VerilatedVpiImp::assertOneCheck();
+    const p_vpi_error_info imp_info_p = VerilatedVpiImp::error_info()->getError();
+    if (error_info_p && imp_info_p) *error_info_p = *imp_info_p;
+    if (!imp_info_p) return 0;  // no error occurred
+    return imp_info_p->level;  // return error severity level
+}
+
+#ifndef VL_NO_LEGACY
+PLI_INT32 vpi_free_object(vpiHandle object) {
+    // vpi_free_object is IEEE deprecated, use vpi_release_handle
+    return vpi_release_handle(object);
+}
+#endif
+
+PLI_INT32 vpi_release_handle(vpiHandle object) {
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n", object););
+    VerilatedVpiImp::assertOneCheck();
+    VerilatedVpio* const vop = VerilatedVpio::castp(object);
+    VL_VPI_ERROR_RESET_();
+    if (VL_UNLIKELY(!vop)) return 0;
+    VL_DO_DANGLING(delete vop, vop);
+    return 1;
+}
+
+PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) {
+    // This is VL_MT_SAFE, but not marked as can't indicate it in the standardized header file
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    const auto argc_argv = Verilated::threadContextp()->impp()->argc_argv();
+    vlog_info_p->argc = argc_argv.first;
+    vlog_info_p->argv = argc_argv.second;
+    vlog_info_p->product = const_cast(Verilated::productName());
+    vlog_info_p->version = const_cast(Verilated::productVersion());
+    return 1;
+}
+
+// routines added with 1364-2001
+
+PLI_INT32 vpi_get_data(PLI_INT32 /*id*/, PLI_BYTE8* /*dataLoc*/, PLI_INT32 /*numOfBytes*/) {
+    VL_VPI_UNIMP_();
+    return 0;
+}
+PLI_INT32 vpi_put_data(PLI_INT32 /*id*/, PLI_BYTE8* /*dataLoc*/, PLI_INT32 /*numOfBytes*/) {
+    VL_VPI_UNIMP_();
+    return 0;
+}
+void* vpi_get_userdata(vpiHandle /*obj*/) {
+    VL_VPI_UNIMP_();
+    return nullptr;
+}
+PLI_INT32 vpi_put_userdata(vpiHandle /*obj*/, void* /*userdata*/) {
+    VL_VPI_UNIMP_();
+    return 0;
+}
+
+PLI_INT32 vpi_control(PLI_INT32 operation, ...) {
+    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_control %d\n", operation););
+    VerilatedVpiImp::assertOneCheck();
+    VL_VPI_ERROR_RESET_();
+    switch (operation) {
+    case vpiFinish: {
+        VL_FINISH_MT("", 0, "*VPI*");
+        return 1;
+    }
+    case vpiStop: {
+        VL_STOP_MT("", 0, "*VPI*");
+        return 1;  // LCOV_EXCL_LINE
+    }
+    default: {
+        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, ignoring", __func__,
+                        VerilatedVpiError::strFromVpiProp(operation));
+        return 0;
+    }
+    }
+}
+
+vpiHandle vpi_handle_by_multi_index(vpiHandle /*obj*/, PLI_INT32 /*num_index*/,
+                                    PLI_INT32* /*index_array*/) {
+    VL_VPI_UNIMP_();
+    return nullptr;
+}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.h
new file mode 100644
index 00000000000..14ac9bff046
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.h
@@ -0,0 +1,72 @@
+// -*- mode: C++; c-file-style: "cc-mode" -*-
+//*************************************************************************
+//
+// Code available from: https://verilator.org
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of either the GNU Lesser General Public License Version 3
+// or the Perl Artistic License Version 2.0.
+// SPDX-FileCopyrightText: 2009-2026 Wilson Snyder
+// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+//
+//=========================================================================
+///
+/// \file
+/// \brief Verilated VPI header
+///
+/// This file contains routines related to using VPI with Verilated models.
+///
+/// User wrapper code may need to include this if controlling Verilated
+/// models that use ths VPI.
+///
+//=========================================================================
+
+#ifndef VERILATOR_VERILATED_VPI_H_
+#define VERILATOR_VERILATED_VPI_H_
+
+#include "verilatedos.h"
+
+#include "verilated.h"
+#include "verilated_syms.h"
+
+//======================================================================
+// From IEEE 1800-2023 annex M
+
+#include "vltstd/sv_vpi_user.h"
+
+//======================================================================
+
+/// Class for namespace-like grouping of Verilator VPI functions.
+
+class VerilatedVpi final {
+public:
+    /// Call timed callbacks.
+    /// User wrapper code should call this from their main loops.
+    static void callTimedCbs() VL_MT_UNSAFE_ONE;
+    /// Call value based callbacks.
+    /// User wrapper code should call this from their main loops.
+    static bool callValueCbs() VL_MT_UNSAFE_ONE;
+    /// Call callbacks of arbitrary types.
+    /// User wrapper code should call this from their main loops.
+    static bool callCbs(uint32_t reason) VL_MT_UNSAFE_ONE;
+    /// Returns true if there are callbacks of the given reason registered.
+    /// User wrapper code should call this from their main loops.
+    static bool hasCbs(uint32_t reason) VL_MT_UNSAFE_ONE;
+    /// Returns time of the next registered VPI callback, or
+    /// ~(0ULL) if none are registered
+    static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
+    /// Debug dump of callbacks
+    static void dumpCbs() VL_MT_UNSAFE_ONE;
+    /// Checks VPI dirty state (i.e. whether vpi_put_value() has
+    /// been called since the last clearEvalNeeded())
+    static bool evalNeeded() VL_MT_UNSAFE_ONE;
+    /// Clears VPI dirty state (see evalNeeded())
+    static void clearEvalNeeded() VL_MT_UNSAFE_ONE;
+    /// Perform inertially delayed puts
+    static void doInertialPuts() VL_MT_UNSAFE_ONE;
+
+    // Self test, for internal use only
+    static void selfTest() VL_MT_UNSAFE_ONE;
+};
+
+#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos.h
new file mode 100644
index 00000000000..b93eaae5617
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos.h
@@ -0,0 +1,753 @@
+// -*- mode: C++; c-file-style: "cc-mode" -*-
+//*************************************************************************
+//
+// Code available from: https://verilator.org
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of either the GNU Lesser General Public License Version 3
+// or the Perl Artistic License Version 2.0.
+// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
+// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+//
+//*************************************************************************
+///
+/// \file
+/// \brief Verilated/Verilator common header for OS portability
+///
+/// This header is included by user wrappers and defines the Verilated
+/// public-facing API.
+///
+/// User wrapper code does not generally need to include this, instead
+/// include verilated.h.
+///
+/// This header is used by both the Verilator source code (run on the build
+/// and host system), and the Verilated output (run on the target system).
+///
+/// Configuration code needed by only the host system is in
+/// config_build.h.in, code needed by Verilated code only is in
+/// verilated.h, and code needed by both is here (verilatedos.h).
+///
+//*************************************************************************
+
+#ifndef VERILATOR_VERILATEDOS_H_
+#define VERILATOR_VERILATEDOS_H_
+
+// Current clang-format versions botch #ifdef inclusion, so
+// clang-format off
+//=========================================================================
+// Compiler pragma abstraction
+
+#if defined(__clang__)
+# define VL_CLANG_ATTR(attr) __attribute__(( attr ))
+#else
+# define VL_CLANG_ATTR(attr)
+#endif
+
+#ifdef __GNUC__
+# define VL_ATTR_ALWINLINE __attribute__((always_inline)) inline
+# define VL_ATTR_NOINLINE __attribute__((noinline))
+# define VL_ATTR_COLD __attribute__((cold))
+# define VL_ATTR_HOT __attribute__((hot))
+# define VL_ATTR_NORETURN __attribute__((noreturn))
+// clang and gcc-8.0+ support no_sanitize("string") style attribute
+# if defined(__clang__) || (__GNUC__ >= 8)
+#  define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize("alignment")))
+# else  // The entire undefined sanitizer has to be disabled for older gcc
+#  define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize_undefined))
+# endif
+# define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1)))
+# define VL_ATTR_PURE __attribute__((pure))
+# define VL_ATTR_UNUSED __attribute__((unused))
+# define VL_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+# if !defined(_WIN32) && !defined(__MINGW32__)
+// All VL_ATTR_WEAK symbols must be marked with the macOS -U linker flag in verilated.mk.in
+#  define VL_ATTR_WEAK __attribute__((weak))
+# endif
+# define VL_LIKELY(x) __builtin_expect(!!(x), 1)  // Prefer over C++20 [[likely]]
+# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0)  // Prefer over C++20 [[unlikely]]
+# define VL_PREFETCH_RD(p) __builtin_prefetch((p), 0)
+# define VL_PREFETCH_RW(p) __builtin_prefetch((p), 1)
+#endif
+
+#ifdef __cpp_lib_unreachable
+/// Statement that may never be reached (for coverage etc)
+# define VL_UNREACHABLE std::unreachable()  // C++23
+#elif defined(__GNUC__)
+# define VL_UNREACHABLE __builtin_unreachable()
+#elif defined(_MSC_VER)  // MSVC
+# define VL_UNREACHABLE __assume(false)
+#else
+# define VL_UNREACHABLE
+#endif
+
+// Function acquires a capability/lock (-fthread-safety)
+#define VL_ACQUIRE(...) \
+        VL_CLANG_ATTR(annotate("ACQUIRE")) \
+        VL_CLANG_ATTR(acquire_capability(__VA_ARGS__))
+// Function acquires a shared capability/lock (-fthread-safety)
+#define VL_ACQUIRE_SHARED(...) \
+        VL_CLANG_ATTR(annotate("ACQUIRE_SHARED")) \
+        VL_CLANG_ATTR(acquire_shared_capability(__VA_ARGS__))
+// Function releases a capability/lock (-fthread-safety)
+#define VL_RELEASE(...) \
+        VL_CLANG_ATTR(annotate("RELEASE")) \
+        VL_CLANG_ATTR(release_capability(__VA_ARGS__))
+// Function releases a shared capability/lock (-fthread-safety)
+#define VL_RELEASE_SHARED(...) \
+        VL_CLANG_ATTR(annotate("RELEASE_SHARED")) \
+        VL_CLANG_ATTR(release_shared_capability(__VA_ARGS__))
+// Function returns bool if acquired a capability (-fthread-safety)
+#define VL_TRY_ACQUIRE(...) \
+        VL_CLANG_ATTR(try_acquire_capability(__VA_ARGS__))
+// Function returns bool if acquired shared (-fthread-safety)
+#define VL_TRY_ACQUIRE_SHARED(...) \
+        VL_CLANG_ATTR(try_acquire_shared_capability(__VA_ARGS__))
+// Function requires a capability inbound (-fthread-safety)
+#define VL_CAPABILITY(x) \
+        VL_CLANG_ATTR(capability(x))
+// Name of mutex protecting this variable (-fthread-safety)
+#define VL_EXCLUDES(x) \
+        VL_CLANG_ATTR(annotate("EXCLUDES")) \
+        VL_CLANG_ATTR(locks_excluded(x))
+// Scoped threaded capability/lock (-fthread-safety)
+#define VL_SCOPED_CAPABILITY \
+        VL_CLANG_ATTR(scoped_lockable)
+// Annotated function returns reference to the given capability.
+// Allowed on: function, method. (-fthread-safety)
+#define VL_RETURN_CAPABILITY(x) \
+        VL_CLANG_ATTR(lock_returned(x))
+// Assert that capability is already held.
+// Allowed on: function, method. (-fthread-safety)
+#define VL_ASSERT_CAPABILITY(x) \
+        VL_CLANG_ATTR(assert_capability(x))
+// Disable thread safety analysis for the annotted function
+// Use this only when absolutely sure code is correct, but too
+// complicated for the compiler to prove.
+#define VL_NO_THREAD_SAFETY_ANALYSIS \
+        VL_CLANG_ATTR(no_thread_safety_analysis)
+
+// Require mutex locks only in code units which work with enabled multi-threading.
+#if !defined(VL_MT_DISABLED_CODE_UNIT)
+// Function requires not having a capability inbound (-fthread-safety)
+# define VL_REQUIRES(x) \
+        VL_CLANG_ATTR(annotate("REQUIRES")) \
+        VL_CLANG_ATTR(requires_capability(x))
+// Name of capability/lock (-fthread-safety)
+# define VL_GUARDED_BY(x) \
+        VL_CLANG_ATTR(annotate("GUARDED_BY")) \
+        VL_CLANG_ATTR(guarded_by(x))
+// The data that the annotated pointer points to is protected by the given capability.
+// The pointer itself is not protected.
+// Allowed on: pointer data member. (-fthread-safety)
+# define VL_PT_GUARDED_BY(x) \
+        VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \
+        VL_CLANG_ATTR(pt_guarded_by(x))
+#else
+// Keep annotations for clang_check_attributes
+# define VL_REQUIRES(x) \
+        VL_CLANG_ATTR(annotate("REQUIRES"))
+# define VL_GUARDED_BY(x) \
+        VL_CLANG_ATTR(annotate("GUARDED_BY"))
+# define VL_PT_GUARDED_BY(x) \
+        VL_CLANG_ATTR(annotate("PT_GUARDED_BY"))
+#endif
+
+// Defaults for unsupported compiler features
+#ifndef VL_ATTR_ALWINLINE
+# define VL_ATTR_ALWINLINE inline  ///< Attribute to inline, even when not optimizing
+#endif
+#ifndef VL_ATTR_NOINLINE
+# define VL_ATTR_NOINLINE  ///< Attribute to never inline, even when optimizing
+#endif
+#ifndef VL_ATTR_COLD
+# define VL_ATTR_COLD  ///< Attribute that function is rarely executed
+#endif
+#ifndef VL_ATTR_HOT
+# define VL_ATTR_HOT  ///< Attribute that function is highly executed
+#endif
+#ifndef VL_ATTR_NORETURN
+# define VL_ATTR_NORETURN  ///< Attribute that function does not ever return
+#endif
+#ifndef VL_ATTR_NO_SANITIZE_ALIGN
+# define VL_ATTR_NO_SANITIZE_ALIGN  ///< Attribute that function contains intended unaligned access
+#endif
+#ifndef VL_ATTR_PRINTF
+# define VL_ATTR_PRINTF(fmtArgNum)  ///< Attribute for function with printf format checking
+#endif
+#ifndef VL_ATTR_PURE
+# define VL_ATTR_PURE  ///< Attribute that function is pure (and thus also VL_MT_SAFE)
+#endif
+#ifndef VL_ATTR_UNUSED
+# define VL_ATTR_UNUSED  ///< Attribute that function that may be never used
+#endif
+#ifndef VL_ATTR_WARN_UNUSED_RESULT
+# define VL_ATTR_WARN_UNUSED_RESULT  ///< Attribute that return value of function must be used
+#endif
+#ifndef VL_ATTR_WEAK
+# define VL_ATTR_WEAK  ///< Attribute that function external that is optionally defined
+#endif
+#ifndef VL_LIKELY
+# define VL_LIKELY(x) (!!(x))  ///< Return boolean expression that is more often true
+# define VL_UNLIKELY(x) (!!(x))  ///< Return boolean expression that is more often false
+#endif
+/// Boolean expression never hit by users (branch coverage disabled)
+# define VL_UNCOVERABLE(x) VL_UNLIKELY(x)
+#ifndef VL_PREFETCH_RD
+# define VL_PREFETCH_RD(p)  ///< Prefetch pointer argument with read intent
+#endif
+#ifndef VL_PREFETCH_RW
+# define VL_PREFETCH_RW(p)  ///< Prefetch pointer argument with read/write intent
+#endif
+
+
+#ifndef VL_NO_LEGACY
+# define VL_ATTR_ALIGNED(alignment)  // Deprecated
+# define VL_FUNC __func__  // Deprecated
+# define VL_THREAD  // Deprecated
+# define VL_THREAD_LOCAL thread_local  // Deprecated
+# define VL_STATIC_OR_THREAD static  // Deprecated
+#endif
+
+// Comment tag that Function is pure (and thus also VL_MT_SAFE)
+#define VL_PURE VL_CLANG_ATTR(annotate("PURE"))
+// Annotated function can be called only in MT_DISABLED context, i.e. either in a code unit
+// compiled with VL_MT_DISABLED_CODE_UNIT preprocessor definition, or in the main thread.
+#define VL_MT_DISABLED \
+    VL_CLANG_ATTR(annotate("MT_DISABLED")) \
+    VL_EXCLUDES(VlOs::MtScopeMutex::s_haveThreadScope)
+// Comment tag that function is threadsafe
+#define VL_MT_SAFE VL_CLANG_ATTR(annotate("MT_SAFE"))
+// Comment tag that function is threadsafe, only if
+// other threads doesn't change tree topology
+#define VL_MT_STABLE VL_CLANG_ATTR(annotate("MT_STABLE"))
+// Comment tag that function is threadsafe, only
+// during normal operation (post-init)
+#define VL_MT_SAFE_POSTINIT VL_CLANG_ATTR(annotate("MT_SAFE_POSTINIT"))
+// Attribute that function is clang threadsafe and uses given mutex
+#define VL_MT_SAFE_EXCLUDES(mutex) VL_CLANG_ATTR(annotate("MT_SAFE_EXCLUDES")) VL_EXCLUDES(mutex)
+// Comment tag that function is not threadsafe
+#define VL_MT_UNSAFE VL_CLANG_ATTR(annotate("MT_UNSAFE"))
+// Comment tag that function is not threadsafe
+// protected to make sure single-caller
+#define VL_MT_UNSAFE_ONE VL_CLANG_ATTR(annotate("MT_UNSAFE_ONE"))
+// Comment tag that function is entry point of parallelization
+#define VL_MT_START VL_CLANG_ATTR(annotate("MT_START")) VL_REQUIRES(VlOs::MtScopeMutex::s_haveThreadScope)
+
+#ifndef VL_NO_LEGACY
+# define VL_ULL(c) (c##ULL)  // Add appropriate suffix to 64-bit constant (deprecated)
+#endif
+
+// Convert argument to IData
+// This is not necessarily the same as "#UL", depending on what the IData typedef is.
+#define VL_UL(c) (static_cast(c##UL))
+
+#if defined(VL_CPPCHECK) || defined(__clang_analyzer__) || __cplusplus < 201103L
+# define VL_DANGLING(var)
+#else
+/// After e.g. delete, set variable to nullptr to indicate must not use later
+# define VL_DANGLING(var) \
+    do { \
+        *const_cast(reinterpret_cast(&var)) = nullptr; \
+    } while (false)
+#endif
+
+/// Perform an e.g. delete, then set variable to nullptr to indicate must not use later.
+/// Unlike VL_DO_CLEAR the setting of the variable is only for debug reasons.
+#define VL_DO_DANGLING(stmt, var) \
+    do { \
+        do { \
+            stmt; \
+        } while (false); \
+        VL_DANGLING(var); \
+    } while (false)
+
+/// As with VL_DO_DANGLING, but two variables dangle.
+#define VL_DO_DANGLING2(stmt, var, var2) \
+    do { \
+        do { \
+            stmt; \
+        } while (false); \
+        VL_DANGLING(var); \
+        VL_DANGLING(var2); \
+    } while (false)
+
+/// Perform an e.g. delete, then set variable to nullptr as a requirement
+#define VL_DO_CLEAR(stmt, stmt2) \
+    do { \
+        do { \
+            stmt; \
+        } while (false); \
+        do { \
+            stmt2; \
+        } while (false); \
+    } while (false)
+
+#ifdef _MSC_VER
+# if _MSC_VER < 1929
+#  error "Verilator requires at least Visual Studio 2019 version 16.11.2"
+# endif
+#endif
+
+//=========================================================================
+// C++-2014
+
+#if __cplusplus >= 201402L || defined(VL_CPPCHECK) || defined(_MSC_VER)
+#else
+# error "Verilator requires a C++14 or newer compiler"
+#endif
+
+#ifndef VL_NO_LEGACY
+// These are deprecated historical defines. We leave them in case users referenced them.
+# define VL_EQ_DELETE = delete
+# define vl_unique_ptr std::unique_ptr
+# define vl_unordered_map std::unordered_map
+# define vl_unordered_set std::unordered_set
+# define VL_INCLUDE_UNORDERED_MAP 
+# define VL_INCLUDE_UNORDERED_SET 
+# define VL_FINAL final
+# define VL_MUTABLE mutable
+# define VL_OVERRIDE override
+#endif
+
+//=========================================================================
+// C++-2017
+
+#if __cplusplus >= 201703L
+# define VL_CONSTEXPR_CXX17 constexpr
+#else
+# define VL_CONSTEXPR_CXX17
+#endif
+
+
+//=========================================================================
+// Optimization
+
+#ifndef VL_NO_LEGACY
+# ifndef VL_INLINE_OPT
+#   define VL_INLINE_OPT  // Historical, has no effect on Verilated models.
+# endif
+#endif
+
+//=========================================================================
+// Internal coverage
+
+#ifdef VL_GCOV
+extern "C" void __gcov_dump();
+// Dump internal code coverage data before e.g. std::abort()
+# define VL_GCOV_DUMP() __gcov_dump()
+#else
+# define VL_GCOV_DUMP()
+#endif
+
+//=========================================================================
+// Warning disabled
+
+#ifndef VL_WARNINGS
+# ifdef _MSC_VER
+#  pragma warning(disable:4099)  // C4099: type name first seen using 'class' now seen using 'struct' (V3AstNode)
+#  pragma warning(disable:4100)  // C4100: unreferenced formal parameter (L4)
+#  pragma warning(disable:4127)  // C4127: conditional expression is constant (L4)
+#  pragma warning(disable:4146)  // C4146: unary minus operator applied to unsigned type, result still unsigned
+#  pragma warning(disable:4189)  // C4189: local variable is initialized but not referenced (L4)
+#  pragma warning(disable:4244)  // C4244: conversion from 'uint64_t' to 'uint_32_t', possible loss of data
+#  pragma warning(disable:4245)  // C4245: conversion from 'int' to 'unsigned', signed/unsigned mismatch
+#  pragma warning(disable:4996)  // C4996: sscanf/fopen/etc may be unsafe
+# endif
+#endif
+
+//=========================================================================
+// Basic integer types
+
+#ifdef __MINGW32__
+# define __USE_MINGW_ANSI_STDIO 1  // Force old MinGW (GCC 5 and older) to use C99 formats
+#endif
+
+// The inttypes supplied with some GCC & MINGW32 versions requires STDC_FORMAT_MACROS
+// to be declared in order to get the PRIxx macros used by fstapi.c
+#define __STDC_FORMAT_MACROS
+
+// Now that C++ requires these standard types the vl types are deprecated
+#include 
+#include 
+#include 
+#include 
+
+#ifndef VL_NO_LEGACY
+using vluint8_t = uint8_t;  ///< 8-bit unsigned type (backward compatibility)
+using vluint16_t = uint16_t;  ///< 16-bit unsigned type (backward compatibility)
+using vluint32_t = uint32_t;  ///< 32-bit unsigned type (backward compatibility)
+using vluint64_t = uint64_t;  ///< 64-bit unsigned type (backward compatibility)
+using vlsint8_t = int8_t;  ///< 8-bit signed type (backward compatibility)
+using vlsint16_t = int16_t;  ///< 16-bit signed type (backward compatibility)
+using vlsint32_t = int32_t;  ///< 32-bit signed type (backward compatibility)
+using vlsint64_t = int64_t;  ///< 64-bit signed type (backward compatibility)
+#endif
+
+#if defined(__CYGWIN__)
+
+# include   // __WORDSIZE
+# include   // ssize_t
+
+#elif defined(_WIN32) && defined(_MSC_VER)
+
+# ifndef _SSIZE_T_DEFINED
+#  ifdef _WIN64
+using ssize_t = uint64_t;  ///< signed size_t; returned from read()
+#  else
+using ssize_t = uint32_t;  ///< signed size_t; returned from read()
+#  endif
+# endif
+
+#else  // Linux or compliant Unix flavors, -m64
+
+# include   // Solaris
+# include   // __WORDSIZE
+# include   // ssize_t
+#endif
+
+//=========================================================================
+// Printing printf/scanf formats
+
+// Use Microsoft-specific format specifiers for Microsoft Visual C++ only
+// Deprecated, favor C++11's PRIx64, etc, instead
+#ifndef VL_NO_LEGACY
+# ifdef _MSC_VER
+#  define VL_PRI64 "I64"  ///< print a uint64_t (backward compatibility)
+# else  // use standard C99 format specifiers
+#  if defined(__WORDSIZE) && (__WORDSIZE == 64)
+#   define VL_PRI64 "l"  ///< print a uint64_t (backward compatibility)
+#  else
+#   define VL_PRI64 "ll"  ///< print a uint64_t (backward compatibility)
+#  endif
+# endif
+#endif
+
+#if defined(_WIN32) && defined(_MSC_VER)
+# if (_MSC_VER < 1900)
+#  define VL_SNPRINTF _snprintf
+# else
+#  define VL_SNPRINTF snprintf
+# endif
+# define VL_VSNPRINTF vsnprintf
+#else
+# define VL_SNPRINTF snprintf
+# define VL_VSNPRINTF vsnprintf
+#endif
+
+//=========================================================================
+// File system functions
+
+#ifdef _WIN32
+# define VL_DEV_NULL "nul"
+#else  // Linux or compliant Unix flavors
+# define VL_DEV_NULL "/dev/null"
+#endif
+
+//=========================================================================
+// Integer size macros
+
+#define VL_BYTESIZE 8  ///< Bits in a CData / byte
+#define VL_SHORTSIZE 16  ///< Bits in a SData / short
+#define VL_IDATASIZE 32  ///< Bits in an IData / word
+#define VL_QUADSIZE 64  ///< Bits in a QData / quadword
+#define VL_EDATASIZE 32  ///< Bits in an EData (WData entry)
+#define VL_EDATASIZE_LOG2 5  ///< log2(VL_EDATASIZE)
+#define VL_CACHE_LINE_BYTES 64  ///< Bytes in a cache line (for alignment)
+
+#ifndef VL_NO_LEGACY
+# define VL_WORDSIZE VL_IDATASIZE  // Legacy define
+#endif
+
+/// Return number of bytes argument-number of bits needs (1 bit=1 byte)
+#define VL_BYTES_I(nbits) (((nbits) + (VL_BYTESIZE - 1)) / VL_BYTESIZE)
+/// Return Words/EDatas in argument-number of bits needs (1 bit=1 word)
+#define VL_WORDS_I(nbits) (((nbits) + (VL_EDATASIZE - 1)) / VL_EDATASIZE)
+// Number of Words/EDatas a quad requires
+#define VL_WQ_WORDS_E VL_WORDS_I(VL_QUADSIZE)
+
+//=========================================================================
+// Class definition helpers
+
+// Comment tag to indicate a base class, e.g. cannot label "class final"
+#define VL_NOT_FINAL
+
+// Declare a class as uncopyable; put after a private:
+#define VL_UNCOPYABLE(Type) \
+    Type(const Type& other) = delete; \
+    Type& operator=(const Type&) = delete
+
+// Declare a class as unmovable; put after a private:
+#define VL_UNMOVABLE(Type) \
+    Type(Type&& other) = delete; \
+    Type& operator=(Type&&) = delete
+
+//=========================================================================
+// Verilated function size macros
+
+#define VL_MULS_MAX_WORDS 128  ///< Max size in words of MULS operation
+
+#ifndef VL_VALUE_STRING_MAX_WORDS
+    #define VL_VALUE_STRING_MAX_WORDS 64  ///< Max size in words of String conversion operation
+#endif
+#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS) * 4
+
+//=========================================================================
+// Base macros
+
+#define VL_SIZEBITS_I (VL_IDATASIZE - 1)  ///< Bit mask for bits in a word
+#define VL_SIZEBITS_Q (VL_QUADSIZE - 1)  ///< Bit mask for bits in a quad
+#define VL_SIZEBITS_E (VL_EDATASIZE - 1)  ///< Bit mask for bits in a quad
+
+/// Return mask for words with 1's where relevant bits are (0=all bits)
+/// Arguments must not have side effects
+#define VL_MASK_I(nbits) (((nbits) & VL_SIZEBITS_I) ? ((1U << ((nbits) & VL_SIZEBITS_I)) - 1) : ~0)
+/// Return mask for quads with 1's where relevant bits are (0=all bits)
+/// Arguments must not have side effects
+#define VL_MASK_Q(nbits) \
+    (((nbits) & VL_SIZEBITS_Q) ? ((1ULL << ((nbits) & VL_SIZEBITS_Q)) - 1ULL) : ~0ULL)
+/// Return mask for EData with 1's where relevant bits are (0=all bits)
+/// Arguments must not have side effects
+#define VL_MASK_E(nbits) VL_MASK_I(nbits)
+
+#define VL_EUL(n) VL_UL(n)  // Make constant number EData sized
+
+#define VL_BITWORD_I(bit) ((bit) / VL_IDATASIZE)  ///< Word number for sv DPI vectors
+#define VL_BITWORD_E(bit) ((bit) >> VL_EDATASIZE_LOG2)  ///< Word number for a wide quantity
+#define VL_BITBIT_I(bit) ((bit) & VL_SIZEBITS_I)  ///< Bit number for a bit in a long
+#define VL_BITBIT_Q(bit) ((bit) & VL_SIZEBITS_Q)  ///< Bit number for a bit in a quad
+#define VL_BITBIT_E(bit) ((bit) & VL_SIZEBITS_E)  ///< Bit number for a bit in an EData
+
+// Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
+#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit)))
+#define VL_BITISSET_Q(data, bit) ((data) & (1ULL << VL_BITBIT_Q(bit)))
+#define VL_BITISSET_E(data, bit) ((data) & (VL_EUL(1) << VL_BITBIT_E(bit)))
+#define VL_BITISSET_W(data, bit) ((data)[VL_BITWORD_E(bit)] & (VL_EUL(1) << VL_BITBIT_E(bit)))
+
+//=========================================================================
+// Floating point
+// #defines, to avoid requiring math.h on all compile runs
+
+#ifdef _MSC_VER
+static inline double VL_TRUNC(double n) {
+    return (n < 0) ? std::ceil(n) : std::floor(n);
+}
+static inline double VL_ROUND(double n) {
+    return (n < 0) ? std::ceil(n-0.5) : std::floor(n + 0.5);
+}
+#else
+# define VL_TRUNC(n) std::trunc(n)
+# define VL_ROUND(n) std::round(n)
+#endif
+
+//=========================================================================
+// Performance counters
+
+#if defined(__i386__) || defined(__x86_64__)
+// The uint64_t argument is loaded with a high-performance counter for profiling
+// or 0x0 if not implemented on this platform
+#define VL_GET_CPU_TICK(val) \
+    { \
+        uint32_t hi; \
+        uint32_t lo; \
+        asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); \
+        (val) = ((uint64_t)lo) | (((uint64_t)hi) << 32); \
+    }
+#elif defined(__aarch64__)
+// 1 GHz virtual system timer on SBSA level 5 compliant systems, else often 100 MHz
+# define VL_GET_CPU_TICK(val) \
+    { \
+        asm volatile("isb" : : : "memory"); \
+        asm volatile("mrs %[rt],CNTVCT_EL0" : [rt] "=r"(val)); \
+    }
+#else
+// We just silently ignore unknown OSes, as only leads to missing statistics
+# define VL_GET_CPU_TICK(val) (val) = 0;
+#endif
+
+//=========================================================================
+// Threading related OS-specific functions
+
+#ifdef _WIN32
+# define WIN32_LEAN_AND_MEAN
+# ifndef NOMINMAX
+#  define NOMINMAX
+# endif
+# include "windows.h"
+# define VL_CPU_RELAX() YieldProcessor()
+#elif defined(__i386__) || defined(__x86_64__) || defined(VL_CPPCHECK)
+// For more efficient busy waiting on SMT CPUs, let the processor know
+// we're just waiting so it can let another thread run
+# define VL_CPU_RELAX() asm volatile("rep; nop" ::: "memory")
+#elif defined(__ia64__)
+# define VL_CPU_RELAX() asm volatile("hint @pause" ::: "memory")
+#elif defined(__armel__) || defined(__ARMEL__)  // Arm, but broken, must be before __arm__
+# define VL_CPU_RELAX() asm volatile("nop" ::: "memory");
+#elif defined(__aarch64__) || defined(__arm__)
+# define VL_CPU_RELAX() asm volatile("yield" ::: "memory")
+#elif defined(__hppa__)  // HPPA does not currently have yield/pause
+# define VL_CPU_RELAX() asm volatile("nop" ::: "memory")
+#elif defined(__loongarch__)  // LoongArch does not currently have yield/pause
+# define VL_CPU_RELAX() asm volatile("nop" ::: "memory")
+#elif defined(__mips64el__) || defined(__mips__) || defined(__mips64__) || defined(__mips64)
+# define VL_CPU_RELAX() asm volatile("pause" ::: "memory")
+#elif defined(__POWERPC__) && defined(__APPLE__) // First check for a special case of macOS
+# define VL_CPU_RELAX() asm volatile("or r1, r1, r1; or r2, r2, r2;" ::: "memory")
+#elif defined(__powerpc64__) || defined(__powerpc__) // Generic powerpc
+# define VL_CPU_RELAX() asm volatile("or 1, 1, 1; or 2, 2, 2;" ::: "memory")
+#elif defined(__riscv)  // RiscV does not currently have yield/pause, but one is proposed
+# define VL_CPU_RELAX() asm volatile("nop" ::: "memory")
+#elif defined(__s390x__)
+# define VL_CPU_RELAX() asm volatile("lr 0,0" ::: "memory")
+#elif defined(__sparc__)
+# define VL_CPU_RELAX() asm volatile("rd %%ccr, %%g0" ::: "memory")
+#elif defined(VL_IGNORE_UNKNOWN_ARCH)
+# define VL_CPU_RELAX()
+#else
+# error "Missing VL_CPU_RELAX() definition."
+#endif
+
+//=========================================================================
+// String/time related OS-specific functions
+
+#ifdef _MSC_VER
+# define VL_STRCASECMP _stricmp
+#else
+# define VL_STRCASECMP strcasecmp
+#endif
+
+//=========================================================================
+// Macros controlling target-specific optimizations
+
+// Define VL_PORTABLE_ONLY to disable all target-specific optimizations
+#ifndef VL_PORTABLE_ONLY
+# ifdef __x86_64__
+#  define VL_X86_64 1
+# endif
+#endif  // VL_PORTABLE_ONLY
+// clang-format on
+
+//=========================================================================
+// Stringify macros
+
+#define VL_STRINGIFY(...) VL_STRINGIFY2(__VA_ARGS__)
+#define VL_STRINGIFY2(...) #__VA_ARGS__
+
+//=========================================================================
+// Offset of field in type
+
+// Address zero can cause compiler problems
+#define VL_OFFSETOF(type, field) \
+    (reinterpret_cast(&(reinterpret_cast(0x10000000)->field)) - 0x10000000)
+
+//=========================================================================
+// Time and performance
+
+#include 
+
+namespace VlOs {
+
+/// Get environment variable
+extern std::string getenvStr(const std::string& envvar,
+                             const std::string& defaultValue) VL_MT_SAFE;
+
+/// Return currently executing processor number; may do an OS call underneath so slow
+extern uint16_t getcpu() VL_MT_SAFE;
+
+/// Return number of processors available to the current process. This might be
+/// less than the number of logical processors in the machine, if a processor
+/// affinity mask was used, e.g. via 'numactl -C 0-3'. Returns 0 if cannot
+/// be determiend.
+extern unsigned getProcessAvailableParallelism() VL_MT_SAFE;
+
+/// Return getProcessAvailableParallelism if non-zero, otherwise the number of
+/// hardware threads in the host machine.
+extern unsigned getProcessDefaultParallelism() VL_MT_SAFE;
+
+/// Return memory usage in bytes, or 0 if unknown
+extern void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE;
+
+// Internal: Record CPU time, starting point on construction, and current delta from that
+class DeltaCpuTime final {
+    double m_start{};  // Time constructed at
+    static double gettime() VL_MT_SAFE;
+
+public:
+    // Construct, and if startit is true, start() timer
+    explicit DeltaCpuTime(bool startit) {
+        if (startit) start();
+    }
+    void start() VL_MT_SAFE { m_start = gettime(); }  // Start timer; record current time
+    double deltaTime() const VL_MT_SAFE {  // Return time between now and start()
+        return (m_start == 0.0) ? 0.0 : gettime() - m_start;
+    }
+};
+// Internal: Record wall time, starting point on construction, and current delta from that
+class DeltaWallTime final {
+    double m_start{};  // Time constructed at
+    static double gettime() VL_MT_SAFE;
+
+public:
+    // Construct, and if startit is true, start() timer
+    explicit DeltaWallTime(bool startit) {
+        if (startit) start();
+    }
+    void start() VL_MT_SAFE { m_start = gettime(); }  // Start timer; record current time
+    double deltaTime() const VL_MT_SAFE {  // Return time between now and start()
+        return (m_start == 0.0) ? 0.0 : gettime() - m_start;
+    }
+};
+
+// Used by clang's -fthread-safety, ensures that only one instance of V3ThreadScope
+// is created at a time
+class VL_CAPABILITY("mutex") MtScopeMutex final {
+public:
+    static MtScopeMutex s_haveThreadScope;
+};
+
+}  //namespace VlOs
+
+//=========================================================================
+// Conversions
+
+#include 
+
+namespace vlstd {
+
+template 
+struct reverse_wrapper final {
+    const T& m_v;
+
+    explicit reverse_wrapper(const T& a_v)
+        : m_v(a_v) {}  // Need () constructor
+    auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
+    auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
+};
+
+// C++20's std::ranges::reverse_view
+template 
+reverse_wrapper reverse_view(const T& v) {
+    return reverse_wrapper(v);
+}
+
+// C++17's std::as_const
+// `VL_MT_SAFE` annotation only applies to this function.
+// Object that is returned by this function is not considered
+// as MT_SAFE and any function call on this object still
+// needs to be `VL_MT_SAFE`.
+template 
+T const& as_const(T& v) VL_MT_SAFE {
+    return v;
+}
+
+// Utility function
+template 
+inline constexpr size_t roundUpToMultipleOf(size_t value) {
+    static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
+    return (value + N - 1) & ~(N - 1);
+}
+
+};  // namespace vlstd
+
+//=========================================================================
+
+#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos_c.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos_c.h
new file mode 100644
index 00000000000..16279d8c0c7
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos_c.h
@@ -0,0 +1,219 @@
+// -*- mode: C++; c-file-style: "cc-mode" -*-
+//*************************************************************************
+//
+// Code available from: https://verilator.org
+//
+// This program is free software; you can redistribute it and/or modify it
+// under the terms of either the GNU Lesser General Public License Version 3
+// or the Perl Artistic License Version 2.0.
+// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
+// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
+//
+//*************************************************************************
+///
+/// \file
+/// \brief Verilated/Verilator common implementation for OS portability
+///
+/// This is compiled as part of other .cpp files to reduce compile time
+/// and as such is a .h file rather than .cpp file.
+///
+//*************************************************************************
+
+#ifndef VL_ALLOW_VERILATEDOS_C
+#error "This file should be included only from V3Os.cpp/Verilated.cpp"
+#endif
+
+#include "verilatedos.h"
+
+#include 
+#include 
+
+// clang-format off
+#if defined(_WIN32) || defined(__MINGW32__)
+# include    // LONG for bcrypt.h on MINGW
+# include   // GetProcessTimes
+# include    // GetProcessMemoryInfo
+#endif
+
+#if defined(__linux)
+# include   // For sched_getcpu()
+#endif
+#if defined(__APPLE__) && !defined(__arm64__) && !defined(__POWERPC__)
+# include   // For __cpuid_count()
+#endif
+#if defined(__FreeBSD__)
+# include   // For pthread_getaffinity_np()
+#endif
+
+#if defined(__APPLE__) && defined(__MACH__)
+# include   // For task_info()
+#endif
+// clang-format on
+
+namespace VlOs {
+
+//=========================================================================
+// VlOs::VlGetCpuTime/VlGetWallTime implementation
+
+double DeltaCpuTime::gettime() VL_MT_SAFE {
+#if defined(_WIN32) || defined(__MINGW32__)
+    FILETIME lpCreationTime, lpExitTime, lpKernelTime, lpUserTime;
+    if (0
+        != GetProcessTimes(GetCurrentProcess(), &lpCreationTime, &lpExitTime, &lpKernelTime,
+                           &lpUserTime)) {
+        return static_cast(static_cast(lpUserTime.dwLowDateTime)
+                                   | static_cast(lpUserTime.dwHighDateTime) << 32ULL)
+               * 1e-7;
+    }
+#else
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
+    timespec ts;
+    if (0 == clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts))  // MT-Safe  // LCOV_EXCL_BR_LINE
+        return ts.tv_sec + ts.tv_nsec * 1e-9;
+#endif
+    return 0.0;  // LCOV_EXCL_LINE
+}
+double DeltaWallTime::gettime() VL_MT_SAFE {
+#if defined(_WIN32) || defined(__MINGW32__)
+    FILETIME ft;  // contains number of 0.1us intervals since the beginning of 1601 UTC.
+    GetSystemTimeAsFileTime(&ft);
+    const uint64_t tenthus
+        = ((static_cast(ft.dwHighDateTime) << 32) + ft.dwLowDateTime + 5ULL);
+    return static_cast(tenthus) * 1e-7;
+#else
+    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
+    timespec ts;
+    if (0 == clock_gettime(CLOCK_MONOTONIC, &ts))  // MT-Safe  // LCOV_EXCL_BR_LINE
+        return ts.tv_sec + ts.tv_nsec * 1e-9;
+    return 0.0;  // LCOV_EXCL_LINE
+#endif
+}
+
+//=============================================================================
+// Vlos::getcpu implementation
+
+uint16_t getcpu() VL_MT_SAFE {
+#if defined(__linux)
+    return sched_getcpu();  // TODO: this is a system call. Not exactly cheap.
+#elif defined(__APPLE__) && !defined(__arm64__) && !defined(__POWERPC__)
+    uint32_t info[4];
+    __cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
+    // info[1] is EBX, bits 24-31 are APIC ID
+    if ((info[3] & (1 << 9)) == 0) {
+        return 0;  // no APIC on chip
+    } else {
+        return (unsigned)info[1] >> 24;
+    }
+#elif defined(_WIN32)
+    return GetCurrentProcessorNumber();
+#else
+    return 0;
+#endif
+}
+
+//=============================================================================
+// Vlos::getProcessAvailableParallelism implementation
+
+unsigned getProcessAvailableParallelism() VL_MT_SAFE {
+#if defined(__linux) || defined(CPU_ZERO)  // Linux-like; assume we have pthreads etc
+    cpu_set_t cpuset;
+    CPU_ZERO(&cpuset);
+    const int rc = pthread_getaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
+    if (rc == 0) {
+        unsigned nCpus = 0;
+        for (int i = 0; i < CPU_SETSIZE; ++i) {
+            if (CPU_ISSET(i, &cpuset)) ++nCpus;
+        }
+        return nCpus;
+    }
+#endif
+    // Cannot determine
+    return 0;
+}
+
+//=============================================================================
+// Vlos::getProcessDefaultParallelism implementation
+
+unsigned getProcessDefaultParallelism() VL_MT_SAFE {
+    const unsigned n = getProcessAvailableParallelism();
+    // cppcheck-suppress knownConditionTrueFalse
+    return n ? n : std::thread::hardware_concurrency();
+}
+
+//=========================================================================
+// VlOs::memPeakUsageBytes implementation
+
+void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
+    peakr = 0;
+    currentr = 0;
+#if defined(_WIN32) || defined(__MINGW32__)
+    const HANDLE process = GetCurrentProcess();
+    PROCESS_MEMORY_COUNTERS pmc;
+    if (GetProcessMemoryInfo(process, &pmc, sizeof(pmc))) {
+        // The best we can do using simple Windows APIs is to get the size of the working set.
+        peakr = pmc.PeakWorkingSetSize;
+        currentr = pmc.WorkingSetSize;
+    }
+#elif defined(__APPLE__) && defined(__MACH__)
+    mach_task_basic_info_data_t info;
+    mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
+    const kern_return_t ret
+        = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &count);
+    if (ret == KERN_SUCCESS && count == MACH_TASK_BASIC_INFO_COUNT) {
+        peakr = info.resident_size_max;
+        currentr = info.resident_size;
+    }
+#else
+    // Highly unportable. Sorry
+    std::ifstream is{"/proc/self/status"};
+    if (!is) return;
+    std::string line;
+    uint64_t vmPeak = 0;
+    uint64_t vmRss = 0;
+    uint64_t vmSwap = 0;
+    std::string field;
+    while (std::getline(is, line)) {
+        if (line.rfind("VmPeak:", 0) == 0) {
+            std::stringstream ss{line};
+            ss >> field >> vmPeak;
+        } else if (line.rfind("VmRSS:", 0) == 0) {
+            std::stringstream ss{line};
+            ss >> field >> vmRss;
+        } else if (line.rfind("VmSwap:", 0) == 0) {
+            std::stringstream ss{line};
+            ss >> field >> vmSwap;
+        }
+    }
+    peakr = vmPeak * 1024;
+    currentr = (vmRss + vmSwap) * 1024;
+#endif
+}
+
+//=========================================================================
+// VlOs::getenvStr implementation
+
+std::string getenvStr(const std::string& envvar, const std::string& defaultValue) VL_MT_SAFE {
+    std::string ret;
+#if defined(_MSC_VER)
+    // Note: MinGW does not offer _dupenv_s
+    const char* envvalue = nullptr;
+    _dupenv_s((char**)&envvalue, nullptr, envvar.c_str());
+    if (envvalue != nullptr) {
+        const std::string result{envvalue};
+        free((void*)envvalue);
+        ret = result;
+    } else {
+        ret = defaultValue;
+    }
+#else
+    if (const char* const envvalue = getenv(envvar.c_str())) {
+        ret = envvalue;
+    } else {
+        ret = defaultValue;
+    }
+#endif
+    return ret;
+}
+
+//=========================================================================
+}  //namespace VlOs

From 411097cdb3f95f67831b72a891c3c866d614f328 Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:02:19 +0800
Subject: [PATCH 04/18] feat: support emulated tls for pthread

---
 components/libc/posix/pthreads/pthread.c      | 67 ++++++++++++++++---
 .../libc/posix/pthreads/pthread_internal.h    |  2 +
 components/libc/posix/pthreads/pthread_tls.c  | 13 ++--
 3 files changed, 67 insertions(+), 15 deletions(-)

diff --git a/components/libc/posix/pthreads/pthread.c b/components/libc/posix/pthreads/pthread.c
index ff5a1196cca..05818fb6a52 100644
--- a/components/libc/posix/pthreads/pthread.c
+++ b/components/libc/posix/pthreads/pthread.c
@@ -23,6 +23,9 @@ RT_DEFINE_HW_SPINLOCK(pth_lock);
 _pthread_data_t *pth_table[PTHREAD_NUM_MAX] = {NULL};
 static int concurrency_level;
 
+static void _pthread_cleanup(rt_thread_t tid);
+static void _pthread_cleanup_borrowed(rt_thread_t tid);
+
 /**
  * @brief   Retrieves the private data structure of a specified thread
  *
@@ -270,6 +273,37 @@ void _pthread_data_destroy(_pthread_data_t *ptd)
     }
 }
 
+_pthread_data_t *_pthread_get_self_data(rt_bool_t create)
+{
+    rt_thread_t tid;
+    pthread_t pth_id;
+    _pthread_data_t *ptd;
+
+    tid = rt_thread_self();
+    if (tid == RT_NULL)
+        return RT_NULL;
+
+    ptd = (_pthread_data_t *)tid->pthread_data;
+    if ((ptd != RT_NULL) || !create)
+        return ptd;
+
+    pth_id = _pthread_data_create();
+    if (pth_id == PTHREAD_NUM_MAX)
+        return RT_NULL;
+
+    ptd = _pthread_get_data(pth_id);
+    if (ptd == RT_NULL)
+        return RT_NULL;
+
+    pthread_attr_init(&ptd->attr);
+    ptd->tid = tid;
+    ptd->thread_cleanup = tid->cleanup;
+    tid->cleanup = _pthread_cleanup_borrowed;
+    tid->pthread_data = (void *)ptd;
+
+    return ptd;
+}
+
 /**
  * @brief Perform final cleanup of thread resources during thread termination
  *
@@ -305,6 +339,28 @@ static void _pthread_cleanup(rt_thread_t tid)
     rt_free(tid);
 }
 
+static void _pthread_cleanup_borrowed(rt_thread_t tid)
+{
+    _pthread_data_t *ptd;
+    void (*thread_cleanup)(rt_thread_t tid) = RT_NULL;
+
+    ptd = (_pthread_data_t *)tid->pthread_data;
+
+    tid->cleanup = RT_NULL;
+    tid->pthread_data = RT_NULL;
+
+    if (ptd != RT_NULL)
+    {
+        thread_cleanup = ptd->thread_cleanup;
+        _pthread_data_destroy(ptd);
+    }
+
+    if (thread_cleanup != RT_NULL)
+    {
+        thread_cleanup(tid);
+    }
+}
+
 /**
  * @brief Thread entry point stub that manages thread execution and resource cleanup
  *
@@ -664,15 +720,11 @@ RTM_EXPORT(pthread_join);
  */
 pthread_t pthread_self (void)
 {
-    rt_thread_t tid;
     _pthread_data_t *ptd;
 
-    tid = rt_thread_self();
-    if (tid == NULL) return PTHREAD_NUM_MAX;
-
-    /* get pthread data from pthread_data of thread */
-    ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
-    RT_ASSERT(ptd != RT_NULL);
+    ptd = _pthread_get_self_data(RT_TRUE);
+    if (ptd == RT_NULL)
+        return PTHREAD_NUM_MAX;
 
     return _pthread_data_get_pth(ptd);
 }
@@ -1503,4 +1555,3 @@ int pthread_cancel(pthread_t thread)
     return 0;
 }
 RTM_EXPORT(pthread_cancel);
-
diff --git a/components/libc/posix/pthreads/pthread_internal.h b/components/libc/posix/pthreads/pthread_internal.h
index 03db46acb64..2d8a27b15d4 100644
--- a/components/libc/posix/pthreads/pthread_internal.h
+++ b/components/libc/posix/pthreads/pthread_internal.h
@@ -41,6 +41,7 @@ struct _pthread_data
     rt_uint32_t magic;
     pthread_attr_t attr;
     rt_thread_t tid;
+    void (*thread_cleanup)(rt_thread_t tid);
 
     void* (*thread_entry)(void *parameter);
     void *thread_parameter;
@@ -62,5 +63,6 @@ struct _pthread_data
 typedef struct _pthread_data _pthread_data_t;
 
 _pthread_data_t *_pthread_get_data(pthread_t thread);
+_pthread_data_t *_pthread_get_self_data(rt_bool_t create);
 
 #endif
diff --git a/components/libc/posix/pthreads/pthread_tls.c b/components/libc/posix/pthreads/pthread_tls.c
index 996c117a5b8..059643dad56 100644
--- a/components/libc/posix/pthreads/pthread_tls.c
+++ b/components/libc/posix/pthreads/pthread_tls.c
@@ -52,9 +52,9 @@ void *pthread_getspecific(pthread_key_t key)
 
     if (rt_thread_self() == NULL) return NULL;
 
-    /* get pthread data from user data of thread */
-    ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
-    RT_ASSERT(ptd != NULL);
+    ptd = _pthread_get_self_data(RT_TRUE);
+    if (ptd == NULL)
+        return NULL;
 
     if (ptd->tls == NULL)
         return NULL;
@@ -103,9 +103,9 @@ int pthread_setspecific(pthread_key_t key, const void *value)
 
     if (rt_thread_self() == NULL) return EINVAL;
 
-    /* get pthread data from user data of thread */
-    ptd = (_pthread_data_t *)rt_thread_self()->pthread_data;
-    RT_ASSERT(ptd != NULL);
+    ptd = _pthread_get_self_data(RT_TRUE);
+    if (ptd == NULL)
+        return EINVAL;
 
     /* check tls area */
     if (ptd->tls == NULL)
@@ -215,4 +215,3 @@ int pthread_key_delete(pthread_key_t key)
     return 0;
 }
 RTM_EXPORT(pthread_key_delete);
-

From 36e05758e15ee914b1467be9aa8b96ba1c026af3 Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:05:30 +0800
Subject: [PATCH 05/18] fix: chech `__STDCPP_THREADS__` before define it

---
 components/libc/cplusplus/cpp11/gcc/thread | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/components/libc/cplusplus/cpp11/gcc/thread b/components/libc/cplusplus/cpp11/gcc/thread
index 60e3ec964ca..829012f24fc 100644
--- a/components/libc/cplusplus/cpp11/gcc/thread
+++ b/components/libc/cplusplus/cpp11/gcc/thread
@@ -27,7 +27,9 @@
 
 namespace std
 {
+    #ifndef __STDCPP_THREADS__
     #define __STDCPP_THREADS__ __cplusplus
+    #endif
 
     
 

From e07d92e97c8d1124e8938ef135ab36e5834d4efc Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:06:00 +0800
Subject: [PATCH 06/18] fix: do not override clang builtin atomic function

---
 components/libc/cplusplus/cpp11/atomic_8.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/components/libc/cplusplus/cpp11/atomic_8.c b/components/libc/cplusplus/cpp11/atomic_8.c
index 65fe22abcf7..df18367a3bc 100644
--- a/components/libc/cplusplus/cpp11/atomic_8.c
+++ b/components/libc/cplusplus/cpp11/atomic_8.c
@@ -13,6 +13,8 @@
 #include 
 #include 
 
+#ifndef __clang__
+
 /*
 * override gcc builtin atomic function for std::atomic, std::atomic
 * @see https://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
@@ -165,3 +167,5 @@ __atomic_fetch_op_8(sub, -)
 __atomic_fetch_op_8( and, &)
 __atomic_fetch_op_8( or, |)
 __atomic_fetch_op_8(xor, ^)
+
+#endif /* __clang__ */

From 221e74554c5551b6d66dff86a42edab6eec4ee15 Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:06:44 +0800
Subject: [PATCH 07/18] fix: add missing header when using llvm

---
 components/libc/cplusplus/cpp11/SConscript | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/components/libc/cplusplus/cpp11/SConscript b/components/libc/cplusplus/cpp11/SConscript
index 0d96a062694..ef7b218bf38 100644
--- a/components/libc/cplusplus/cpp11/SConscript
+++ b/components/libc/cplusplus/cpp11/SConscript
@@ -9,7 +9,7 @@ src += Glob('*.cpp') + Glob('*.c')
 if rtconfig.PLATFORM in ['armclang']:
     src += Glob('armclang/*.cpp') + Glob('armclang/*.c')
     CPPPATH += [cwd + '/armclang']
-elif rtconfig.PLATFORM in ['gcc']:
+elif rtconfig.PLATFORM in ['gcc', 'llvm-riscv']:
     src += Glob('gcc/*.cpp') + Glob('gcc/*.c')
     CPPPATH += [cwd + '/gcc']
 

From bc909daa2b91a5b6d8c64ea14dc7bbe95ac3f117 Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:07:29 +0800
Subject: [PATCH 08/18] feat: override cstring header for extended lib func

---
 components/libc/cplusplus/cpp11/gcc/cstring | 180 ++++++++++++++++++++
 1 file changed, 180 insertions(+)
 create mode 100644 components/libc/cplusplus/cpp11/gcc/cstring

diff --git a/components/libc/cplusplus/cpp11/gcc/cstring b/components/libc/cplusplus/cpp11/gcc/cstring
new file mode 100644
index 00000000000..5140dc22898
--- /dev/null
+++ b/components/libc/cplusplus/cpp11/gcc/cstring
@@ -0,0 +1,180 @@
+// -*- C++ -*- forwarding header.
+
+// Copyright (C) 1997-2025 Free Software Foundation, Inc.
+//
+// This file is part of the GNU ISO C++ Library.  This library is free
+// software; you can redistribute it and/or modify it under the
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// Under Section 7 of GPL version 3, you are granted additional
+// permissions described in the GCC Runtime Library Exception, version
+// 3.1, as published by the Free Software Foundation.
+
+// You should have received a copy of the GNU General Public License and
+// a copy of the GCC Runtime Library Exception along with this program;
+// see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+// .
+
+/** @file cstring
+ *  This is a Standard C++ Library file.  You should @c \#include this file
+ *  in your programs, rather than any of the @a *.h implementation files.
+ *
+ *  This is the C++ version of the Standard C Library header @c string.h,
+ *  and its contents are (mostly) the same as that header, but are all
+ *  contained in the namespace @c std (except for names which are defined
+ *  as macros in C).
+ */
+
+//
+// ISO C++ 14882: 20.4.6  C library
+//
+
+#ifndef _GLIBCXX_CSTRING
+#define _GLIBCXX_CSTRING 1
+
+#ifdef _GLIBCXX_SYSHDR
+#pragma GCC system_header
+#endif
+
+#define __glibcxx_want_freestanding_cstring
+#include 
+#include 
+
+// Get rid of those macros defined in  in lieu of real functions.
+#undef memchr
+#undef memcmp
+#undef memcpy
+#undef memmove
+#undef memset
+#undef strcat
+#undef strchr
+#undef strcmp
+#undef strcoll
+#undef strcpy
+#undef strcspn
+#undef strerror
+#undef strlen
+#undef strncat
+#undef strncmp
+#undef strncpy
+#undef strpbrk
+#undef strrchr
+#undef strspn
+#undef strstr
+#undef strtok
+#undef strxfrm
+
+extern "C++"
+{
+namespace std _GLIBCXX_VISIBILITY(default)
+{
+_GLIBCXX_BEGIN_NAMESPACE_VERSION
+
+  using ::memchr;
+  using ::memcmp;
+  using ::memcpy;
+  using ::memmove;
+  using ::memset;
+  using ::strcat;
+  using ::strcmp;
+  using ::strcoll;
+  using ::strcpy;
+  using ::strcspn;
+  using ::strerror;
+  using ::strlen;
+  using ::strncat;
+  using ::strncmp;
+  using ::strncpy;
+  using ::strspn;
+#if _GLIBCXX_HOSTED || __cplusplus <= 202302L
+  using ::strtok;
+#endif
+  using ::strxfrm;
+  using ::strchr;
+  using ::strpbrk;
+  using ::strrchr;
+  using ::strstr;
+
+#ifndef __CORRECT_ISO_CPP_STRING_H_PROTO
+  inline void*
+  memchr(void* __s, int __c, size_t __n)
+  { return __builtin_memchr(__s, __c, __n); }
+
+  inline char*
+  strchr(char* __s, int __n)
+  { return __builtin_strchr(__s, __n); }
+
+  inline char*
+  strpbrk(char* __s1, const char* __s2)
+  { return __builtin_strpbrk(__s1, __s2); }
+
+  inline char*
+  strrchr(char* __s, int __n)
+  { return __builtin_strrchr(__s, __n); }
+
+  inline char*
+  strstr(char* __s1, const char* __s2)
+  { return __builtin_strstr(__s1, __s2); }
+#endif
+
+  // extension standard C functions in rt-thread
+  void bzero(void * s, size_t n);
+  void bcopy(const void * src, void * dest, size_t n);
+  int bcmp(const void * s1, const void * s2, size_t n);
+  void explicit_bzero(void * s, size_t n);
+  char *index(const char * s, int c);
+  char *rindex(const char * s, int c);
+  int ffs(int i);
+  int ffsl(long i);
+  int ffsll(long long i);
+  void *memrchr(const void* ptr, int ch, size_t pos);
+  size_t strnlen(const char *s, size_t maxlen);
+  char* strchrnul(const char *s, int c);
+  int strcasecmp(const char * s1, const char * s2);
+  int strncasecmp(const char * s1, const char * s2, size_t n);
+  char *strdup(const char *s);
+  char *strndup(const char *s, size_t size);
+  char *strtok_r(char *str, const char *delim, char **saveptr);
+
+_GLIBCXX_END_NAMESPACE_VERSION
+} // namespace
+} // extern "C++"
+
+// from rt-thread libc, for extension standard C functions in rt-thread
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include 
+#include 
+
+void bzero(void * s, size_t n);
+void bcopy(const void * src, void * dest, size_t n);
+int bcmp(const void * s1, const void * s2, size_t n);
+void explicit_bzero(void * s, size_t n);
+char *index(const char * s, int c);
+char *rindex(const char * s, int c);
+int ffs(int i);
+int ffsl(long i);
+int ffsll(long long i);
+void *memrchr(const void* ptr, int ch, size_t pos);
+size_t strnlen(const char *s, size_t maxlen);
+char* strchrnul(const char *s, int c);
+int strcasecmp(const char * s1, const char * s2);
+int strncasecmp(const char * s1, const char * s2, size_t n);
+char *strdup(const char *s);
+char *strndup(const char *s, size_t size);
+char *strtok_r(char *str, const char *delim, char **saveptr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

From 27977845c2711d2970abdcd0cc47e78652b16462 Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:08:12 +0800
Subject: [PATCH 09/18] build: define _XOPEN_SOURCE to enable more libc func

---
 components/libc/compilers/newlib/SConscript | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/components/libc/compilers/newlib/SConscript b/components/libc/compilers/newlib/SConscript
index 87b1f3345fb..b42ead6596d 100644
--- a/components/libc/compilers/newlib/SConscript
+++ b/components/libc/compilers/newlib/SConscript
@@ -14,7 +14,7 @@ if newlib_version and not GetDepend('RT_USING_EXTERNAL_LIBC'):
     src = Glob('*.c')
 
     CPPPATH = [cwd]
-    CPPDEFINES = ['RT_USING_NEWLIBC', 'RT_USING_LIBC', '_POSIX_C_SOURCE=1'] # identify this is Newlib, and only enable POSIX.1-1990
+    CPPDEFINES = ['RT_USING_NEWLIBC', 'RT_USING_LIBC', '_POSIX_C_SOURCE=199309L', '_XOPEN_SOURCE=700'] # identify this is Newlib, and only enable POSIX.1-1990
     LIBS = ['c', 'm'] # link libc and libm
     AddDepend(['RT_USING_NEWLIBC', 'RT_USING_LIBC'])
 

From 4e82178a0aa9971268cb2e1c32050a3adb1388fc Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:20:09 +0800
Subject: [PATCH 10/18] build: enable pthread and cpp11

---
 bsp/xiangshan-verilator/.config    | 5 +++--
 bsp/xiangshan-verilator/rtconfig.h | 3 +++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/bsp/xiangshan-verilator/.config b/bsp/xiangshan-verilator/.config
index 8ed52cd1c3e..1079575c187 100644
--- a/bsp/xiangshan-verilator/.config
+++ b/bsp/xiangshan-verilator/.config
@@ -367,7 +367,8 @@ CONFIG_RT_USING_POSIX_MMAN=y
 CONFIG_RT_USING_POSIX_DELAY=y
 CONFIG_RT_USING_POSIX_CLOCK=y
 CONFIG_RT_USING_POSIX_TIMER=y
-# CONFIG_RT_USING_PTHREADS is not set
+CONFIG_RT_USING_PTHREADS=y
+CONFIG_PTHREAD_NUM_MAX=8
 # CONFIG_RT_USING_MODULE is not set
 
 #
@@ -385,7 +386,7 @@ CONFIG_RT_USING_POSIX_PIPE_SIZE=512
 # end of POSIX (Portable Operating System Interface) layer
 
 CONFIG_RT_USING_CPLUSPLUS=y
-# CONFIG_RT_USING_CPLUSPLUS11 is not set
+CONFIG_RT_USING_CPLUSPLUS11=y
 CONFIG_RT_USING_CPP_WRAPPER=y
 # CONFIG_RT_USING_CPP_EXCEPTIONS is not set
 # end of C/C++ and POSIX layer
diff --git a/bsp/xiangshan-verilator/rtconfig.h b/bsp/xiangshan-verilator/rtconfig.h
index d610a63bd8a..d63d57ca77b 100644
--- a/bsp/xiangshan-verilator/rtconfig.h
+++ b/bsp/xiangshan-verilator/rtconfig.h
@@ -228,6 +228,8 @@
 #define RT_USING_POSIX_DELAY
 #define RT_USING_POSIX_CLOCK
 #define RT_USING_POSIX_TIMER
+#define RT_USING_PTHREADS
+#define PTHREAD_NUM_MAX 8
 
 /* Interprocess Communication (IPC) */
 
@@ -239,6 +241,7 @@
 /* end of Interprocess Communication (IPC) */
 /* end of POSIX (Portable Operating System Interface) layer */
 #define RT_USING_CPLUSPLUS
+#define RT_USING_CPLUSPLUS11
 #define RT_USING_CPP_WRAPPER
 /* end of C/C++ and POSIX layer */
 

From 639c93c9b02358b20fbede684348c3ef580b947f Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:20:45 +0800
Subject: [PATCH 11/18] build: enable emulated tls

---
 bsp/xiangshan-verilator/rtconfig.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/bsp/xiangshan-verilator/rtconfig.py b/bsp/xiangshan-verilator/rtconfig.py
index d70d9fc1a02..4f9e16b5f89 100644
--- a/bsp/xiangshan-verilator/rtconfig.py
+++ b/bsp/xiangshan-verilator/rtconfig.py
@@ -33,7 +33,8 @@
     OBJCPY  = PREFIX + 'objcopy'
 
     DEVICE  = ' -mcmodel=medany -march=rv64imac -mabi=lp64 '
-    CFLAGS  = DEVICE + '-fno-omit-frame-pointer -flax-vector-conversions -Wno-cpp -fno-common -ffunction-sections -fdata-sections -fdiagnostics-color=always -Xclang -fexperimental-max-bitint-width=20000 -fbracket-depth=2048 -Wno-parentheses-equality -DGSIM'
+    # Enable emulated TLS
+    CFLAGS  = DEVICE + '-fno-omit-frame-pointer -flax-vector-conversions -Wno-cpp -fno-common -ffunction-sections -fdata-sections -fdiagnostics-color=always -Xclang -fexperimental-max-bitint-width=20000 -fbracket-depth=2048 -Wno-parentheses-equality -DGSIM -femulated-tls'
     AFLAGS  = ' -c' + DEVICE + ' -x assembler-with-cpp -D__ASSEMBLY__ '
     LFLAGS  = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,_start -T link.lds' + ' -stdlib=libstdc++ -lc -lsupc++ -lgcc -lstdc++ -static'
     CPATH   = ''

From 9e4d828e53921c9d609746750937a4b9779516f0 Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:22:49 +0800
Subject: [PATCH 12/18] chore: purge copied and modified verilator runtime

---
 .../verilator_runtime/verilated.cpp           | 3726 ---------------
 .../verilator_runtime/verilated.h             | 1056 -----
 .../verilator_runtime/verilated_config.h      |   30 -
 .../verilator_runtime/verilated_cov.cpp       |  541 ---
 .../verilator_runtime/verilated_cov.h         |  249 -
 .../verilator_runtime/verilated_cov_key.h     |   85 -
 .../verilator_runtime/verilated_dpi.cpp       |  814 ----
 .../verilator_runtime/verilated_dpi.h         |  112 -
 .../verilator_runtime/verilated_fst_c.cpp     |  405 --
 .../verilator_runtime/verilated_fst_c.h       |  262 --
 .../verilator_runtime/verilated_fst_sc.cpp    |   24 -
 .../verilator_runtime/verilated_fst_sc.h      |   56 -
 .../verilator_runtime/verilated_funcs.h       | 3059 -------------
 .../verilator_runtime/verilated_imp.h         |  616 ---
 .../verilator_runtime/verilated_intrinsics.h  |   45 -
 .../verilator_runtime/verilated_probdist.cpp  |  240 -
 .../verilator_runtime/verilated_profiler.cpp  |  225 -
 .../verilator_runtime/verilated_profiler.h    |  305 --
 .../verilator_runtime/verilated_random.cpp    |  966 ----
 .../verilator_runtime/verilated_random.h      |  677 ---
 .../verilator_runtime/verilated_saif_c.cpp    |  669 ---
 .../verilator_runtime/verilated_saif_c.h      |  292 --
 .../verilator_runtime/verilated_saif_sc.h     |   56 -
 .../verilator_runtime/verilated_save.cpp      |  270 --
 .../verilator_runtime/verilated_save.h        |  335 --
 .../verilator_runtime/verilated_sc.h          |   52 -
 .../verilator_runtime/verilated_sc_trace.h    |  247 -
 .../verilator_runtime/verilated_sym_props.h   |  278 --
 .../verilator_runtime/verilated_syms.h        |   75 -
 .../verilator_runtime/verilated_threads.cpp   |  281 --
 .../verilator_runtime/verilated_threads.h     |  260 --
 .../verilator_runtime/verilated_timing.cpp    |  271 --
 .../verilator_runtime/verilated_timing.h      |  482 --
 .../verilator_runtime/verilated_trace.h       |  648 ---
 .../verilator_runtime/verilated_trace_imp.h   |  936 ----
 .../verilator_runtime/verilated_types.h       | 2139 ---------
 .../verilator_runtime/verilated_vcd_c.cpp     |  685 ---
 .../verilator_runtime/verilated_vcd_c.h       |  329 --
 .../verilator_runtime/verilated_vcd_sc.cpp    |   24 -
 .../verilator_runtime/verilated_vcd_sc.h      |   57 -
 .../verilator_runtime/verilated_vpi.cpp       | 4029 -----------------
 .../verilator_runtime/verilated_vpi.h         |   72 -
 .../verilator_runtime/verilatedos.h           |  753 ---
 .../verilator_runtime/verilatedos_c.h         |  219 -
 44 files changed, 26952 deletions(-)
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_config.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov_key.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_funcs.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_imp.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_intrinsics.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_probdist.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_sc.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc_trace.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sym_props.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_syms.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace_imp.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_types.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.cpp
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos.h
 delete mode 100644 bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos_c.h

diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.cpp
deleted file mode 100644
index 86a891df8e5..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.cpp
+++ /dev/null
@@ -1,3726 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=========================================================================
-///
-/// \file
-/// \brief Verilated general routine implementation code
-///
-/// This file must be compiled and linked against all Verilated objects
-/// (all code created from Verilator).
-///
-/// Verilator always adds this file to the Makefile for the linker.
-///
-/// Those macro/function/variable starting or ending in _ are internal,
-/// however many of the other function/macros here are also internal.
-///
-//=========================================================================
-// Internal note:
-//
-// verilated.o may exist both in --lib-create (incrementally linked .a/.so)
-// and the main module.  Both refer the same instance of static
-// variables/thread_local in verilated.o such as Verilated, or
-// VerilatedImpData.  This is important to share that state, but the
-// sharing may cause a double-free error when shutting down because the
-// loader will insert a constructor/destructor at each reference to
-// verilated.o, resulting in at runtime constructors/destructors being
-// called multiple times.
-//
-// To avoid the trouble:
-//   * Statics declared inside functions. The compiler will wrap
-//     the construction in must-be-one-time checks.
-//   * Or, use only C++20 constinit types. (TODO: Make a VL_CONSTINIT).
-//   * Or, use types that are multi-constructor safe.
-//   * Or, the static should be of a union, which will avoid compiler
-//     construction, and appropriately check for duplicate construction.
-//   * Or, code is not linked in protected library. e.g. the VPI
-//     and DPI libraries are not needed there.
-//=========================================================================
-
-#define VERILATOR_VERILATED_CPP_
-
-#include "verilated_config.h"
-#include "verilatedos.h"
-
-#include "verilated_imp.h"
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-#include   // mkdir
-
-// clang-format off
-#if defined(_WIN32) || defined(__MINGW32__)
-# include   // mkdir
-#endif
-#ifdef __GLIBC__
-# include 
-# include 
-# define _VL_HAVE_STACKTRACE
-#endif
-#if defined(__unix__) || (defined(__APPLE__) && defined(__MACH__))
-# include 
-# include 
-# define _VL_HAVE_GETRLIMIT
-#endif
-
-#include "verilated_threads.h"
-// clang-format on
-
-#include "verilated_trace.h"
-
-#ifdef VM_SOLVER_DEFAULT
-#define VL_SOLVER_DEFAULT VM_SOLVER_DEFAULT
-#else
-#define VL_SOLVER_DEFAULT "z3 --in"
-#endif
-
-// Max characters in static char string for VL_VALUE_STRING
-constexpr unsigned VL_VALUE_STRING_MAX_WIDTH = 8192;
-
-//===========================================================================
-// Static sanity checks
-
-static_assert(sizeof(uint8_t) == 1, "uint8_t is missized");
-static_assert(sizeof(uint16_t) == 2, "uint8_t is missized");
-static_assert(sizeof(uint32_t) == 4, "uint8_t is missized");
-static_assert(sizeof(uint64_t) == 8, "uint8_t is missized");
-
-//===========================================================================
-// Global variables
-// Internal note: Globals may multi-construct, see verilated.cpp top.
-
-// Fast path, keep together
-int Verilated::s_debug = 0;
-VerilatedContext* Verilated::s_lastContextp = nullptr;
-
-// Keep below together in one cache line
-// Internal note: Globals may multi-construct, see verilated.cpp top.
-thread_local Verilated::ThreadLocal Verilated::t_s;
-
-//===========================================================================
-// Warning print helper
-
-void vl_print_warn_error(const char* prefix, const char* filename, int linenum,
-                         const char* msg) VL_MT_UNSAFE {
-    // A msg of "ERRORCODE: ..." is a code that changes to a prefix, e.g. "%Error-ERRORCODE: ..."
-    // This avoids changing public API of the vl_stop and related functions.
-    const char* msgNoCp = msg;
-    for (; isupper(*msgNoCp); ++msgNoCp);
-    if (msgNoCp[0] == ':' && msgNoCp[1] == ' ') {
-        const int codeWidth = static_cast(msgNoCp - msg);
-        msgNoCp += 2;
-        if (filename && filename[0]) {
-            VL_PRINTF(  // Not VL_PRINTF_MT, already on main thread
-                "%s-%.*s: %s:%d: %s\n", prefix, codeWidth, msg, filename, linenum, msgNoCp);
-        } else {
-            VL_PRINTF(  // Not VL_PRINTF_MT, already on main thread
-                "%s-%.*s: %s\n", prefix, codeWidth, msg, msgNoCp);
-        }
-    } else {
-        if (filename && filename[0]) {
-            VL_PRINTF(  // Not VL_PRINTF_MT, already on main thread
-                "%s: %s:%d: %s\n", prefix, filename, linenum, msg);
-        } else {
-            VL_PRINTF(  // Not VL_PRINTF_MT, already on main thread
-                "%s: %s\n", prefix, msg);
-        }
-    }
-}
-
-//===========================================================================
-// User definable functions
-// Note a TODO is a future version of the API will pass a structure so that
-// the calling arguments allow for extension
-
-#ifndef VL_USER_FINISH  ///< Define this to override the vl_finish function
-void vl_finish(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
-    (void)hier;  // hier is unused in the default implementation.
-    VL_PRINTF(  // Not VL_PRINTF_MT, already on main thread
-        "- %s:%d: Verilog $finish\n", filename, linenum);
-    Verilated::threadContextp()->gotFinish(true);
-}
-#endif
-
-#ifndef VL_USER_STOP  ///< Define this to override the vl_stop function
-void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE {
-    // $stop or $fatal reporting; would break current API to add param as to which
-    if (Verilated::threadContextp()->gotFinish()) return;
-    const char* const msg = "Verilog $stop";
-    Verilated::threadContextp()->gotError(true);
-    Verilated::threadContextp()->gotFinish(true);
-    if (Verilated::threadContextp()->fatalOnError()) {
-        vl_fatal(filename, linenum, hier, msg);
-    } else {
-        vl_print_warn_error("%Error", filename, linenum, msg);
-        Verilated::runFlushCallbacks();
-    }
-}
-#endif
-
-#ifndef VL_USER_FATAL  ///< Define this to override the vl_fatal function
-void vl_fatal(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE {
-    (void)hier;  // hier is unused in the default implementation.
-    Verilated::threadContextp()->gotError(true);
-    Verilated::threadContextp()->gotFinish(true);
-    vl_print_warn_error("%Error", filename, linenum, msg);
-    Verilated::runFlushCallbacks();
-
-    VL_PRINTF("Aborting...\n");  // Not VL_PRINTF_MT, already on main thread
-
-    // Second flush in case VL_PRINTF does something needing a flush
-    Verilated::runFlushCallbacks();
-
-    // Callbacks prior to termination
-    Verilated::runExitCallbacks();
-
-    if (Verilated::debug()) {
-        std::abort();
-    } else {
-        std::exit(1);
-    }
-}
-#endif
-
-#ifndef VL_USER_STOP_MAYBE  ///< Define this to override the vl_stop_maybe function
-void vl_stop_maybe(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_UNSAFE {
-    // $stop or $fatal
-    Verilated::threadContextp()->errorCountInc();
-    if (maybe
-        && Verilated::threadContextp()->errorCount() < Verilated::threadContextp()->errorLimit()) {
-        // Do just once when cross error limit
-        if (Verilated::threadContextp()->errorCount() == 1) {
-            vl_print_warn_error("-Info", filename, linenum,
-                                "Verilog $stop, ignored due to +verilator+error+limit");
-        }
-    } else {
-        vl_stop(filename, linenum, hier);
-    }
-}
-#endif
-
-#ifndef VL_USER_WARN  ///< Define this to override the vl_warn function
-void vl_warn(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_UNSAFE {
-    (void)hier;  // hier is unused in the default implementation.
-    vl_print_warn_error("%Warning", filename, linenum, msg);
-    Verilated::runFlushCallbacks();
-}
-#endif
-
-//===========================================================================
-// Wrapper to call certain functions via messages when multithreaded
-
-void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE {
-    VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() {  //
-        vl_finish(filename, linenum, hier);
-    }});
-}
-
-void VL_STOP_MT(const char* filename, int linenum, const char* hier, bool maybe) VL_MT_SAFE {
-    VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() {  //
-        vl_stop_maybe(filename, linenum, hier, maybe);
-    }});
-}
-
-void VL_FATAL_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
-    VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() {  //
-        vl_fatal(filename, linenum, hier, msg);
-    }});
-}
-
-void VL_WARN_MT(const char* filename, int linenum, const char* hier, const char* msg) VL_MT_SAFE {
-    VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() {  //
-        vl_warn(filename, linenum, hier, msg);
-    }});
-}
-
-//===========================================================================
-// Debug prints
-
-// sprintf but return as string (this isn't fast, for print messages only)
-std::string _vl_string_vprintf(const char* formatp, va_list ap) VL_MT_SAFE {
-    va_list aq;
-    va_copy(aq, ap);
-    const size_t len = VL_VSNPRINTF(nullptr, 0, formatp, aq);
-    va_end(aq);
-    if (VL_UNLIKELY(len < 1)) return "";
-
-    char* const bufp = new char[len + 1];
-    VL_VSNPRINTF(bufp, len + 1, formatp, ap);
-
-    std::string result{bufp, len};  // Not const to allow move optimization
-    delete[] bufp;
-    return result;
-}
-
-uint64_t _vl_dbg_sequence_number() VL_MT_SAFE {
-    static std::atomic s_sequence;
-    return ++s_sequence;
-}
-
-uint32_t VL_THREAD_ID() VL_MT_SAFE {
-    // Alternative is to use std::this_thread::get_id, but that returns a
-    // hard-to-read number and is very slow
-    static std::atomic s_nextId(0);
-    static thread_local uint32_t t_myId = ++s_nextId;
-    return t_myId;
-}
-
-void VL_DBG_MSGF(const char* formatp, ...) VL_MT_SAFE {
-    // We're still using c printf formats instead of operator<< so we can avoid the heavy
-    // includes that otherwise would be required in every Verilated module
-    va_list ap;
-    va_start(ap, formatp);
-    const std::string result = _vl_string_vprintf(formatp, ap);
-    va_end(ap);
-    // printf("-imm-V{t%d,%" PRId64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(),
-    // result.c_str());
-
-    // Using VL_PRINTF not VL_PRINTF_MT so that we can call VL_DBG_MSGF
-    // from within the guts of the thread execution machinery (and it goes
-    // to the screen and not into the queues we're debugging)
-    VL_PRINTF("-V{t%u,%" PRIu64 "}%s", VL_THREAD_ID(), _vl_dbg_sequence_number(), result.c_str());
-}
-
-void VL_PRINTF_MT(const char* formatp, ...) VL_MT_SAFE {
-    va_list ap;
-    va_start(ap, formatp);
-    const std::string result = _vl_string_vprintf(formatp, ap);
-    va_end(ap);
-    VerilatedThreadMsgQueue::post(VerilatedMsg{[=]() {  //
-        VL_PRINTF("%s", result.c_str());
-    }});
-}
-
-//===========================================================================
-// Process -- parts of std::process implementation
-
-std::string VlProcess::randstate() const VL_MT_UNSAFE {
-    return VlRNG::vl_thread_rng().get_randstate();
-}
-void VlProcess::randstate(const std::string& state) VL_MT_UNSAFE {
-    VlRNG::vl_thread_rng().set_randstate(state);
-}
-
-//===========================================================================
-// Random -- Mostly called at init time, so not inline.
-
-static std::pair vl_splitmix64(uint64_t x) VL_PURE {
-    // SplitMix64 algorithm, copied under public domain from
-    // https://prng.di.unimi.it/splitmix64.c
-    // by Sebastiano Vigna
-    uint64_t z = (x += 0x9e3779b97f4a7c15ULL);
-    z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL;
-    z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL;
-    return {x, z ^ (z >> 31)};
-}
-
-// Xoroshiro128** algorithm, copied under public domain from
-// https://xoshiro.di.unimi.it/xoroshiro128starstar.c
-// by David Blackman and Sebastiano Vigna
-
-static uint64_t vl_rolt(const uint64_t x, int k) VL_PURE { return (x << k) | (x >> (64 - k)); }
-
-static std::array vl_rng_state_from_seed(uint64_t seed) VL_PURE {
-    const auto split1 = vl_splitmix64(seed);
-    const auto split2 = vl_splitmix64(split1.first);
-    return {split1.second, split2.second};
-}
-
-static uint64_t vl_rng_result(const std::array& state) VL_PURE {
-    const uint64_t s0 = state[0];
-    return vl_rolt(s0 * 5, 7) * 9;
-}
-
-static std::array
-vl_rng_compute_new_state(const std::array& current_state) VL_PURE {
-    const uint64_t s0 = current_state[0];
-    uint64_t s1 = current_state[1];
-
-    s1 ^= s0;
-    const uint64_t new_s0 = vl_rolt(s0, 24) ^ s1 ^ (s1 << 16);  // a, b
-    const uint64_t new_s1 = vl_rolt(s1, 37);  // c
-
-    return {new_s0, new_s1};
-}
-
-VlRNG::VlRNG() VL_MT_SAFE {
-    VlRNG& fromr = vl_thread_rng();
-
-    const uint64_t s0 = vl_rng_result(fromr.m_state);
-    fromr.m_state = vl_rng_compute_new_state(fromr.m_state);
-
-    const uint64_t s1 = vl_rng_result(fromr.m_state);
-    fromr.m_state = vl_rng_compute_new_state(fromr.m_state);
-
-    m_state = {s0, s1};
-}
-
-VlRNG::VlRNG(uint64_t seed) VL_PURE { m_state = vl_rng_state_from_seed(seed); }
-void VlRNG::srandom(uint64_t n) VL_MT_UNSAFE { m_state = vl_rng_state_from_seed(n); }
-
-uint64_t VlRNG::rand64() VL_MT_UNSAFE {
-    const uint64_t result = vl_rng_result(m_state);
-    m_state = vl_rng_compute_new_state(m_state);
-    return result;
-}
-uint64_t VlRNG::vl_thread_rng_rand64() VL_MT_SAFE {
-    VlRNG& fromr = vl_thread_rng();
-    const uint64_t result = vl_rng_result(fromr.m_state);
-    fromr.m_state = vl_rng_compute_new_state(fromr.m_state);
-    return result;
-}
-
-std::string VlRNG::get_randstate() const VL_MT_UNSAFE {
-    // Though not stated in IEEE, assumption is the string must be printable
-    const char* const stateCharsp = reinterpret_cast(&m_state);
-    static_assert(sizeof(m_state) == 16, "");
-    std::string result{"R00112233445566770011223344556677"};
-    for (size_t i = 0; i < sizeof(m_state); ++i) {
-        result[1 + i * 2] = 'a' + ((stateCharsp[i] >> 4) & 15);
-        result[1 + i * 2 + 1] = 'a' + (stateCharsp[i] & 15);
-    }
-    return result;
-}
-void VlRNG::set_randstate(const std::string& state) VL_MT_UNSAFE {
-    if (VL_UNLIKELY((state.length() != 1 + 2 * sizeof(m_state)) || (state[0] != 'R'))) {
-        VL_PRINTF_MT("%%Warning: set_randstate ignored as state string not from get_randstate\n");
-        return;
-    }
-    char* const stateCharsp = reinterpret_cast(&m_state);
-    for (size_t i = 0; i < sizeof(m_state); ++i) {
-        stateCharsp[i]
-            = (((state[1 + i * 2] - 'a') & 15) << 4) | ((state[1 + i * 2 + 1] - 'a') & 15);
-    }
-}
-
-static uint32_t vl_sys_rand32() VL_MT_SAFE {
-    // Return random 32-bits using system library.
-    // Used only to construct seed for Verilator's PRNG.
-    static VerilatedMutex s_mutex;
-    const VerilatedLockGuard lock{s_mutex};  // Otherwise rand is unsafe
-#if defined(_WIN32) && !defined(__CYGWIN__)
-    // Windows doesn't have lrand48(), although Cygwin does.
-    return (std::rand() << 16) ^ std::rand();
-#else
-    return (lrand48() << 16) ^ lrand48();
-#endif
-}
-
-VlRNG& VlRNG::vl_thread_rng() VL_MT_SAFE {
-    static thread_local VlRNG t_rng{0};
-    static thread_local uint32_t t_seedEpoch = 0;
-    // For speed, we use a thread-local epoch number to know when to reseed
-    // A thread always belongs to a single context, so this works out ok
-    if (VL_UNLIKELY(t_seedEpoch != VerilatedContextImp::randSeedEpoch())) {
-        // Set epoch before state, to avoid race case with new seeding
-        t_seedEpoch = VerilatedContextImp::randSeedEpoch();
-        t_rng.m_state
-            = vl_rng_state_from_seed(Verilated::threadContextp()->impp()->randSeedDefault64());
-    }
-    return t_rng;
-}
-
-WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE {
-    for (int i = 0; i < VL_WORDS_I(obits); ++i) outwp[i] = vl_rand64();
-    // Last word is unclean
-    return outwp;
-}
-
-double VL_RANDOM_RNG_D(VlRNG& rngr) VL_MT_UNSAFE { return VL_CVT_D_Q(VL_RANDOM_RNG_Q(rngr)); }
-
-WDataOutP VL_RANDOM_RNG_W(VlRNG& rngr, int obits, WDataOutP outwp) VL_MT_UNSAFE {
-    for (int i = 0; i < VL_WORDS_I(obits); ++i) outwp[i] = rngr.rand64();
-    // Last word is unclean
-    return outwp;
-}
-
-IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE {
-    // $random - seed is a new seed to apply, then we return new seed
-    Verilated::threadContextp()->randSeed(static_cast(seedr));
-    seedr = VL_RANDOM_I();
-    return VL_RANDOM_I();
-}
-IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE {
-    // $urandom - seed is a new seed to apply
-    Verilated::threadContextp()->randSeed(static_cast(seed));
-    return VL_RANDOM_I();
-}
-
-IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
-    if (Verilated::threadContextp()->randReset() == 0) return 0;
-    IData data = ~0;
-    if (Verilated::threadContextp()->randReset() != 1) {  // if 2, randomize
-        VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
-        data = rng.rand64();
-    }
-    data &= VL_MASK_I(obits);
-    return data;
-}
-
-QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
-    if (Verilated::threadContextp()->randReset() == 0) return 0;
-    QData data = ~0ULL;
-    if (Verilated::threadContextp()->randReset() != 1) {  // if 2, randomize
-        VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
-        data = rng.rand64();
-    }
-    data &= VL_MASK_Q(obits);
-    return data;
-}
-
-WDataOutP VL_SCOPED_RAND_RESET_W(int obits, WDataOutP outwp, uint64_t scopeHash,
-                                 uint64_t salt) VL_MT_UNSAFE {
-    if (Verilated::threadContextp()->randReset() != 2) { return VL_RAND_RESET_W(obits, outwp); }
-    VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
-    for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = rng.rand64();
-    outwp[VL_WORDS_I(obits) - 1] = rng.rand64() & VL_MASK_E(obits);
-    return outwp;
-}
-
-IData VL_SCOPED_RAND_RESET_ASSIGN_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
-    VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
-    const IData data = rng.rand64() & VL_MASK_I(obits);
-    return data;
-}
-
-QData VL_SCOPED_RAND_RESET_ASSIGN_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE {
-    VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
-    const QData data = rng.rand64() & VL_MASK_Q(obits);
-    return data;
-}
-
-WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint64_t scopeHash,
-                                        uint64_t salt) VL_MT_UNSAFE {
-    VlRNG rng{Verilated::threadContextp()->randSeed() ^ scopeHash ^ salt};
-    for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = rng.rand64();
-    outwp[VL_WORDS_I(obits) - 1] = rng.rand64() & VL_MASK_E(obits);
-    return outwp;
-}
-
-IData VL_RAND_RESET_I(int obits) VL_MT_SAFE {
-    if (Verilated::threadContextp()->randReset() == 0) return 0;
-    IData data = ~0;
-    if (Verilated::threadContextp()->randReset() != 1) {  // if 2, randomize
-        data = VL_RANDOM_I();
-    }
-    data &= VL_MASK_I(obits);
-    return data;
-}
-
-QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE {
-    if (Verilated::threadContextp()->randReset() == 0) return 0;
-    QData data = ~0ULL;
-    if (Verilated::threadContextp()->randReset() != 1) {  // if 2, randomize
-        data = VL_RANDOM_Q();
-    }
-    data &= VL_MASK_Q(obits);
-    return data;
-}
-
-WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
-    for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) outwp[i] = VL_RAND_RESET_I(32);
-    outwp[VL_WORDS_I(obits) - 1] = VL_RAND_RESET_I(32) & VL_MASK_E(obits);
-    return outwp;
-}
-WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
-    // Not inlined to speed up compilation of slowpath code
-    return VL_ZERO_W(obits, outwp);
-}
-
-//===========================================================================
-// Debug
-
-void _vl_debug_print_w(int lbits, const WDataInP iwp) VL_MT_SAFE {
-    VL_PRINTF_MT("  Data: w%d: ", lbits);
-    for (int i = VL_WORDS_I(lbits) - 1; i >= 0; --i) VL_PRINTF_MT("%08x ", iwp[i]);
-    VL_PRINTF_MT("\n");
-}
-
-//===========================================================================
-// Slow expressions
-
-WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, const WDataInP lwp, const WDataInP rwp,
-                       bool is_modulus) VL_MT_SAFE {
-    // See Knuth Algorithm D.  Computes u/v = q.r
-    // This isn't massively tuned, as wide division is rare
-    // for debug see V3Number version
-    // Requires clean input
-    const int words = VL_WORDS_I(lbits);
-    for (int i = 0; i < words; ++i) owp[i] = 0;
-    // Find MSB and check for zero.
-    const int umsbp1 = VL_MOSTSETBITP1_W(words, lwp);  // dividend
-    const int vmsbp1 = VL_MOSTSETBITP1_W(words, rwp);  // divisor
-    if (VL_UNLIKELY(vmsbp1 == 0)  // rwp==0 so division by zero.  Return 0.
-        || VL_UNLIKELY(umsbp1 == 0)) {  // 0/x so short circuit and return 0
-        return owp;
-    }
-
-    const int uw = VL_WORDS_I(umsbp1);  // aka "m" in the algorithm
-    const int vw = VL_WORDS_I(vmsbp1);  // aka "n" in the algorithm
-    VL_DEBUG_IFDEF(assert(uw <= VL_MULS_MAX_WORDS););
-    VL_DEBUG_IFDEF(assert(vw <= VL_MULS_MAX_WORDS););
-
-    if (vw == 1) {  // Single divisor word breaks rest of algorithm
-        uint64_t k = 0;
-        for (int j = uw - 1; j >= 0; --j) {
-            const uint64_t unw64 = ((k << 32ULL) + static_cast(lwp[j]));
-            owp[j] = unw64 / static_cast(rwp[0]);
-            k = unw64 - static_cast(owp[j]) * static_cast(rwp[0]);
-        }
-        if (is_modulus) {
-            owp[0] = k;
-            for (int i = 1; i < words; ++i) owp[i] = 0;
-        }
-        return owp;
-    }
-
-    // +1 word as we may shift during normalization
-    uint32_t un[VL_MULS_MAX_WORDS + 1];  // Fixed size, as MSVC++ doesn't allow [words] here
-    uint32_t vn[VL_MULS_MAX_WORDS + 1];  // v normalized
-
-    // Zero for ease of debugging and to save having to zero for shifts
-    // Note +1 as loop will use extra word
-    for (int i = 0; i < words + 1; ++i) un[i] = vn[i] = 0;
-
-    // Algorithm requires divisor MSB to be set
-    // Copy and shift to normalize divisor so MSB of vn[vw-1] is set
-    const int s = 31 - VL_BITBIT_I(vmsbp1 - 1);  // shift amount (0...31)
-    // Copy and shift dividend by same amount; may set new upper word
-    if (s) {
-        for (int i = vw - 1; i > 0; --i) vn[i] = (rwp[i] << s) | (rwp[i - 1] >> (32 - s));
-        vn[0] = rwp[0] << s;
-        un[uw] = lwp[uw - 1] >> (32 - s);
-        for (int i = uw - 1; i > 0; --i) un[i] = (lwp[i] << s) | (lwp[i - 1] >> (32 - s));
-        un[0] = lwp[0] << s;
-    } else {
-        for (int i = vw - 1; i > 0; --i) vn[i] = rwp[i];
-        vn[0] = rwp[0];
-        un[uw] = 0;
-        for (int i = uw - 1; i > 0; --i) un[i] = lwp[i];
-        un[0] = lwp[0];
-    }
-
-    // Main loop
-    for (int j = uw - vw; j >= 0; --j) {
-        // Estimate
-        const uint64_t unw64
-            = (static_cast(un[j + vw]) << 32ULL | static_cast(un[j + vw - 1]));
-        uint64_t qhat = unw64 / static_cast(vn[vw - 1]);
-        uint64_t rhat = unw64 - qhat * static_cast(vn[vw - 1]);
-
-    again:
-        if (qhat >= 0x100000000ULL || ((qhat * vn[vw - 2]) > ((rhat << 32ULL) + un[j + vw - 2]))) {
-            qhat = qhat - 1;
-            rhat = rhat + vn[vw - 1];
-            if (rhat < 0x100000000ULL) goto again;
-        }
-
-        int64_t t = 0;  // Must be signed
-        uint64_t k = 0;
-        for (int i = 0; i < vw; ++i) {
-            const uint64_t p = qhat * vn[i];  // Multiply by estimate
-            t = un[i + j] - k - (p & 0xFFFFFFFFULL);  // Subtract
-            un[i + j] = t;
-            k = (p >> 32ULL) - (t >> 32ULL);
-        }
-        t = un[j + vw] - k;
-        un[j + vw] = t;
-        owp[j] = qhat;  // Save quotient digit
-
-        if (t < 0) {
-            // Over subtracted; correct by adding back
-            owp[j]--;
-            k = 0;
-            for (int i = 0; i < vw; ++i) {
-                t = static_cast(un[i + j]) + static_cast(vn[i]) + k;
-                un[i + j] = t;
-                k = t >> 32ULL;
-            }
-            un[j + vw] = un[j + vw] + k;
-        }
-    }
-
-    if (is_modulus) {  // modulus
-        // Need to reverse normalization on copy to output
-        if (s) {
-            for (int i = 0; i < vw; ++i) owp[i] = (un[i] >> s) | (un[i + 1] << (32 - s));
-        } else {
-            for (int i = 0; i < vw; ++i) owp[i] = un[i];
-        }
-        for (int i = vw; i < words; ++i) owp[i] = 0;
-        return owp;
-    } else {  // division
-        return owp;
-    }
-}
-
-WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp,
-                     const WDataInP rwp) VL_MT_SAFE {
-    // obits==lbits, rbits can be different
-    const int owords = VL_WORDS_I(obits);
-    VL_DEBUG_IFDEF(assert(owords <= VL_MULS_MAX_WORDS););
-    owp[0] = 1;
-    for (int i = 1; i < VL_WORDS_I(obits); ++i) owp[i] = 0;
-    VlWide powstore;  // Fixed size, as MSVC++ doesn't allow [words] here
-    VlWide lastpowstore;  // Fixed size, as MSVC++ doesn't allow [words] here
-    VlWide lastoutstore;  // Fixed size, as MSVC++ doesn't allow [words] here
-    VL_ASSIGN_W(obits, powstore, lwp);
-    for (int bit = 0; bit < rbits; ++bit) {
-        if (bit > 0) {  // power = power*power
-            VL_ASSIGN_W(obits, lastpowstore, powstore);
-            VL_MUL_W(owords, powstore, lastpowstore, lastpowstore);
-        }
-        if (VL_BITISSET_W(rwp, bit)) {  // out *= power
-            VL_ASSIGN_W(obits, lastoutstore, owp);
-            VL_MUL_W(owords, owp, lastoutstore, powstore);
-        }
-    }
-    return owp;
-}
-WDataOutP VL_POW_WWQ(int obits, int lbits, int rbits, WDataOutP owp, const WDataInP lwp,
-                     QData rhs) VL_MT_SAFE {
-    VlWide rhsw;
-    VL_SET_WQ(rhsw, rhs);
-    return VL_POW_WWW(obits, lbits, rbits, owp, lwp, rhsw);
-}
-QData VL_POW_QQW(int, int, int rbits, QData lhs, const WDataInP rwp) VL_MT_SAFE {
-    const int rwords = VL_WORDS_I(rbits);
-    EData rnz = rwp[0];
-    for (int w = 1; w < rwords; ++w) rnz |= rwp[w];
-    if (!rnz) return 1;  // rwp == 0
-    if (VL_UNLIKELY(lhs == 0)) return 0;
-    QData power = lhs;
-    QData result = 1ULL;
-    for (int bit = 0; bit < rbits; ++bit) {
-        if (bit > 0) power = power * power;
-        if (VL_BITISSET_W(rwp, bit)) result *= power;
-    }
-    return result;
-}
-
-WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, const WDataInP lwp,
-                       const WDataInP rwp, bool lsign, bool rsign) VL_MT_SAFE {
-    // obits==lbits, rbits can be different
-    if (rsign && VL_SIGN_W(rbits, rwp)) {
-        const int words = VL_WORDS_I(obits);
-        VL_ZERO_W(obits, owp);
-        EData lor = 0;  // 0=all zeros, ~0=all ones, else mix
-        for (int i = 1; i < (words - 1); ++i) lor |= lwp[i];
-        lor |= ((lwp[words - 1] == VL_MASK_E(rbits)) ? ~VL_EUL(0) : 0);
-        if (lor == 0 && lwp[0] == 0) {  // "X" so return 0
-            return owp;
-        } else if (lor == 0 && lwp[0] == 1) {  // 1
-            owp[0] = 1;
-            return owp;
-        } else if (lsign && lor == ~VL_EUL(0) && lwp[0] == ~VL_EUL(0)) {  // -1
-            if (rwp[0] & 1) {  // -1^odd=-1
-                return VL_ALLONES_W(obits, owp);
-            } else {  // -1^even=1
-                owp[0] = 1;
-                return owp;
-            }
-        }
-        return owp;
-    }
-    return VL_POW_WWW(obits, rbits, rbits, owp, lwp, rwp);
-}
-WDataOutP VL_POWSS_WWQ(int obits, int lbits, int rbits, WDataOutP owp, const WDataInP lwp,
-                       QData rhs, bool lsign, bool rsign) VL_MT_SAFE {
-    VlWide rhsw;
-    VL_SET_WQ(rhsw, rhs);
-    return VL_POWSS_WWW(obits, lbits, rbits, owp, lwp, rhsw, lsign, rsign);
-}
-QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, const WDataInP rwp, bool lsign,
-                   bool rsign) VL_MT_SAFE {
-    // Skip check for rhs == 0, as short-circuit doesn't save time
-    if (rsign && VL_SIGN_W(rbits, rwp)) {
-        if (lhs == 0) {
-            return 0;  // "X"
-        } else if (lhs == 1) {
-            return 1;
-        } else if (lsign && lhs == VL_MASK_Q(obits)) {  // -1
-            if (rwp[0] & 1) {
-                return VL_MASK_Q(obits);  // -1^odd=-1
-            } else {
-                return 1;  // -1^even=1
-            }
-        }
-        return 0;
-    }
-    return VL_POW_QQW(obits, rbits, rbits, lhs, rwp);
-}
-
-double VL_ITOR_D_W(int lbits, const WDataInP lwp) VL_PURE {
-    int ms_word = VL_WORDS_I(lbits) - 1;
-    for (; !lwp[ms_word] && ms_word > 0;) --ms_word;
-    if (ms_word == 0) return static_cast(lwp[0]);
-    if (ms_word == 1) return static_cast(VL_SET_QW(lwp));
-    // We need 53 bits of mantissa, which might mean looking at 3 words
-    // namely ms_word, ms_word-1 and ms_word-2
-    const EData ihi = lwp[ms_word];
-    const EData imid = lwp[ms_word - 1];
-    const EData ilo = lwp[ms_word - 2];
-    const double hi = static_cast(ihi) * std::exp2(2 * VL_EDATASIZE);
-    const double mid = static_cast(imid) * std::exp2(VL_EDATASIZE);
-    const double lo = static_cast(ilo);
-    const double d = (hi + mid + lo) * std::exp2(VL_EDATASIZE * (ms_word - 2));
-    return d;
-}
-double VL_ISTOR_D_W(int lbits, const WDataInP lwp) VL_MT_SAFE {
-    if (!VL_SIGN_W(lbits, lwp)) return VL_ITOR_D_W(lbits, lwp);
-    const int words = VL_WORDS_I(lbits);
-    VL_DEBUG_IFDEF(assert(words <= VL_MULS_MAX_WORDS););
-    uint32_t pos[VL_MULS_MAX_WORDS + 1];  // Fixed size, as MSVC++ doesn't allow [words] here
-    VL_NEGATE_W(words, pos, lwp);
-    _vl_clean_inplace_w(lbits, pos);
-    return -VL_ITOR_D_W(lbits, pos);
-}
-
-//===========================================================================
-// Formatting
-
-// Output a string representation of a wide number
-std::string VL_DECIMAL_NW(int width, const WDataInP lwp) VL_MT_SAFE {
-    const int maxdecwidth = (width + 3) * 4 / 3;
-    // Or (maxdecwidth+7)/8], but can't have more than 4 BCD bits per word
-    VlWide bcd;
-    VL_ZERO_W(maxdecwidth, bcd);
-    VlWide tmp;
-    VlWide tmp2;
-    int from_bit = width - 1;
-    // Skip all leading zeros
-    for (; from_bit >= 0 && !(VL_BITRSHIFT_W(lwp, from_bit) & 1); --from_bit) {}
-    // Double-dabble algorithm
-    for (; from_bit >= 0; --from_bit) {
-        // Any digits >= 5 need an add 3 (via tmp)
-        for (int nibble_bit = 0; nibble_bit < maxdecwidth; nibble_bit += 4) {
-            if ((VL_BITRSHIFT_W(bcd, nibble_bit) & 0xf) >= 5) {
-                VL_ZERO_W(maxdecwidth, tmp2);
-                tmp2[VL_BITWORD_E(nibble_bit)] |= VL_EUL(0x3) << VL_BITBIT_E(nibble_bit);
-                VL_ASSIGN_W(maxdecwidth, tmp, bcd);
-                VL_ADD_W(VL_WORDS_I(maxdecwidth), bcd, tmp, tmp2);
-            }
-        }
-        // Shift; bcd = bcd << 1
-        VL_ASSIGN_W(maxdecwidth, tmp, bcd);
-        VL_SHIFTL_WWI(maxdecwidth, maxdecwidth, 32, bcd, tmp, 1);
-        // bcd[0] = lwp[from_bit]
-        if (VL_BITISSET_W(lwp, from_bit)) bcd[0] |= 1;
-    }
-    std::string output;
-    int lsb = (maxdecwidth - 1) & ~3;
-    for (; lsb > 0; lsb -= 4) {  // Skip leading zeros
-        if (VL_BITRSHIFT_W(bcd, lsb) & 0xf) break;
-    }
-    for (; lsb >= 0; lsb -= 4) {
-        output += ('0' + (VL_BITRSHIFT_W(bcd, lsb) & 0xf));  // 0..9
-    }
-    return output;
-}
-
-template 
-std::string _vl_vsformat_time(char* tmp, T ld, int timeunit, bool left, size_t width) VL_MT_SAFE {
-    const VerilatedContextImp* const ctxImpp = Verilated::threadContextp()->impp();
-    const std::string suffix = ctxImpp->timeFormatSuffix();
-    const int userUnits = ctxImpp->timeFormatUnits();  // 0..-15
-    const int fracDigits = ctxImpp->timeFormatPrecision();  // 0..N
-    const int shift = -userUnits + fracDigits + timeunit;  // 0..-15
-    int digits = 0;
-    if (std::numeric_limits::is_integer) {
-        constexpr int b = 128;
-        constexpr int w = VL_WORDS_I(b);
-        VlWide tmp0;
-        VlWide tmp1;
-        VlWide tmp2;
-        VlWide tmp3;
-
-        WDataInP shifted = VL_EXTEND_WQ(b, 0, tmp0, static_cast(ld));
-        if (shift < 0) {
-            const WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(-shift));
-            shifted = VL_DIV_WWW(b, tmp2, shifted, pow10);
-        } else {
-            const WDataInP pow10 = VL_EXTEND_WQ(b, 0, tmp1, vl_time_pow10(shift));
-            shifted = VL_MUL_W(w, tmp2, shifted, pow10);
-        }
-
-        const WDataInP fracDigitsPow10 = VL_EXTEND_WQ(b, 0, tmp3, vl_time_pow10(fracDigits));
-        const WDataInP integer = VL_DIV_WWW(b, tmp0, shifted, fracDigitsPow10);
-        const WDataInP frac = VL_MODDIV_WWW(b, tmp1, shifted, fracDigitsPow10);
-        const WDataInP max64Bit
-            = VL_EXTEND_WQ(b, 0, tmp2, std::numeric_limits::max());  // breaks shifted
-        if (VL_GT_W(w, integer, max64Bit)) {
-            WDataOutP v = VL_ASSIGN_W(b, tmp3, integer);  // breaks fracDigitsPow10
-            VlWide zero;
-            VlWide ten;
-            VL_ZERO_W(b, zero);
-            VL_EXTEND_WI(b, 0, ten, 10);
-            char buf[128];  // 128B is obviously long enough to represent 128bit integer in decimal
-            char* ptr = buf + sizeof(buf) - 1;
-            *ptr = '\0';
-            while (VL_GT_W(w, v, zero)) {
-                --ptr;
-                const WDataInP mod = VL_MODDIV_WWW(b, tmp2, v, ten);  // breaks max64Bit
-                *ptr = "0123456789"[VL_SET_QW(mod)];
-                VlWide divided;
-                VL_DIV_WWW(b, divided, v, ten);
-                VL_ASSIGN_W(b, v, divided);
-            }
-            if (!fracDigits) {
-                digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%s%s", ptr, suffix.c_str());
-            } else {
-                digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%s.%0*" PRIu64 "%s", ptr,
-                                     fracDigits, VL_SET_QW(frac), suffix.c_str());
-            }
-        } else {
-            const uint64_t integer64 = VL_SET_QW(integer);
-            if (!fracDigits) {
-                digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRIu64 "%s", integer64,
-                                     suffix.c_str());
-            } else {
-                digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRIu64 ".%0*" PRIu64 "%s",
-                                     integer64, fracDigits, VL_SET_QW(frac), suffix.c_str());
-            }
-        }
-    } else {
-        const double shiftd = vl_time_multiplier(shift);
-        const double scaled = ld * shiftd;
-        const double fracDiv = vl_time_multiplier(fracDigits);
-        const double whole = scaled / fracDiv;
-        if (!fracDigits) {
-            digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%.0f%s", whole, suffix.c_str());
-        } else {
-            digits = VL_SNPRINTF(tmp, VL_VALUE_STRING_MAX_WIDTH, "%.*f%s", fracDigits, whole,
-                                 suffix.c_str());
-        }
-    }
-
-    const int needmore = static_cast(width) - digits;
-    std::string padding;
-    if (needmore > 0) padding.append(needmore, ' ');  // Pad with spaces
-    return left ? (tmp + padding) : (padding + tmp);
-}
-
-// Do a va_arg returning a quad, assuming input argument is anything less than wide
-#define VL_VA_ARG_Q_(ap, bits) (((bits) <= VL_IDATASIZE) ? va_arg(ap, IData) : va_arg(ap, QData))
-
-void _vl_vsformat(std::string& output, const std::string& format, va_list ap) VL_MT_SAFE {
-    // Format a Verilog $write style format into the output list
-    // The format must be pre-processed (and lower cased) by Verilator
-    // Arguments are in "width, arg-value (or WDataIn* if wide)" form
-    //
-    // Note uses a single buffer internally; presumes only one usage per printf
-    // Note also assumes variables < 64 are not wide, this assumption is
-    // sometimes not true in low-level routines written here in verilated.cpp
-    static thread_local char t_tmp[VL_VALUE_STRING_MAX_WIDTH];
-    std::string::const_iterator pctit = format.end();  // Most recent %##.##g format
-    bool inPct = false;
-    bool widthSet = false;
-    bool left = false;
-    size_t width = 0;
-    for (std::string::const_iterator pos = format.cbegin(); pos != format.cend(); ++pos) {
-        if (!inPct && pos[0] == '%') {
-            pctit = pos;
-            inPct = true;
-            widthSet = false;
-            width = 0;
-        } else if (!inPct) {  // Normal text
-            // Fast-forward to next escape and add to output
-            std::string::const_iterator ep = pos;
-            while (ep != format.end() && ep[0] != '%') ++ep;
-            if (ep != pos) {
-                output.append(pos, ep);
-                pos = ep - 1;
-            }
-        } else {  // Format character
-            inPct = false;
-            const char fmt = pos[0];
-            switch (fmt) {
-            case '0':  // FALLTHRU
-            case '1':  // FALLTHRU
-            case '2':  // FALLTHRU
-            case '3':  // FALLTHRU
-            case '4':  // FALLTHRU
-            case '5':  // FALLTHRU
-            case '6':  // FALLTHRU
-            case '7':  // FALLTHRU
-            case '8':  // FALLTHRU
-            case '9':
-                inPct = true;  // Get more digits
-                widthSet = true;
-                width = width * 10 + (fmt - '0');
-                break;
-            case '-':
-                left = true;
-                inPct = true;  // Get more digits
-                break;
-            case '.':
-                inPct = true;  // Get more digits
-                break;
-            case '%':  //
-                output += '%';
-                break;
-            case 'N': {  // "C" string with name of module, add . if needed
-                const char* const cstrp = va_arg(ap, const char*);
-                if (VL_LIKELY(*cstrp)) {
-                    output += cstrp;
-                    output += '.';
-                }
-                break;
-            }
-            case 'S': {  // "C" string
-                const char* const cstrp = va_arg(ap, const char*);
-                output += cstrp;
-                break;
-            }
-            case '@': {  // Verilog/C++ string
-                va_arg(ap, int);  // # bits is ignored
-                const std::string* const cstrp = va_arg(ap, const std::string*);
-                std::string padding;
-                if (width > cstrp->size()) padding.append(width - cstrp->size(), ' ');
-                output += left ? (*cstrp + padding) : (padding + *cstrp);
-                break;
-            }
-            case 'e':
-            case 'f':
-            case 'g':
-            case '^': {  // Realtime
-                const int lbits = va_arg(ap, int);
-                const double d = va_arg(ap, double);
-                (void)lbits;  // UNUSED - always 64
-                if (fmt == '^') {  // Realtime
-                    if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
-                    const int timeunit = va_arg(ap, int);
-                    output += _vl_vsformat_time(t_tmp, d, timeunit, left, width);
-                } else {
-                    const std::string fmts{pctit, pos + 1};
-                    VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, fmts.c_str(), d);
-                    output += t_tmp;
-                }
-                break;
-            }
-            case 'p': {  // 'x' but parameter is string
-                const int lbits = va_arg(ap, int);
-                (void)lbits;
-                const std::string* const cstr = va_arg(ap, const std::string*);
-                std::ostringstream oss;
-                for (unsigned char c : *cstr) oss << std::hex << static_cast(c);
-                std::string hex_str = oss.str();
-                if (width > 0 && widthSet) {
-                    hex_str = hex_str.size() > width
-                                  ? hex_str.substr(0, width)
-                                  : std::string(width - hex_str.size(), '0') + hex_str;
-                    output += hex_str;
-                }
-                break;
-            }
-            default: {
-                // Deal with all read-and-print somethings
-                const int lbits = va_arg(ap, int);
-                QData ld = 0;
-                VlWide qlwp;
-                WDataInP lwp = nullptr;
-                if (lbits <= VL_QUADSIZE) {
-                    ld = VL_VA_ARG_Q_(ap, lbits);
-                    VL_SET_WQ(qlwp, ld);
-                    lwp = qlwp;
-                } else {
-                    lwp = va_arg(ap, WDataInP);
-                    ld = lwp[0];
-                }
-                int lsb = lbits - 1;
-                if (widthSet && width == 0) {
-                    while (lsb && !VL_BITISSET_W(lwp, lsb)) --lsb;
-                }
-                switch (fmt) {
-                case 'c': {
-                    const IData charval = ld & 0xff;
-                    output += static_cast(charval);
-                    break;
-                }
-                case 's': {
-                    std::string field;
-                    for (; lsb >= 0; --lsb) {
-                        lsb = (lsb / 8) * 8;  // Next digit
-                        const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
-                        field += (charval == 0) ? ' ' : charval;
-                    }
-                    std::string padding;
-                    if (width > field.size()) padding.append(width - field.size(), ' ');
-                    output += left ? (field + padding) : (padding + field);
-                    break;
-                }
-                case 'd': {  // Signed decimal
-                    int digits = 0;
-                    std::string append;
-                    if (lbits <= VL_QUADSIZE) {
-                        digits
-                            = VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRId64,
-                                          static_cast(VL_EXTENDS_QQ(lbits, lbits, ld)));
-                        append = t_tmp;
-                    } else {
-                        if (VL_SIGN_E(lbits, lwp[VL_WORDS_I(lbits) - 1])) {
-                            VlWide neg;
-                            VL_NEGATE_W(VL_WORDS_I(lbits), neg, lwp);
-                            append = "-"s + VL_DECIMAL_NW(lbits, neg);
-                        } else {
-                            append = VL_DECIMAL_NW(lbits, lwp);
-                        }
-                        digits = static_cast(append.length());
-                    }
-                    const int needmore = static_cast(width) - digits;
-                    if (needmore > 0) {
-                        std::string padding;
-                        if (left) {
-                            padding.append(needmore, ' ');  // Pre-pad spaces
-                            output += append + padding;
-                        } else {
-                            if (pctit != format.end() && pctit[0] && pctit[1] == '0') {  // %0
-                                padding.append(needmore, '0');  // Pre-pad zero
-                            } else {
-                                padding.append(needmore, ' ');  // Pre-pad spaces
-                            }
-                            output += padding + append;
-                        }
-                    } else {
-                        output += append;
-                    }
-                    break;
-                }
-                case '#': {  // Unsigned decimal
-                    int digits = 0;
-                    std::string append;
-                    if (lbits <= VL_QUADSIZE) {
-                        digits = VL_SNPRINTF(t_tmp, VL_VALUE_STRING_MAX_WIDTH, "%" PRIu64, ld);
-                        append = t_tmp;
-                    } else {
-                        append = VL_DECIMAL_NW(lbits, lwp);
-                        digits = static_cast(append.length());
-                    }
-                    const int needmore = static_cast(width) - digits;
-                    if (needmore > 0) {
-                        std::string padding;
-                        if (left) {
-                            padding.append(needmore, ' ');  // Pre-pad spaces
-                            output += append + padding;
-                        } else {
-                            if (pctit != format.end() && pctit[0] && pctit[1] == '0') {  // %0
-                                padding.append(needmore, '0');  // Pre-pad zero
-                            } else {
-                                padding.append(needmore, ' ');  // Pre-pad spaces
-                            }
-                            output += padding + append;
-                        }
-                    } else {
-                        output += append;
-                    }
-                    break;
-                }
-                case 't': {  // Time
-                    if (!widthSet) width = Verilated::threadContextp()->impp()->timeFormatWidth();
-                    const int timeunit = va_arg(ap, int);
-                    output += _vl_vsformat_time(t_tmp, ld, timeunit, left, width);
-                    break;
-                }
-                case 'b':  // FALLTHRU
-                case 'o':  // FALLTHRU
-                case 'x': {
-                    if (widthSet || left) {
-                        lsb = VL_MOSTSETBITP1_W(VL_WORDS_I(lbits), lwp);
-                        lsb = (lsb < 1) ? 0 : (lsb - 1);
-                    }
-
-                    std::string append;
-                    int digits;
-                    switch (fmt) {
-                    case 'b': {
-                        digits = lsb + 1;
-                        for (; lsb >= 0; --lsb) append += (VL_BITRSHIFT_W(lwp, lsb) & 1) + '0';
-                        break;
-                    }
-                    case 'o': {
-                        digits = (lsb + 1 + 2) / 3;
-                        for (; lsb >= 0; --lsb) {
-                            lsb = (lsb / 3) * 3;  // Next digit
-                            // Octal numbers may span more than one wide word,
-                            // so we need to grab each bit separately and check for overrun
-                            // Octal is rare, so we'll do it a slow simple way
-                            append += static_cast(
-                                '0' + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 0)) ? 1 : 0)
-                                + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 1)) ? 2 : 0)
-                                + ((VL_BITISSETLIMIT_W(lwp, lbits, lsb + 2)) ? 4 : 0));
-                        }
-                        break;
-                    }
-                    default: {  // 'x'
-                        digits = (lsb + 1 + 3) / 4;
-                        for (; lsb >= 0; --lsb) {
-                            lsb = (lsb / 4) * 4;  // Next digit
-                            const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xf;
-                            append += "0123456789abcdef"[charval];
-                        }
-                        break;
-                    }
-                    }  // switch
-
-                    const int needmore = static_cast(width) - digits;
-                    if (needmore > 0) {
-                        std::string padding;
-                        if (left) {
-                            padding.append(needmore, ' ');  // Pre-pad spaces
-                            output += append + padding;
-                        } else {
-                            padding.append(needmore, '0');  // Pre-pad zero
-                            output += padding + append;
-                        }
-                    } else {
-                        output += append;
-                    }
-                    break;
-                }  // b / o / x
-                case 'u':
-                case 'z': {  // Packed 4-state
-                    const bool is_4_state = (fmt == 'z');
-                    output.reserve(output.size() + ((is_4_state ? 2 : 1) * VL_WORDS_I(lbits)));
-                    int bytes_to_go = VL_BYTES_I(lbits);
-                    int bit = 0;
-                    while (bytes_to_go > 0) {
-                        const int wr_bytes = std::min(4, bytes_to_go);
-                        for (int byte = 0; byte < wr_bytes; byte++, bit += 8)
-                            output += static_cast(VL_BITRSHIFT_W(lwp, bit) & 0xff);
-                        output.append(4 - wr_bytes, static_cast(0));
-                        if (is_4_state) output.append(4, static_cast(0));
-                        bytes_to_go -= wr_bytes;
-                    }
-                    break;
-                }
-                case 'v':  // Strength; assume always strong
-                    for (lsb = lbits - 1; lsb >= 0; --lsb) {
-                        if (VL_BITRSHIFT_W(lwp, lsb) & 1) {
-                            output += "St1 ";
-                        } else {
-                            output += "St0 ";
-                        }
-                    }
-                    break;
-                default: {  // LCOV_EXCL_START
-                    const std::string msg = "Unknown _vl_vsformat code: "s + pos[0];
-                    VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
-                    break;
-                }  // LCOV_EXCL_STOP
-                }  // switch
-            }
-            }  // switch
-        }
-    }
-}
-
-static bool _vl_vsss_eof(FILE* fp, int floc) VL_MT_SAFE {
-    if (VL_LIKELY(fp)) {
-        return std::feof(fp) ? true : false;  // true : false to prevent MSVC++ warning
-    } else {
-        return floc < 0;
-    }
-}
-static void _vl_vsss_advance(FILE* fp, int& floc) VL_MT_SAFE {
-    if (VL_LIKELY(fp)) {
-        std::fgetc(fp);
-    } else {
-        floc -= 8;
-    }
-}
-static int _vl_vsss_peek(FILE* fp, int& floc, const WDataInP fromp,
-                         const std::string& fstr) VL_MT_SAFE {
-    // Get a character without advancing
-    if (VL_LIKELY(fp)) {
-        const int data = std::fgetc(fp);
-        if (data == EOF) return EOF;
-        ungetc(data, fp);
-        return data;
-    } else {
-        if (floc < 0) return EOF;
-        floc = floc & ~7;  // Align to closest character
-        if (fromp == nullptr) {
-            return fstr[fstr.length() - 1 - (floc >> 3)];
-        } else {
-            return VL_BITRSHIFT_W(fromp, floc) & 0xff;
-        }
-    }
-}
-static void _vl_vsss_skipspace(FILE* fp, int& floc, const WDataInP fromp,
-                               const std::string& fstr) VL_MT_SAFE {
-    while (true) {
-        const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
-        if (c == EOF || !std::isspace(c)) return;
-        _vl_vsss_advance(fp, floc);
-    }
-}
-static void _vl_vsss_read_str(FILE* fp, int& floc, const WDataInP fromp, const std::string& fstr,
-                              char* tmpp, const char* acceptp) VL_MT_SAFE {
-    // Read into tmp, consisting of characters from acceptp list
-    char* cp = tmpp;
-    while (true) {
-        int c = _vl_vsss_peek(fp, floc, fromp, fstr);
-        if (c == EOF || std::isspace(c)) break;
-        if (acceptp && nullptr == std::strchr(acceptp, c)) break;  // String - allow anything
-        if (acceptp) c = std::tolower(c);  // Non-strings we'll simplify
-        *cp++ = c;
-        _vl_vsss_advance(fp, floc);
-    }
-    *cp++ = '\0';
-    // VL_DBG_MSGF(" _read got='"< 0) {
-        const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
-        if (c == EOF) return nullptr;
-        if (!inhibit) *beginp++ = c;
-        _vl_vsss_advance(fp, floc);
-    }
-    return beginp;
-}
-static void _vl_vsss_setbit(WDataOutP iowp, int obits, int lsb, int nbits, IData ld) VL_MT_SAFE {
-    for (; nbits && lsb < obits; nbits--, lsb++, ld >>= 1) VL_ASSIGNBIT_WI(lsb, iowp, ld & 1);
-}
-void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp, size_t posstart,
-                    size_t posend) VL_MT_SAFE {
-    // Read in base "2^^baseLog2" digits from strp[posstart..posend-1] into owp of size obits.
-    VL_ZERO_W(obits, owp);
-    int lsb = 0;
-    for (int i = 0, pos = static_cast(posend) - 1;
-         i < obits && pos >= static_cast(posstart); --pos) {
-        // clang-format off
-        switch (tolower (strp[pos])) {
-        case 'x': case 'z': case '?':  // FALLTHRU
-        case '0': lsb += baseLog2; break;
-        case '1': _vl_vsss_setbit(owp, obits, lsb, baseLog2,  1); lsb += baseLog2; break;
-        case '2': _vl_vsss_setbit(owp, obits, lsb, baseLog2,  2); lsb += baseLog2; break;
-        case '3': _vl_vsss_setbit(owp, obits, lsb, baseLog2,  3); lsb += baseLog2; break;
-        case '4': _vl_vsss_setbit(owp, obits, lsb, baseLog2,  4); lsb += baseLog2; break;
-        case '5': _vl_vsss_setbit(owp, obits, lsb, baseLog2,  5); lsb += baseLog2; break;
-        case '6': _vl_vsss_setbit(owp, obits, lsb, baseLog2,  6); lsb += baseLog2; break;
-        case '7': _vl_vsss_setbit(owp, obits, lsb, baseLog2,  7); lsb += baseLog2; break;
-        case '8': _vl_vsss_setbit(owp, obits, lsb, baseLog2,  8); lsb += baseLog2; break;
-        case '9': _vl_vsss_setbit(owp, obits, lsb, baseLog2,  9); lsb += baseLog2; break;
-        case 'a': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 10); lsb += baseLog2; break;
-        case 'b': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 11); lsb += baseLog2; break;
-        case 'c': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 12); lsb += baseLog2; break;
-        case 'd': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 13); lsb += baseLog2; break;
-        case 'e': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 14); lsb += baseLog2; break;
-        case 'f': _vl_vsss_setbit(owp, obits, lsb, baseLog2, 15); lsb += baseLog2; break;
-        case '_': break;
-        }
-        // clang-format on
-    }
-}
-
-IData _vl_vsscanf(FILE* fp,  // If a fscanf
-                  int fbits, const WDataInP fromp,  // Else if a sscanf
-                  const std::string& fstr,  // if a sscanf to string
-                  const std::string& format, va_list ap) VL_MT_SAFE {
-    // Read a Verilog $sscanf/$fscanf style format into the output list
-    // The format must be pre-processed (and lower cased) by Verilator
-    // Arguments are in "width, arg-value (or WDataIn* if wide)" form
-    static thread_local char t_tmp[VL_VALUE_STRING_MAX_WIDTH];
-    int floc = fbits - 1;
-    IData got = 0;
-    bool inPct = false;
-    bool inIgnore = false;
-    std::string::const_iterator pos = format.cbegin();
-    for (; pos != format.cend(); ++pos) {
-        // VL_DBG_MSGF("_vlscan fmt='%c' floc=%d file='%c'\n", pos[0], floc,
-        // _vl_vsss_peek(fp, floc, fromp, fstr));
-        if (!inPct && pos[0] == '%') {
-            inPct = true;
-            inIgnore = false;
-        } else if (!inPct && std::isspace(pos[0])) {  // Format spaces
-            while (std::isspace(pos[1])) ++pos;
-            _vl_vsss_skipspace(fp, floc, fromp, fstr);
-        } else if (!inPct) {  // Expected Format
-            _vl_vsss_skipspace(fp, floc, fromp, fstr);
-            const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
-            if (c != pos[0]) goto done;
-            _vl_vsss_advance(fp, floc);
-        } else {  // Format character
-            // Skip loading spaces
-            inPct = false;
-            const char fmt = pos[0];
-            switch (fmt) {
-            case '%': {
-                const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
-                if (c != '%') goto done;
-                _vl_vsss_advance(fp, floc);
-                break;
-            }
-            case '0':  // FALLTHRU
-            case '1':  // FALLTHRU
-            case '2':  // FALLTHRU
-            case '3':  // FALLTHRU
-            case '4':  // FALLTHRU
-            case '5':  // FALLTHRU
-            case '6':  // FALLTHRU
-            case '7':  // FALLTHRU
-            case '8':  // FALLTHRU
-            case '9': {
-                inPct = true;
-                break;
-            }
-            case '*':
-                inPct = true;
-                inIgnore = true;
-                break;
-            default: {
-                // Deal with all read-and-scan somethings
-                // Note LSBs are preserved if there's an overflow
-                int obits = inIgnore ? 0 : va_arg(ap, int);
-                VlWide qowp;
-                VL_SET_WQ(qowp, 0ULL);
-                WDataOutP owp = qowp;
-                if (obits == -1) {  // string
-                    owp = nullptr;
-                    if (VL_UNCOVERABLE(fmt != 's')) {
-                        VL_FATAL_MT(
-                            __FILE__, __LINE__, "",
-                            "Internal: format other than %s is passed to string");  // LCOV_EXCL_LINE
-                    }
-                } else if (obits > VL_QUADSIZE) {
-                    owp = va_arg(ap, WDataOutP);
-                }
-
-                for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0;
-                switch (fmt) {
-                case 'c': {
-                    const int c = _vl_vsss_peek(fp, floc, fromp, fstr);
-                    if (c == EOF) goto done;
-                    _vl_vsss_advance(fp, floc);
-                    owp[0] = c;
-                    break;
-                }
-                case 's': {
-                    _vl_vsss_skipspace(fp, floc, fromp, fstr);
-                    _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, nullptr);
-                    if (!t_tmp[0]) goto done;
-                    if (owp) {
-                        int lpos = (static_cast(std::strlen(t_tmp))) - 1;
-                        int lsb = 0;
-                        for (int i = 0; i < obits && lpos >= 0; --lpos) {
-                            _vl_vsss_setbit(owp, obits, lsb, 8, t_tmp[lpos]);
-                            lsb += 8;
-                        }
-                    }
-                    break;
-                }
-                case 'd': {  // Signed decimal
-                    _vl_vsss_skipspace(fp, floc, fromp, fstr);
-                    _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "0123456789+-xXzZ?_");
-                    if (!t_tmp[0]) goto done;
-                    int64_t ld = 0;
-                    std::sscanf(t_tmp, "%30" PRId64, &ld);
-                    VL_SET_WQ(owp, ld);
-                    break;
-                }
-                case 'f':
-                case 'e':
-                case 'g': {  // Real number
-                    _vl_vsss_skipspace(fp, floc, fromp, fstr);
-                    _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "+-.0123456789eE");
-                    if (!t_tmp[0]) goto done;
-                    union {
-                        double r;
-                        int64_t ld;
-                    } u;
-                    u.r = std::strtod(t_tmp, nullptr);
-                    VL_SET_WQ(owp, u.ld);
-                    break;
-                }
-                case 't': {  // Time
-                    _vl_vsss_skipspace(fp, floc, fromp, fstr);
-                    _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "+-.0123456789eE");
-                    if (!t_tmp[0]) goto done;
-                    union {
-                        double r;
-                        int64_t ld;
-                    } u;
-                    // Get pointer argument first, as proceeds the timeunit value
-                    if (obits != 64) goto done;
-                    QData* const realp = va_arg(ap, QData*);
-                    const int timeunit = va_arg(ap, int);
-                    const int userUnits
-                        = Verilated::threadContextp()->impp()->timeFormatUnits();  // 0..-15
-                    const int shift = -userUnits + timeunit;  // 0..-15
-                    u.r = std::strtod(t_tmp, nullptr) * vl_time_multiplier(-shift);
-                    *realp = VL_CLEAN_QQ(obits, obits, u.ld);
-                    obits = 0;  // Already loaded the value, don't read arg
-                    break;
-                }
-                case '#': {  // Unsigned decimal
-                    _vl_vsss_skipspace(fp, floc, fromp, fstr);
-                    _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "0123456789+-xXzZ?_");
-                    if (!t_tmp[0]) goto done;
-                    QData ld = 0;
-                    std::sscanf(t_tmp, "%30" PRIu64, &ld);
-                    VL_SET_WQ(owp, ld);
-                    break;
-                }
-                case 'b': {
-                    _vl_vsss_skipspace(fp, floc, fromp, fstr);
-                    _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "01xXzZ?_");
-                    if (!t_tmp[0]) goto done;
-                    _vl_vsss_based(owp, obits, 1, t_tmp, 0, std::strlen(t_tmp));
-                    break;
-                }
-                case 'o': {
-                    _vl_vsss_skipspace(fp, floc, fromp, fstr);
-                    _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp, "01234567xXzZ?_");
-                    if (!t_tmp[0]) goto done;
-                    _vl_vsss_based(owp, obits, 3, t_tmp, 0, std::strlen(t_tmp));
-                    break;
-                }
-                case 'x': {
-                    _vl_vsss_skipspace(fp, floc, fromp, fstr);
-                    _vl_vsss_read_str(fp, floc, fromp, fstr, t_tmp,
-                                      "0123456789abcdefABCDEFxXzZ?_");
-                    if (!t_tmp[0]) goto done;
-                    _vl_vsss_based(owp, obits, 4, t_tmp, 0, std::strlen(t_tmp));
-                    break;
-                }
-                case 'u': {
-                    // Read packed 2-value binary data
-                    const int bytes = VL_BYTES_I(obits);
-                    char* const out = reinterpret_cast(owp);
-                    if (!_vl_vsss_read_bin(fp, floc, fromp, fstr, out, bytes)) goto done;
-                    const int last = bytes % 4;
-                    if (last != 0
-                        && !_vl_vsss_read_bin(fp, floc, fromp, fstr, out, 4 - last, true))
-                        goto done;
-                    break;
-                }
-                case 'z': {
-                    // Read packed 4-value binary data
-                    char* out = reinterpret_cast(owp);
-                    int bytes = VL_BYTES_I(obits);
-                    while (bytes > 0) {
-                        const int abytes = std::min(4, bytes);
-                        // aval (4B) read {0, 1} state
-                        out = _vl_vsss_read_bin(fp, floc, fromp, fstr, out, abytes);
-                        if (!out) goto done;
-                        // bval (4B) disregard {X, Z} state and align to new 8B boundary.
-                        out = _vl_vsss_read_bin(fp, floc, fromp, fstr, out, 8 - abytes, true);
-                        if (!out) goto done;
-                        bytes -= abytes;
-                    }
-                    break;
-                }
-                default: {  // LCOV_EXCL_START
-                    const std::string msg = "Unknown _vl_vsscanf code: "s + pos[0];
-                    VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str());
-                    break;
-                }  // LCOV_EXCL_STOP
-
-                }  // switch
-
-                if (!inIgnore) ++got;
-                // Reload data if non-wide (if wide, we put it in the right place directly)
-                if (obits == 0) {  // Due to inIgnore
-                } else if (obits == -1) {  // string
-                    std::string* const p = va_arg(ap, std::string*);
-                    *p = t_tmp;
-                } else if (obits <= VL_BYTESIZE) {
-                    CData* const p = va_arg(ap, CData*);
-                    *p = VL_CLEAN_II(obits, obits, owp[0]);
-                } else if (obits <= VL_SHORTSIZE) {
-                    SData* const p = va_arg(ap, SData*);
-                    *p = VL_CLEAN_II(obits, obits, owp[0]);
-                } else if (obits <= VL_IDATASIZE) {
-                    IData* const p = va_arg(ap, IData*);
-                    *p = VL_CLEAN_II(obits, obits, owp[0]);
-                } else if (obits <= VL_QUADSIZE) {
-                    QData* const p = va_arg(ap, QData*);
-                    *p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp));
-                } else {
-                    _vl_clean_inplace_w(obits, owp);
-                }
-            }
-            }  // switch
-        }
-    }
-    // Processed all arguments
-    return got;
-
-done:
-    // Scan stopped early, return parsed or EOF
-    if (_vl_vsss_eof(fp, floc)) return -1;
-    return got;
-}
-
-//===========================================================================
-// File I/O
-
-FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE {
-    // Expected non-MCD case; returns null on MCD descriptors.
-    return Verilated::threadContextp()->impp()->fdToFp(lhs);
-}
-
-void _vl_vint_to_string(int obits, char* destoutp, const WDataInP sourcep) VL_MT_SAFE {
-    // See also VL_DATA_TO_STRING_NW
-    int lsb = obits - 1;
-    bool start = true;
-    char* destp = destoutp;
-    for (; lsb >= 0; --lsb) {
-        lsb = (lsb / 8) * 8;  // Next digit
-        const IData charval = VL_BITRSHIFT_W(sourcep, lsb) & 0xff;
-        if (!start || charval) {
-            *destp++ = (charval == 0) ? ' ' : charval;
-            start = false;  // Drop leading 0s
-        }
-    }
-    *destp = '\0';  // Terminate
-    if (!start) {  // Drop trailing spaces
-        while (std::isspace(*(destp - 1)) && destp > destoutp) *--destp = '\0';
-    }
-}
-
-void _vl_string_to_vint(int obits, void* destp, size_t srclen, const char* srcp) VL_MT_SAFE {
-    // Convert C string to Verilog format
-    const size_t bytes = VL_BYTES_I(obits);
-    char* op = reinterpret_cast(destp);
-    if (srclen > bytes) srclen = bytes;  // Don't overflow destination
-    size_t i = 0;
-    for (i = 0; i < srclen; ++i) *op++ = srcp[srclen - 1 - i];
-    for (; i < bytes; ++i) *op++ = 0;
-}
-
-static IData getLine(std::string& str, IData fpi, size_t maxLen) VL_MT_SAFE {
-    str.clear();
-
-    // While threadsafe, each thread can only access different file handles
-    FILE* const fp = VL_CVT_I_FP(fpi);
-    if (VL_UNLIKELY(!fp)) return 0;
-
-    // We don't use fgets, as we must read \0s.
-    while (str.size() < maxLen) {
-        const int c = getc(fp);  // getc() is threadsafe
-        if (c == EOF) break;
-        str.push_back(c);
-        if (c == '\n') break;
-    }
-    return static_cast(str.size());
-}
-
-IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE {
-    std::string str;
-    const IData bytes = VL_BYTES_I(obits);
-    const IData got = getLine(str, fpi, bytes);
-
-    if (VL_UNLIKELY(str.empty())) return 0;
-
-    // V3Emit has static check that bytes < VL_VALUE_STRING_MAX_WORDS, but be safe
-    if (VL_UNCOVERABLE(bytes < str.size())) {
-        VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: fgets buffer overrun");  // LCOV_EXCL_LINE
-    }
-
-    _vl_string_to_vint(obits, destp, got, str.data());
-    return got;
-}
-
-IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE {
-    return getLine(dest, fpi, std::numeric_limits::max());
-}
-
-IData VL_FERROR_IN(IData, std::string& outputr) VL_MT_SAFE {
-    // We ignore lhs/fpi - IEEE says "most recent error" so probably good enough
-    const IData ret = errno;
-    outputr = std::string{::std::strerror(ret)};
-    return ret;
-}
-IData VL_FERROR_IW(IData fpi, int obits, WDataOutP outwp) VL_MT_SAFE {
-    std::string output;
-    const IData ret = VL_FERROR_IN(fpi, output /*ref*/);
-    _vl_string_to_vint(obits, outwp, output.length(), output.c_str());
-    return ret;
-}
-
-IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) {
-    return Verilated::threadContextp()->impp()->fdNew(filename.c_str(), mode.c_str());
-}
-IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE {
-    return Verilated::threadContextp()->impp()->fdNewMcd(filename.c_str());
-}
-
-void VL_FFLUSH_I(IData fdi) VL_MT_SAFE { Verilated::threadContextp()->impp()->fdFlush(fdi); }
-IData VL_FSEEK_I(IData fdi, IData offset, IData origin) VL_MT_SAFE {
-    return Verilated::threadContextp()->impp()->fdSeek(fdi, offset, origin);
-}
-IData VL_FTELL_I(IData fdi) VL_MT_SAFE { return Verilated::threadContextp()->impp()->fdTell(fdi); }
-void VL_FCLOSE_I(IData fdi) VL_MT_SAFE {
-    // While threadsafe, each thread can only access different file handles
-    Verilated::threadContextp()->impp()->fdClose(fdi);
-}
-
-void VL_SFORMAT_NX(int obits, CData& destr, const std::string& format, int argc, ...) VL_MT_SAFE {
-    static thread_local std::string t_output;  // static only for speed
-    t_output = "";
-    va_list ap;
-    va_start(ap, argc);
-    _vl_vsformat(t_output, format, ap);
-    va_end(ap);
-
-    _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
-}
-
-void VL_SFORMAT_NX(int obits, SData& destr, const std::string& format, int argc, ...) VL_MT_SAFE {
-    static thread_local std::string t_output;  // static only for speed
-    t_output = "";
-    va_list ap;
-    va_start(ap, argc);
-    _vl_vsformat(t_output, format, ap);
-    va_end(ap);
-
-    _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
-}
-
-void VL_SFORMAT_NX(int obits, IData& destr, const std::string& format, int argc, ...) VL_MT_SAFE {
-    static thread_local std::string t_output;  // static only for speed
-    t_output = "";
-    va_list ap;
-    va_start(ap, argc);
-    _vl_vsformat(t_output, format, ap);
-    va_end(ap);
-
-    _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
-}
-
-void VL_SFORMAT_NX(int obits, QData& destr, const std::string& format, int argc, ...) VL_MT_SAFE {
-    static thread_local std::string t_output;  // static only for speed
-    t_output = "";
-    va_list ap;
-    va_start(ap, argc);
-    _vl_vsformat(t_output, format, ap);
-    va_end(ap);
-
-    _vl_string_to_vint(obits, &destr, t_output.length(), t_output.c_str());
-}
-
-void VL_SFORMAT_NX(int obits, void* destp, const std::string& format, int argc, ...) VL_MT_SAFE {
-    static thread_local std::string t_output;  // static only for speed
-    t_output = "";
-    va_list ap;
-    va_start(ap, argc);
-    _vl_vsformat(t_output, format, ap);
-    va_end(ap);
-
-    _vl_string_to_vint(obits, destp, t_output.length(), t_output.c_str());
-}
-
-void VL_SFORMAT_NX(int obits_ignored, std::string& output, const std::string& format, int argc,
-                   ...) VL_MT_SAFE {
-    (void)obits_ignored;  // So VL_SFORMAT_NNX function signatures all match
-    std::string temp_output;
-    va_list ap;
-    va_start(ap, argc);
-    _vl_vsformat(temp_output, format, ap);
-    va_end(ap);
-    output = temp_output;
-}
-
-std::string VL_SFORMATF_N_NX(const std::string& format, int argc, ...) VL_MT_SAFE {
-    static thread_local std::string t_output;  // static only for speed
-    t_output = "";
-    va_list ap;
-    va_start(ap, argc);
-    _vl_vsformat(t_output, format, ap);
-    va_end(ap);
-
-    return t_output;
-}
-
-void VL_WRITEF_NX(const std::string& format, int argc, ...) VL_MT_SAFE {
-    static thread_local std::string t_output;  // static only for speed
-    t_output = "";
-    va_list ap;
-    va_start(ap, argc);
-    _vl_vsformat(t_output, format, ap);
-    va_end(ap);
-
-    VL_PRINTF_MT("%s", t_output.c_str());
-}
-
-void VL_FWRITEF_NX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE {
-    // While threadsafe, each thread can only access different file handles
-    static thread_local std::string t_output;  // static only for speed
-    t_output = "";
-
-    va_list ap;
-    va_start(ap, argc);
-    _vl_vsformat(t_output, format, ap);
-    va_end(ap);
-
-    Verilated::threadContextp()->impp()->fdWrite(fpi, t_output);
-}
-
-IData VL_FSCANF_INX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE {
-    // While threadsafe, each thread can only access different file handles
-    FILE* const fp = VL_CVT_I_FP(fpi);
-    if (VL_UNLIKELY(!fp)) return ~0U;  // -1
-
-    va_list ap;
-    va_start(ap, argc);
-    const IData got = _vl_vsscanf(fp, 0, nullptr, "", format, ap);
-    va_end(ap);
-    return got;
-}
-
-IData VL_SSCANF_IINX(int lbits, IData ld, const std::string& format, int argc, ...) VL_MT_SAFE {
-    VlWide fnw;
-    VL_SET_WI(fnw, ld);
-
-    va_list ap;
-    va_start(ap, argc);
-    const IData got = _vl_vsscanf(nullptr, lbits, fnw, "", format, ap);
-    va_end(ap);
-    return got;
-}
-IData VL_SSCANF_IQNX(int lbits, QData ld, const std::string& format, int argc, ...) VL_MT_SAFE {
-    VlWide fnw;
-    VL_SET_WQ(fnw, ld);
-
-    va_list ap;
-    va_start(ap, argc);
-    const IData got = _vl_vsscanf(nullptr, lbits, fnw, "", format, ap);
-    va_end(ap);
-    return got;
-}
-IData VL_SSCANF_IWNX(int lbits, const WDataInP lwp, const std::string& format, int argc,
-                     ...) VL_MT_SAFE {
-    va_list ap;
-    va_start(ap, argc);
-    const IData got = _vl_vsscanf(nullptr, lbits, lwp, "", format, ap);
-    va_end(ap);
-    return got;
-}
-IData VL_SSCANF_INNX(int, const std::string& ld, const std::string& format, int argc,
-                     ...) VL_MT_SAFE {
-    va_list ap;
-    va_start(ap, argc);
-    const IData got
-        = _vl_vsscanf(nullptr, static_cast(ld.length() * 8), nullptr, ld, format, ap);
-    va_end(ap);
-    return got;
-}
-
-// MurmurHash64A
-uint64_t VL_MURMUR64_HASH(const char* key) VL_PURE {
-    const size_t len = strlen(key);
-    const uint64_t seed = 0;
-    const uint64_t m = 0xc6a4a7935bd1e995ULL;
-    const int r = 47;
-
-    uint64_t h = seed ^ (len * m);
-
-    const uint64_t* data = reinterpret_cast(key);
-    const uint64_t* end = data + (len / 8);
-
-    while (data != end) {
-        uint64_t k = *data++;
-
-        k *= m;
-        k ^= k >> r;
-        k *= m;
-
-        h ^= k;
-        h *= m;
-    }
-
-    const unsigned char* data2 = reinterpret_cast(data);
-
-    switch (len & 7) {
-    case 7: h ^= uint64_t(data2[6]) << 48; /* fallthrough */
-    case 6: h ^= uint64_t(data2[5]) << 40; /* fallthrough */
-    case 5: h ^= uint64_t(data2[4]) << 32; /* fallthrough */
-    case 4: h ^= uint64_t(data2[3]) << 24; /* fallthrough */
-    case 3: h ^= uint64_t(data2[2]) << 16; /* fallthrough */
-    case 2: h ^= uint64_t(data2[1]) << 8; /* fallthrough */
-    case 1: h ^= uint64_t(data2[0]); h *= m; /* fallthrough */
-    };
-
-    h ^= h >> r;
-    h *= m;
-    h ^= h >> r;
-
-    return h;
-}
-
-IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi, IData start,
-                 IData count) VL_MT_SAFE {
-    // While threadsafe, each thread can only access different file handles
-    FILE* const fp = VL_CVT_I_FP(fpi);
-    if (VL_UNLIKELY(!fp)) return 0;
-    if (count > (array_size - (start - array_lsb))) count = array_size - (start - array_lsb);
-    // Prep for reading
-    IData read_count = 0;
-    IData read_elements = 0;
-    const int start_shift = (width - 1) & ~7;  // bit+7:bit gets first character
-    int shift = start_shift;
-    // Read the data
-    // We process a character at a time, as then we don't need to deal
-    // with changing buffer sizes dynamically, etc.
-    while (true) {
-        const int c = std::fgetc(fp);
-        if (VL_UNLIKELY(c == EOF)) break;
-        // Shift value in
-        const IData entry = read_elements + start - array_lsb;
-        if (width <= 8) {
-            CData* const datap = &(reinterpret_cast(memp))[entry];
-            if (shift == start_shift) *datap = 0;
-            *datap |= (c << shift) & VL_MASK_I(width);
-        } else if (width <= 16) {
-            SData* const datap = &(reinterpret_cast(memp))[entry];
-            if (shift == start_shift) *datap = 0;
-            *datap |= (c << shift) & VL_MASK_I(width);
-        } else if (width <= VL_IDATASIZE) {
-            IData* const datap = &(reinterpret_cast(memp))[entry];
-            if (shift == start_shift) *datap = 0;
-            *datap |= (c << shift) & VL_MASK_I(width);
-        } else if (width <= VL_QUADSIZE) {
-            QData* const datap = &(reinterpret_cast(memp))[entry];
-            if (shift == start_shift) *datap = 0;
-            *datap |= ((static_cast(c) << static_cast(shift)) & VL_MASK_Q(width));
-        } else {
-            WDataOutP datap = &(reinterpret_cast(memp))[entry * VL_WORDS_I(width)];
-            if (shift == start_shift) VL_ZERO_W(width, datap);
-            datap[VL_BITWORD_E(shift)] |= (static_cast(c) << VL_BITBIT_E(shift));
-        }
-        // Prep for next
-        ++read_count;
-        shift -= 8;
-        if (shift < 0) {
-            shift = start_shift;
-            ++read_elements;
-            if (VL_UNLIKELY(read_elements >= count)) break;
-        }
-    }
-    return read_count;
-}
-
-#ifdef _VL_HAVE_STACKTRACE
-static std::string _vl_stacktrace_demangle(const std::string& input) VL_MT_SAFE {
-    static VerilatedMutex s_demangleMutex;
-    const VerilatedLockGuard lock{s_demangleMutex};
-
-    std::string result;
-    result.reserve(input.size());
-
-    std::string word;
-    for (const char c : input) {
-        if (std::isalpha(c) || c == '_') {
-            word += c;
-        } else if (!word.empty() && std::isdigit(c)) {
-            word += c;
-        } else {
-            if (!word.empty()) {
-                // abi::__cxa_demangle mallocs demangled_name
-                int status = 0;
-                char* const demangled_name
-                    = abi::__cxa_demangle(word.c_str(), NULL, NULL, &status);
-                if (status == 0) {
-                    result += std::string{demangled_name};
-                    std::free(demangled_name);  // Free the allocated memory
-                } else {
-                    result += word;
-                }
-                word.clear();
-            }
-            result += c;
-        }
-    }
-    // input requires final newline, so last word can't be symbol
-    result += word;
-    return result;
-}
-#endif
-
-std::string VL_STACKTRACE_N() VL_MT_SAFE {
-    static VerilatedMutex s_stackTraceMutex;
-    const VerilatedLockGuard lock{s_stackTraceMutex};
-
-#ifdef _VL_HAVE_STACKTRACE
-    int nptrs = 0;
-    char** strings = nullptr;
-
-    constexpr int BT_BUF_SIZE = 100;
-    void* buffer[BT_BUF_SIZE];
-    nptrs = backtrace(buffer, BT_BUF_SIZE);
-    strings = backtrace_symbols(buffer, nptrs);
-
-    // cppcheck-suppress knownConditionTrueFalse
-    if (!strings) return "Unable to backtrace, call failed\n";
-
-    std::string result = "Backtrace:\n";
-    for (int j = 0; j < nptrs; ++j)
-        result += _vl_stacktrace_demangle(std::string{strings[j]} + "\n"s);
-
-    free(strings);
-    return result;
-#else
-    return "Unable to backtrace; not supported\n";
-#endif
-}
-
-void VL_STACKTRACE() VL_MT_SAFE {
-    const std::string result = VL_STACKTRACE_N();
-    VL_PRINTF("%s", result.c_str());
-}
-
-IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE {
-    VlWide lhsw;
-    VL_SET_WQ(lhsw, lhs);
-    return VL_SYSTEM_IW(VL_WQ_WORDS_E, lhsw);
-}
-IData VL_SYSTEM_IW(int lhswords, const WDataInP lhsp) VL_MT_SAFE {
-    const std::string lhs = VL_CVT_PACK_STR_NW(lhswords, lhsp);
-    return VL_SYSTEM_IN(lhs);
-}
-IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE {
-    const int code = std::system(lhs.c_str());  // Yes, std::system() is threadsafe
-    return code >> 8;  // Want exit status
-}
-
-IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE {
-    const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(format.c_str());
-    return match.empty() ? 0 : 1;
-}
-
-IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE {
-    std::string prefix;
-    bool inPct = false;
-    bool done = false;
-    char fmt = ' ';
-    for (const char* posp = ld.c_str(); !done && *posp; ++posp) {
-        if (!inPct && posp[0] == '%') {
-            inPct = true;
-        } else if (!inPct) {  // Normal text
-            prefix += *posp;
-        } else if (*posp == '0') {  // %0
-        } else {  // Format character
-            switch (std::tolower(*posp)) {
-            case '%':
-                prefix += *posp;
-                inPct = false;
-                break;
-            default:
-                fmt = *posp;
-                done = true;
-                break;
-            }
-        }
-    }
-
-    const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
-    const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length();
-    if (match.empty()) return 0;
-
-    VL_ZERO_W(rbits, rwp);
-    switch (std::tolower(fmt)) {
-    case 'd': {
-        int64_t lld = 0;
-        std::sscanf(dp, "%30" PRId64, &lld);
-        VL_SET_WQ(rwp, lld);
-        break;
-    }
-    case 'b': _vl_vsss_based(rwp, rbits, 1, dp, 0, std::strlen(dp)); break;
-    case 'o': _vl_vsss_based(rwp, rbits, 3, dp, 0, std::strlen(dp)); break;
-    case 'h':  // FALLTHRU
-    case 'x': _vl_vsss_based(rwp, rbits, 4, dp, 0, std::strlen(dp)); break;
-    case 's': {  // string/no conversion
-        for (int i = 0, lsb = 0, posp = static_cast(std::strlen(dp)) - 1;
-             i < rbits && posp >= 0; --posp) {
-            _vl_vsss_setbit(rwp, rbits, lsb, 8, dp[posp]);
-            lsb += 8;
-        }
-        break;
-    }
-    case 'e': {
-        double temp = 0.F;
-        std::sscanf(dp, "%le", &temp);
-        VL_SET_WQ(rwp, VL_CVT_Q_D(temp));
-        break;
-    }
-    case 'f': {
-        double temp = 0.F;
-        std::sscanf(dp, "%lf", &temp);
-        VL_SET_WQ(rwp, VL_CVT_Q_D(temp));
-        break;
-    }
-    case 'g': {
-        double temp = 0.F;
-        std::sscanf(dp, "%lg", &temp);
-        VL_SET_WQ(rwp, VL_CVT_Q_D(temp));
-        break;
-    }
-    default:  // Other simulators return 0 in these cases and don't error out
-        return 0;
-    }
-    _vl_clean_inplace_w(rbits, rwp);
-    return 1;
-}
-IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE {
-    std::string prefix;
-    bool inPct = false;
-    bool done = false;
-    for (const char* posp = ld.c_str(); !done && *posp; ++posp) {
-        if (!inPct && posp[0] == '%') {
-            inPct = true;
-        } else if (!inPct) {  // Normal text
-            prefix += *posp;
-        } else {  // Format character
-            switch (std::tolower(*posp)) {
-            case '%':
-                prefix += *posp;
-                inPct = false;
-                break;
-            default:  //
-                done = true;
-                break;
-            }
-        }
-    }
-    const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefix.c_str());
-    const char* const dp = match.c_str() + 1 /*leading + */ + prefix.length();
-    if (match.empty()) return 0;
-    rdr = std::string{dp};
-    return 1;
-}
-
-const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE {
-    const std::string& match = Verilated::threadContextp()->impp()->argPlusMatch(prefixp);
-    static thread_local char t_outstr[VL_VALUE_STRING_MAX_WIDTH];
-    if (match.empty()) return nullptr;
-    char* dp = t_outstr;
-    for (const char* sp = match.c_str() + std::strlen(prefixp) + 1;  // +1 to skip the "+"
-         *sp && (dp - t_outstr) < (VL_VALUE_STRING_MAX_WIDTH - 2);)
-        *dp++ = *sp++;
-    *dp++ = '\0';
-    return t_outstr;
-}
-
-//===========================================================================
-// Heavy string functions
-
-std::string VL_TO_STRING(CData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 8, lhs); }
-std::string VL_TO_STRING(SData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 16, lhs); }
-std::string VL_TO_STRING(IData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 32, lhs); }
-std::string VL_TO_STRING(QData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 64, lhs); }
-std::string VL_TO_STRING(double lhs) { return VL_SFORMATF_N_NX("%g", 0, 64, lhs); }
-std::string VL_TO_STRING_W(int words, const WDataInP obj) {
-    return VL_SFORMATF_N_NX("'h%0x", 0, words * VL_EDATASIZE, obj);
-}
-
-std::string VL_TOLOWER_NN(const std::string& ld) VL_PURE {
-    std::string result = ld;
-    for (auto& cr : result) cr = std::tolower(cr);
-    return result;
-}
-std::string VL_TOUPPER_NN(const std::string& ld) VL_PURE {
-    std::string result = ld;
-    for (auto& cr : result) cr = std::toupper(cr);
-    return result;
-}
-
-std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE {
-    // See also _vl_vint_to_string
-    std::string result;
-    result.reserve((lwords * VL_EDATASIZE) / 8 + 1);
-    const int obits = lwords * VL_EDATASIZE;
-    int lsb = obits - 1;
-    for (; lsb >= 0; --lsb) {
-        lsb = (lsb / 8) * 8;  // Next digit
-        const IData charval = VL_BITRSHIFT_W(lwp, lsb) & 0xff;
-        if (charval) result += static_cast(charval);
-    }
-    return result;
-}
-
-std::string VL_CVT_PACK_STR_ND(const VlQueue& q) VL_PURE {
-    std::string output;
-    for (const std::string& s : q) output += s;
-    return output;
-}
-
-std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE {
-    std::string lstring = lhs;
-    const int32_t rhs_s = rhs;  // To signed value
-    // 6.16.2:str.putc(i, c) does not change the value when i < 0 || i >= str.len() || c == 0
-    if (0 <= rhs_s && rhs < lhs.length() && ths != 0) lstring[rhs] = ths;
-    return lstring;
-}
-
-CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE {
-    CData v = 0;
-    const int32_t rhs_s = rhs;  // To signed value
-    // 6.16.3:str.getc(i) returns 0 if i < 0 || i >= str.len()
-    if (0 <= rhs_s && rhs < lhs.length()) v = lhs[rhs];
-    return v;
-}
-
-std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE {
-    const int32_t rhs_s = rhs;  // To signed value
-    const int32_t ths_s = ths;  // To signed value
-    // 6.16.8:str.substr(i, j) returns an empty string when i < 0 || j < i || j >= str.len()
-    if (rhs_s < 0 || ths_s < rhs_s || ths >= lhs.length()) return "";
-    // Second parameter of std::string::substr(i, n) is length, not position as in SystemVerilog
-    return lhs.substr(rhs, ths - rhs + 1);
-}
-
-IData VL_ATOI_N(const std::string& str, int base) VL_PURE {
-    std::string str_mod = str;
-    // IEEE 1800-2023 6.16.9 says '_' may exist.
-    str_mod.erase(std::remove(str_mod.begin(), str_mod.end(), '_'), str_mod.end());
-
-    errno = 0;
-    auto v = std::strtol(str_mod.c_str(), nullptr, base);
-    if (errno != 0) v = 0;
-    return static_cast(v);
-}
-IData VL_NTOI_I(int obits, const std::string& str) VL_PURE { return VL_NTOI_Q(obits, str); }
-QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE {
-    QData out = 0;
-    const char* const datap = str.data();
-    int pos = static_cast(str.length()) - 1;
-    int bit = 0;
-    while (bit < obits && pos >= 0) {
-        out |= static_cast(datap[pos]) << VL_BITBIT_Q(bit);
-        bit += 8;
-        --pos;
-    }
-    return out & VL_MASK_Q(obits);
-}
-void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str) VL_PURE {
-    const int words = VL_WORDS_I(obits);
-    for (int i = 0; i < words; ++i) owp[i] = 0;
-    const char* const datap = str.data();
-    int pos = static_cast(str.length()) - 1;
-    int bit = 0;
-    while (bit < obits && pos >= 0) {
-        owp[VL_BITWORD_I(bit)] |= static_cast(datap[pos]) << VL_BITBIT_I(bit);
-        bit += 8;
-        --pos;
-    }
-    owp[words - 1] &= VL_MASK_E(obits);
-}
-
-//===========================================================================
-// Readmem/writemem
-
-static const char* memhFormat(int nBits) {
-    assert((nBits >= 1) && (nBits <= 32));
-
-    static thread_local char t_buf[32];
-    switch ((nBits - 1) / 4) {
-    case 0: VL_SNPRINTF(t_buf, 32, "%%01x"); break;
-    case 1: VL_SNPRINTF(t_buf, 32, "%%02x"); break;
-    case 2: VL_SNPRINTF(t_buf, 32, "%%03x"); break;
-    case 3: VL_SNPRINTF(t_buf, 32, "%%04x"); break;
-    case 4: VL_SNPRINTF(t_buf, 32, "%%05x"); break;
-    case 5: VL_SNPRINTF(t_buf, 32, "%%06x"); break;
-    case 6: VL_SNPRINTF(t_buf, 32, "%%07x"); break;
-    case 7: VL_SNPRINTF(t_buf, 32, "%%08x"); break;
-    default: assert(false); break;  // LCOV_EXCL_LINE
-    }
-    return t_buf;
-}
-
-static const char* formatBinary(int nBits, uint32_t bits) {
-    assert((nBits >= 1) && (nBits <= 32));
-
-    static thread_local char t_buf[64];
-    for (int i = 0; i < nBits; ++i) {
-        const bool isOne = bits & (1 << (nBits - 1 - i));
-        t_buf[i] = (isOne ? '1' : '0');
-    }
-    t_buf[nBits] = '\0';
-    return t_buf;
-}
-
-VlReadMem::VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end)
-    : m_hex{hex}
-    , m_bits{bits}
-    , m_filename(filename)  // Need () or GCC 4.8 false warning
-    , m_end{end}
-    , m_addr{start} {
-    m_fp = std::fopen(filename.c_str(), "r");
-    if (VL_UNLIKELY(!m_fp)) {
-        // We don't report the Verilog source filename as it slow to have to pass it down
-        VL_WARN_MT(filename.c_str(), 0, "", "$readmem file not found");
-        return;
-    }
-}
-VlReadMem::~VlReadMem() {
-    if (m_fp) {
-        std::fclose(m_fp);
-        m_fp = nullptr;
-    }
-}
-bool VlReadMem::get(QData& addrr, std::string& valuer) {
-    if (VL_UNLIKELY(!m_fp)) return false;
-    valuer = "";
-    // Prep for reading
-    bool inData = false;
-    bool ignoreToEol = false;
-    bool ignoreToComment = false;
-    bool readingAddress = false;
-    int lastCh = ' ';
-    // Read the data
-    // We process a character at a time, as then we don't need to deal
-    // with changing buffer sizes dynamically, etc.
-    while (true) {
-        int c = std::fgetc(m_fp);
-        if (VL_UNLIKELY(c == EOF)) break;
-        const bool chIs4StateBin
-            = c == '0' || c == '1' || c == 'x' || c == 'X' || c == 'z' || c == 'Z';
-        const bool chIs2StateHex = std::isxdigit(c);
-        const bool chIs4StateHex = std::isxdigit(c) || chIs4StateBin;
-        // printf("%d: Got '%c' Addr%lx IN%d IgE%d IgC%d\n",
-        //        m_linenum, c, m_addr, inData, ignoreToEol, ignoreToComment);
-        // See if previous data value has completed, and if so return
-        if (c == '_') continue;  // Ignore _ e.g. inside a number
-        if (inData && !chIs4StateHex) {
-            // printf("Got data @%lx = %s\n", m_addr, valuer.c_str());
-            ungetc(c, m_fp);
-            addrr = m_addr;
-            ++m_addr;
-            return true;
-        }
-        // Parse line
-        if (c == '\n') {
-            ++m_linenum;
-            ignoreToEol = false;
-            readingAddress = false;
-        } else if (c == '\t' || c == ' ' || c == '\r' || c == '\f') {
-            readingAddress = false;
-        }
-        // Skip // comments and detect /* comments
-        else if (ignoreToComment && lastCh == '*' && c == '/') {
-            ignoreToComment = false;
-            readingAddress = false;
-        } else if (!ignoreToEol && !ignoreToComment) {
-            if (lastCh == '/' && c == '*') {
-                ignoreToComment = true;
-            } else if (lastCh == '/' && c == '/') {
-                ignoreToEol = true;
-            } else if (c == '/') {  // Part of /* or //
-            } else if (c == '#') {
-                ignoreToEol = true;
-            } else if (c == '@') {
-                readingAddress = true;
-                m_anyAddr = true;
-                m_addr = 0;
-            } else if (readingAddress && chIs2StateHex) {
-                c = std::tolower(c);
-                const int addressValue = (c >= 'a') ? (c - 'a' + 10) : (c - '0');
-                m_addr = (m_addr << 4) + addressValue;
-            } else if (readingAddress && chIs4StateHex) {
-                VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
-                            "$readmem address contains 4-state characters");
-            } else if (chIs4StateHex) {
-                inData = true;
-                valuer += static_cast(c);
-                if (VL_UNLIKELY(!m_hex && !chIs4StateBin)) {
-                    VL_FATAL_MT(m_filename.c_str(), m_linenum, "",
-                                "$readmemb (binary) file contains hex characters");
-                }
-            } else {
-                VL_FATAL_MT(m_filename.c_str(), m_linenum, "", "$readmem file syntax error");
-            }
-        }
-        lastCh = c;
-    }
-
-    if (VL_UNLIKELY(m_end != ~0ULL && m_addr <= m_end && !m_anyAddr)) {
-        VL_WARN_MT(m_filename.c_str(), m_linenum, "",
-                   "$readmem file ended before specified final address (IEEE 1800-2023 21.4)");
-    }
-
-    addrr = m_addr;
-    return inData;  // EOF
-}
-void VlReadMem::setData(void* valuep, const std::string& rhs) {
-    const QData shift = m_hex ? 4ULL : 1ULL;
-    bool innum = false;
-    // Shift value in
-    for (const auto& i : rhs) {
-        const char c = std::tolower(i);
-        const int value = (c == 'x' || c == 'z') ? VL_RAND_RESET_I(m_hex ? 4 : 1)
-                          : (c >= 'a')           ? (c - 'a' + 10)
-                                                 : (c - '0');
-        if (m_bits <= 8) {
-            CData* const datap = reinterpret_cast(valuep);
-            if (!innum) *datap = 0;
-            *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
-        } else if (m_bits <= 16) {
-            SData* const datap = reinterpret_cast(valuep);
-            if (!innum) *datap = 0;
-            *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
-        } else if (m_bits <= VL_IDATASIZE) {
-            IData* const datap = reinterpret_cast(valuep);
-            if (!innum) *datap = 0;
-            *datap = ((*datap << shift) + value) & VL_MASK_I(m_bits);
-        } else if (m_bits <= VL_QUADSIZE) {
-            QData* const datap = reinterpret_cast(valuep);
-            if (!innum) *datap = 0;
-            *datap = ((*datap << static_cast(shift)) + static_cast(value))
-                     & VL_MASK_Q(m_bits);
-        } else {
-            WDataOutP datap = reinterpret_cast(valuep);
-            if (!innum) VL_ZERO_W(m_bits, datap);
-            _vl_shiftl_inplace_w(m_bits, datap, static_cast(shift));
-            datap[0] |= value;
-        }
-        innum = true;
-    }
-}
-
-VlWriteMem::VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end)
-    : m_hex{hex}
-    , m_bits{bits} {
-    if (VL_UNLIKELY(start > end)) {
-        VL_FATAL_MT(filename.c_str(), 0, "", "$writemem invalid address range");
-        return;
-    }
-
-    m_fp = std::fopen(filename.c_str(), "w");
-    if (VL_UNLIKELY(!m_fp)) {
-        VL_FATAL_MT(filename.c_str(), 0, "", "$writemem file not found");
-        return;
-    }
-}
-VlWriteMem::~VlWriteMem() {
-    if (m_fp) {
-        std::fclose(m_fp);
-        m_fp = nullptr;
-    }
-}
-void VlWriteMem::print(QData addr, bool addrstamp, const void* valuep) {
-    if (VL_UNLIKELY(!m_fp)) return;
-    if (addr != m_addr && addrstamp) {  // Only assoc has time stamps
-        fprintf(m_fp, "@%" PRIx64 "\n", addr);
-    }
-    m_addr = addr + 1;
-    if (m_bits <= 8) {
-        const CData* const datap = reinterpret_cast(valuep);
-        if (m_hex) {
-            fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
-            fprintf(m_fp, "\n");
-        } else {
-            fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
-        }
-    } else if (m_bits <= 16) {
-        const SData* const datap = reinterpret_cast(valuep);
-        if (m_hex) {
-            fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
-            fprintf(m_fp, "\n");
-        } else {
-            fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
-        }
-    } else if (m_bits <= 32) {
-        const IData* const datap = reinterpret_cast(valuep);
-        if (m_hex) {
-            fprintf(m_fp, memhFormat(m_bits), VL_MASK_I(m_bits) & *datap);
-            fprintf(m_fp, "\n");
-        } else {
-            fprintf(m_fp, "%s\n", formatBinary(m_bits, *datap));
-        }
-    } else if (m_bits <= 64) {
-        const QData* const datap = reinterpret_cast(valuep);
-        const uint64_t value = VL_MASK_Q(m_bits) & *datap;
-        const uint32_t lo = value & 0xffffffff;
-        const uint32_t hi = value >> 32;
-        if (m_hex) {
-            fprintf(m_fp, memhFormat(m_bits - 32), hi);
-            fprintf(m_fp, "%08x\n", lo);
-        } else {
-            fprintf(m_fp, "%s", formatBinary(m_bits - 32, hi));
-            fprintf(m_fp, "%s\n", formatBinary(32, lo));
-        }
-    } else {
-        const WDataInP datap = reinterpret_cast(valuep);
-        // output as a sequence of VL_EDATASIZE'd words
-        // from MSB to LSB. Mask off the MSB word which could
-        // contain junk above the top of valid data.
-        int word_idx = ((m_bits - 1) / VL_EDATASIZE);
-        bool first = true;
-        while (word_idx >= 0) {
-            EData data = datap[word_idx];
-            if (first) {
-                data &= VL_MASK_E(m_bits);
-                const int top_word_nbits = VL_BITBIT_E(m_bits - 1) + 1;
-                if (m_hex) {
-                    fprintf(m_fp, memhFormat(top_word_nbits), data);
-                } else {
-                    fprintf(m_fp, "%s", formatBinary(top_word_nbits, data));
-                }
-            } else {
-                if (m_hex) {
-                    fprintf(m_fp, "%08x", data);
-                } else {
-                    fprintf(m_fp, "%s", formatBinary(32, data));
-                }
-            }
-            --word_idx;
-            first = false;
-        }
-        fprintf(m_fp, "\n");
-    }
-}
-
-void VL_READMEM_N(bool hex,  // Hex format, else binary
-                  int bits,  // M_Bits of each array row
-                  QData depth,  // Number of rows
-                  int array_lsb,  // Index of first row. Valid row addresses
-                  //              //  range from array_lsb up to (array_lsb + depth - 1)
-                  const std::string& filename,  // Input file name
-                  void* memp,  // Array state
-                  QData start,  // First array row address to read
-                  QData end  // Last row address to read
-                  ) VL_MT_SAFE {
-    if (start < static_cast(array_lsb)) start = array_lsb;
-
-    VlReadMem rmem{hex, bits, filename, start, end};
-    if (VL_UNLIKELY(!rmem.isOpen())) return;
-    while (true) {
-        QData addr = 0;
-        std::string value;
-        if (rmem.get(addr /*ref*/, value /*ref*/)) {
-            // printf("readmem.get [%" PRIu64 "]=%s\n", addr, value.c_str());
-            if (VL_UNLIKELY(addr < static_cast(array_lsb)
-                            || addr >= static_cast(array_lsb + depth))) {
-                VL_FATAL_MT(filename.c_str(), rmem.linenum(), "",
-                            "$readmem file address beyond bounds of array");
-            } else {
-                const QData entry = addr - array_lsb;
-                if (bits <= 8) {
-                    CData* const datap = &(reinterpret_cast(memp))[entry];
-                    rmem.setData(datap, value);
-                } else if (bits <= 16) {
-                    SData* const datap = &(reinterpret_cast(memp))[entry];
-                    rmem.setData(datap, value);
-                } else if (bits <= VL_IDATASIZE) {
-                    IData* const datap = &(reinterpret_cast(memp))[entry];
-                    rmem.setData(datap, value);
-                } else if (bits <= VL_QUADSIZE) {
-                    QData* const datap = &(reinterpret_cast(memp))[entry];
-                    rmem.setData(datap, value);
-                } else {
-                    WDataOutP datap
-                        = &(reinterpret_cast(memp))[entry * VL_WORDS_I(bits)];
-                    rmem.setData(datap, value);
-                }
-            }
-        } else {
-            break;
-        }
-    }
-}
-
-void VL_WRITEMEM_N(bool hex,  // Hex format, else binary
-                   int bits,  // Width of each array row
-                   QData depth,  // Number of rows
-                   int array_lsb,  // Index of first row. Valid row addresses
-                   //              //  range from array_lsb up to (array_lsb + depth - 1)
-                   const std::string& filename,  // Output file name
-                   const void* memp,  // Array state
-                   QData start,  // First array row address to write
-                   QData end  // Last address to write, or ~0 when not specified
-                   ) VL_MT_SAFE {
-    const QData addr_max = array_lsb + depth - 1;
-    if (start < static_cast(array_lsb)) start = array_lsb;
-    if (end > addr_max) end = addr_max;
-
-    VlWriteMem wmem{hex, bits, filename, start, end};
-    if (VL_UNLIKELY(!wmem.isOpen())) return;
-
-    for (QData addr = start; addr <= end; ++addr) {
-        const QData row_offset = addr - array_lsb;
-        if (bits <= 8) {
-            const CData* const datap = &(reinterpret_cast(memp))[row_offset];
-            wmem.print(addr, false, datap);
-        } else if (bits <= 16) {
-            const SData* const datap = &(reinterpret_cast(memp))[row_offset];
-            wmem.print(addr, false, datap);
-        } else if (bits <= 32) {
-            const IData* const datap = &(reinterpret_cast(memp))[row_offset];
-            wmem.print(addr, false, datap);
-        } else if (bits <= 64) {
-            const QData* const datap = &(reinterpret_cast(memp))[row_offset];
-            wmem.print(addr, false, datap);
-        } else {
-            const WDataInP memDatap = reinterpret_cast(memp);
-            const WDataInP datap = &memDatap[row_offset * VL_WORDS_I(bits)];
-            wmem.print(addr, false, datap);
-        }
-    }
-}
-
-//===========================================================================
-// Timescale conversion
-
-static const char* vl_time_str(int scale) VL_PURE {
-    static const char* const s_names[]
-        = {"100s",  "10s",  "1s",  "100ms", "10ms", "1ms", "100us", "10us", "1us",
-           "100ns", "10ns", "1ns", "100ps", "10ps", "1ps", "100fs", "10fs", "1fs"};
-    if (VL_UNLIKELY(scale > 2 || scale < -15)) scale = 0;
-    return s_names[2 - scale];
-}
-double vl_time_multiplier(int scale) VL_PURE {
-    // Return timescale multiplier -18 to +18
-    // For speed, this does not check for illegal values
-    if (scale < 0) {
-        static const double neg10[] = {1.0,
-                                       0.1,
-                                       0.01,
-                                       0.001,
-                                       0.0001,
-                                       0.00001,
-                                       0.000001,
-                                       0.0000001,
-                                       0.00000001,
-                                       0.000000001,
-                                       0.0000000001,
-                                       0.00000000001,
-                                       0.000000000001,
-                                       0.0000000000001,
-                                       0.00000000000001,
-                                       0.000000000000001,
-                                       0.0000000000000001,
-                                       0.00000000000000001,
-                                       0.000000000000000001};
-        return neg10[-scale];
-    } else {
-        static const double pow10[] = {1.0,
-                                       10.0,
-                                       100.0,
-                                       1000.0,
-                                       10000.0,
-                                       100000.0,
-                                       1000000.0,
-                                       10000000.0,
-                                       100000000.0,
-                                       1000000000.0,
-                                       10000000000.0,
-                                       100000000000.0,
-                                       1000000000000.0,
-                                       10000000000000.0,
-                                       100000000000000.0,
-                                       1000000000000000.0,
-                                       10000000000000000.0,
-                                       100000000000000000.0,
-                                       1000000000000000000.0};
-        return pow10[scale];
-    }
-}
-uint64_t vl_time_pow10(int n) {
-    static const uint64_t pow10[20] = {
-        1ULL,
-        10ULL,
-        100ULL,
-        1000ULL,
-        10000ULL,
-        100000ULL,
-        1000000ULL,
-        10000000ULL,
-        100000000ULL,
-        1000000000ULL,
-        10000000000ULL,
-        100000000000ULL,
-        1000000000000ULL,
-        10000000000000ULL,
-        100000000000000ULL,
-        1000000000000000ULL,
-        10000000000000000ULL,
-        100000000000000000ULL,
-        1000000000000000000ULL,
-    };
-    return pow10[n];
-}
-
-std::string vl_timescaled_double(double value, const char* format) VL_PURE {
-    const char* suffixp = "s";
-    // clang-format off
-    if      (value >= 1e0)   { suffixp = "s"; value *= 1e0; }
-    else if (value >= 1e-3)  { suffixp = "ms"; value *= 1e3; }
-    else if (value >= 1e-6)  { suffixp = "us"; value *= 1e6; }
-    else if (value >= 1e-9)  { suffixp = "ns"; value *= 1e9; }
-    else if (value >= 1e-12) { suffixp = "ps"; value *= 1e12; }
-    else if (value >= 1e-15) { suffixp = "fs"; value *= 1e15; }
-    else if (value >= 1e-18) { suffixp = "as"; value *= 1e18; }
-    // clang-format on
-    char valuestr[100];
-    VL_SNPRINTF(valuestr, 100, format, value, suffixp);
-    return std::string{valuestr};  // Gets converted to string, so no ref to stack
-}
-
-void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
-                       const VerilatedContext* contextp) VL_MT_SAFE {
-    VL_PRINTF_MT("Time scale of %s is %s / %s\n", namep, timeunitp,
-                 contextp->timeprecisionString());
-}
-void VL_TIMEFORMAT_IINI(bool hasUnits, int units, bool hasPrecision, int precision, bool hasSuffix,
-                        const std::string& suffix, bool hasWidth, int width,
-                        VerilatedContext* contextp) VL_MT_SAFE {
-    if (hasUnits) contextp->impp()->timeFormatUnits(units);
-    if (hasPrecision) contextp->impp()->timeFormatPrecision(precision);
-    if (hasSuffix) contextp->impp()->timeFormatSuffix(suffix);
-    if (hasWidth) contextp->impp()->timeFormatWidth(width);
-}
-
-//======================================================================
-// VerilatedContext:: Methods
-
-VerilatedContext::VerilatedContext()
-    : m_impdatap{new VerilatedContextImpData} {
-    Verilated::lastContextp(this);
-    Verilated::threadContextp(this);
-    m_ns.m_coverageFilename = "coverage.dat";
-    m_ns.m_profExecFilename = "profile_exec.dat";
-    m_ns.m_profVltFilename = "profile.vlt";
-    m_ns.m_solverProgram = VlOs::getenvStr("VERILATOR_SOLVER", VL_SOLVER_DEFAULT);
-    m_fdps.resize(31);
-    std::fill(m_fdps.begin(), m_fdps.end(), static_cast(nullptr));
-    m_fdFreeMct.resize(30);
-    IData id = 1;
-    for (std::size_t i = 0; i < m_fdFreeMct.size(); ++i, ++id) m_fdFreeMct[i] = id;
-}
-
-// Must declare here not in interface, as otherwise forward declarations not known
-VerilatedContext::~VerilatedContext() {
-    checkMagic(this);
-    m_magic = 0x1;  // Arbitrary but 0x1 is what Verilator src uses for a deleted pointer
-}
-
-void VerilatedContext::checkMagic(const VerilatedContext* contextp) {
-    if (VL_UNLIKELY(!contextp || contextp->m_magic != MAGIC)) {
-        VL_FATAL_MT("", 0, "",  // LCOV_EXCL_LINE
-                    "Attempt to create model using a bad/deleted VerilatedContext pointer");
-    }
-}
-
-VerilatedContext::Serialized::Serialized() {
-    constexpr int8_t picosecond = -12;
-    m_timeunit = picosecond;  // Initial value until overridden by _Vconfigure
-    m_timeprecision = picosecond;  // Initial value until overridden by _Vconfigure
-}
-
-bool VerilatedContext::assertOn() const VL_MT_SAFE { return m_s.m_assertOn; }
-void VerilatedContext::assertOn(bool flag) VL_MT_SAFE {
-    // Set all assert and directive types when true, clear otherwise.
-    m_s.m_assertOn = VL_MASK_I(ASSERT_ON_WIDTH) * flag;
-}
-bool VerilatedContext::assertOnGet(VerilatedAssertType_t type,
-                                   VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE {
-    // Check if selected directive type bit in the assertOn is enabled for assertion type.
-    // Note: it is assumed that this is checked only for one type at the time.
-
-    // Flag unspecified assertion types as disabled.
-    if (type == 0) return false;
-
-    // Get index of 3-bit group guarding assertion type status.
-    // Since the assertOnGet is generated __always__ for a single assert type, we assume that only
-    // a single bit will be set. Thus, ceil log2 will work fine.
-    VL_DEBUG_IFDEF(assert((type & (type - 1)) == 0););
-    const IData typeMaskPosition = VL_CLOG2_I(type);
-
-    // Check if directive type bit is enabled in corresponding assertion type bits.
-    return m_s.m_assertOn & (directive << (typeMaskPosition * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH));
-}
-void VerilatedContext::assertOnSet(VerilatedAssertType_t types,
-                                   VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
-    // For each assertion type, set directive bits.
-
-    // Iterate through all positions of assertion type bits. If bit for this assertion type is set,
-    // set directive type bits mask at this group index.
-    for (int i = 0; i < std::numeric_limits::digits; ++i) {
-        if (VL_BITISSET_I(types, i))
-            m_s.m_assertOn |= directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH);
-    }
-}
-void VerilatedContext::assertOnClear(VerilatedAssertType_t types,
-                                     VerilatedAssertDirectiveType_t directives) VL_MT_SAFE {
-    // Iterate through all positions of assertion type bits. If bit for this assertion type is set,
-    // clear directive type bits mask at this group index.
-    for (int i = 0; i < std::numeric_limits::digits; ++i) {
-        if (VL_BITISSET_I(types, i))
-            m_s.m_assertOn &= ~(directives << (i * ASSERT_DIRECTIVE_TYPE_MASK_WIDTH));
-    }
-}
-void VerilatedContext::calcUnusedSigs(bool flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_s.m_calcUnusedSigs = flag;
-}
-void VerilatedContext::coverageFilename(const std::string& flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_ns.m_coverageFilename = flag;
-}
-std::string VerilatedContext::coverageFilename() const VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    return m_ns.m_coverageFilename;
-}
-void VerilatedContext::dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
-    const VerilatedLockGuard lock{m_timeDumpMutex};
-    m_dumpfile = flag;
-}
-std::string VerilatedContext::dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
-    const VerilatedLockGuard lock{m_timeDumpMutex};
-    return m_dumpfile;
-}
-std::string VerilatedContext::dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
-    std::string out = dumpfile();
-    if (VL_UNLIKELY(out.empty())) {
-        VL_PRINTF_MT("%%Warning: $dumpvar ignored as not preceded by $dumpfile\n");
-        return "";
-    }
-    return out;
-}
-void VerilatedContext::errorCount(int val) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_s.m_errorCount = val;
-}
-void VerilatedContext::errorCountInc() VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    ++m_s.m_errorCount;
-}
-void VerilatedContext::errorLimit(int val) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_s.m_errorLimit = val;
-}
-void VerilatedContext::fatalOnError(bool flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_s.m_fatalOnError = flag;
-}
-void VerilatedContext::fatalOnVpiError(bool flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_s.m_fatalOnVpiError = flag;
-}
-void VerilatedContext::gotError(bool flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_s.m_gotError = flag;
-}
-void VerilatedContext::gotFinish(bool flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_s.m_gotFinish = flag;
-}
-void VerilatedContext::profExecStart(uint64_t flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_ns.m_profExecStart = flag;
-}
-void VerilatedContext::profExecWindow(uint64_t flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_ns.m_profExecWindow = flag;
-}
-void VerilatedContext::profExecFilename(const std::string& flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_ns.m_profExecFilename = flag;
-}
-std::string VerilatedContext::profExecFilename() const VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    return m_ns.m_profExecFilename;
-}
-void VerilatedContext::profVltFilename(const std::string& flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_ns.m_profVltFilename = flag;
-}
-std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    return m_ns.m_profVltFilename;
-}
-void VerilatedContext::solverProgram(const std::string& flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_ns.m_solverProgram = flag;
-}
-std::string VerilatedContext::solverProgram() const VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    return m_ns.m_solverProgram;
-}
-void VerilatedContext::quiet(bool flag) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_s.m_quiet = flag;
-}
-void VerilatedContext::randReset(int val) VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_mutex};
-    m_s.m_randReset = val;
-}
-void VerilatedContext::timeunit(int value) VL_MT_SAFE {
-    if (value < 0) value = -value;  // Stored as 0..15
-    const VerilatedLockGuard lock{m_mutex};
-    m_s.m_timeunit = value;
-}
-const char* VerilatedContext::timeunitString() const VL_MT_SAFE { return vl_time_str(timeunit()); }
-const char* VerilatedContext::timeprecisionString() const VL_MT_SAFE {
-    return vl_time_str(timeprecision());
-}
-
-void VerilatedContext::threads(unsigned n) {
-    if (n == 0) VL_FATAL_MT(__FILE__, __LINE__, "", "Simulation threads must be >= 1");
-
-    if (m_threadPool) {
-        VL_FATAL_MT(
-            __FILE__, __LINE__, "",
-            "%Error: Cannot set simulation threads after the thread pool has been created.");
-    }
-
-    m_useNumaAssign = true;
-    if (m_threads == n) return;  // To avoid unnecessary warnings
-    m_threads = n;
-    const unsigned threadsAvailableToProcess = VlOs::getProcessDefaultParallelism();
-    if (m_threads > threadsAvailableToProcess) {
-        VL_PRINTF_MT("%%Warning: Process has %u hardware threads available, but simulation thread "
-                     "count set to %u. This will likely cause significant slowdown.\n",
-                     threadsAvailableToProcess, m_threads);
-    }
-}
-
-void VerilatedContext::useNumaAssign(bool flag) { m_useNumaAssign = flag; }
-
-void VerilatedContext::commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex) {
-    // Not locking m_argMutex here, it is done in impp()->commandArgsAddGuts
-    // m_argMutex here is the same as in impp()->commandArgsAddGuts;
-    // due to clang limitations, it doesn't properly check it
-    impp()->commandArgsGuts(argc, argv);
-}
-void VerilatedContext::commandArgsAdd(int argc, const char** argv)
-    VL_MT_SAFE_EXCLUDES(m_argMutex) {
-    // Not locking m_argMutex here, it is done in impp()->commandArgsAddGuts
-    // m_argMutex here is the same as in impp()->commandArgsAddGuts;
-    // due to clang limitations, it doesn't properly check it
-    impp()->commandArgsAddGutsLock(argc, argv);
-}
-const char* VerilatedContext::commandArgsPlusMatch(const char* prefixp)
-    VL_MT_SAFE_EXCLUDES(m_argMutex) {
-    const std::string& match = impp()->argPlusMatch(prefixp);
-    static thread_local char t_outstr[VL_VALUE_STRING_MAX_WIDTH];
-    if (match.empty()) return "";
-    char* dp = t_outstr;
-    for (const char* sp = match.c_str(); *sp && (dp - t_outstr) < (VL_VALUE_STRING_MAX_WIDTH - 2);)
-        *dp++ = *sp++;
-    *dp++ = '\0';
-    return t_outstr;
-}
-void VerilatedContext::internalsDump() const VL_MT_SAFE {
-    VL_PRINTF_MT("internalsDump:\n");
-    VerilatedImp::versionDump();
-    impp()->commandArgDump();
-    impp()->scopesDump();
-    VerilatedImp::exportsDump();
-    VerilatedImp::userDump();
-}
-
-void VerilatedContext::addModel(const VerilatedModel* modelp) {
-    if (!quiet()) {
-        // CPU time isn't read as starting point until model creation, so that quiet() is set
-        // Thus if quiet(), avoids slow OS read affecting some usages that make many models
-        const VerilatedLockGuard lock{m_mutex};
-        m_ns.m_cpuTimeStart.start();
-        m_ns.m_wallTimeStart.start();
-    }
-
-    // We look for time passing, as opposed to post-eval(), as embedded
-    // models might get added inside initial blocks.
-    if (VL_UNLIKELY(time())) {
-        const std::string msg
-            = "Adding model '"s + modelp->hierName()
-              + "' when time is non-zero. ... Suggest check time(), or for restarting"
-                " model use a new VerilatedContext";
-        VL_FATAL_MT("", 0, "", msg.c_str());
-    }
-
-    threadPoolp();  // Ensure thread pool is created, so m_threads cannot change any more
-    m_threadsInModels += modelp->threads();
-    if (VL_UNLIKELY(modelp->threads() > m_threads)) {
-        std::ostringstream msg;
-        msg << "VerilatedContext has " << m_threads << " threads but model '"
-            << modelp->modelName() << "' (instantiated as '" << modelp->hierName()
-            << "') was Verilated with --threads " << modelp->threads() << ".\n";
-        const std::string str = msg.str();
-        VL_FATAL_MT(__FILE__, __LINE__, modelp->hierName(), str.c_str());
-    }
-}
-
-VerilatedVirtualBase* VerilatedContext::threadPoolp() {
-    if (m_threads == 1) return nullptr;
-    if (!m_threadPool) m_threadPool.reset(new VlThreadPool{this, m_threads - 1});
-    return m_threadPool.get();
-}
-
-void VerilatedContext::prepareClone() { delete m_threadPool.release(); }
-
-VerilatedVirtualBase* VerilatedContext::threadPoolpOnClone() {
-    if (VL_UNLIKELY(m_threadPool)) (void)m_threadPool.release();
-    m_threadPool = std::unique_ptr(new VlThreadPool{this, m_threads - 1});
-    return m_threadPool.get();
-}
-
-VerilatedVirtualBase*
-VerilatedContext::enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&)) {
-    if (!m_executionProfiler) m_executionProfiler.reset(construct(*this));
-    return m_executionProfiler.get();
-}
-
-//======================================================================
-// VerilatedContextImp:: Methods - command line
-
-void VerilatedContextImp::commandArgsGuts(int argc, const char** argv)
-    VL_MT_SAFE_EXCLUDES(m_argMutex) {
-    const VerilatedLockGuard lock{m_argMutex};
-    m_args.m_argVec.clear();  // Empty first, then add
-    commandArgsAddGuts(argc, argv);
-}
-
-void VerilatedContextImp::commandArgsAddGutsLock(int argc, const char** argv)
-    VL_MT_SAFE_EXCLUDES(m_argMutex) {
-    const VerilatedLockGuard lock{m_argMutex};
-    commandArgsAddGuts(argc, argv);
-}
-
-void VerilatedContextImp::commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex) {
-    if (!m_args.m_argVecLoaded) m_args.m_argVec.clear();
-    for (int i = 0; i < argc; ++i) {
-        m_args.m_argVec.emplace_back(argv[i]);
-        commandArgVl(argv[i]);
-    }
-    m_args.m_argVecLoaded = true;  // Can't just test later for empty vector, no arguments is ok
-}
-void VerilatedContextImp::commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex) {
-    const VerilatedLockGuard lock{m_argMutex};
-    VL_PRINTF_MT("  Argv:");
-    for (const auto& i : m_args.m_argVec) VL_PRINTF_MT(" %s", i.c_str());
-    VL_PRINTF_MT("\n");
-}
-std::string VerilatedContextImp::argPlusMatch(const char* prefixp)
-    VL_MT_SAFE_EXCLUDES(m_argMutex) {
-    const VerilatedLockGuard lock{m_argMutex};
-    // Note prefixp does not include the leading "+"
-    const size_t len = std::strlen(prefixp);
-    if (VL_UNLIKELY(!m_args.m_argVecLoaded)) {
-        m_args.m_argVecLoaded = true;  // Complain only once
-        VL_FATAL_MT("unknown", 0, "",
-                    "%Error: Verilog called $test$plusargs or $value$plusargs without"
-                    " testbench C first calling Verilated::commandArgs(argc,argv).");
-    }
-    for (const auto& i : m_args.m_argVec) {
-        if (i[0] == '+') {
-            if (0 == std::strncmp(prefixp, i.c_str() + 1, len)) return i;
-        }
-    }
-    return "";
-}
-// Return string representing current argv
-// Only used by VPI so uses static storage, only supports most recent called context
-std::pair VerilatedContextImp::argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex) {
-    const VerilatedLockGuard lock{m_argMutex};
-    static bool s_loaded = false;
-    static int s_argc = 0;
-    static char** s_argvp = nullptr;
-    if (VL_UNLIKELY(!s_loaded)) {
-        s_loaded = true;
-        s_argc = static_cast(m_args.m_argVec.size());
-        s_argvp = new char*[s_argc + 1];
-        int in = 0;
-        for (const auto& i : m_args.m_argVec) {
-            s_argvp[in] = new char[i.length() + 1];
-            std::memcpy(s_argvp[in], i.c_str(), i.length() + 1);
-            ++in;
-        }
-        s_argvp[s_argc] = nullptr;
-    }
-    return std::make_pair(s_argc, s_argvp);
-}
-
-void VerilatedContextImp::commandArgVl(const std::string& arg) {
-    if (0 == std::strncmp(arg.c_str(), "+verilator+", std::strlen("+verilator+"))) {
-        std::string str;
-        uint64_t u64;
-        if (commandArgVlString(arg, "+verilator+coverage+file+", str)) {
-            coverageFilename(str);
-        } else if (arg == "+verilator+debug") {
-            Verilated::debug(4);
-        } else if (commandArgVlUint64(arg, "+verilator+debugi+", u64, 0,
-                                      std::numeric_limits::max())) {
-            Verilated::debug(static_cast(u64));
-        } else if (commandArgVlUint64(arg, "+verilator+error+limit+", u64, 0,
-                                      std::numeric_limits::max())) {
-            errorLimit(static_cast(u64));
-        } else if (arg == "+verilator+help") {
-            VerilatedImp::versionDump();
-            VL_PRINTF_MT("For help, please see 'verilator --help'\n");
-            VL_FATAL_MT("COMMAND_LINE", 0, "",
-                        "Exiting due to command line argument (not an error)");
-        } else if (arg == "+verilator+noassert") {
-            assertOn(false);
-        } else if (commandArgVlUint64(arg, "+verilator+prof+exec+start+", u64)) {
-            profExecStart(u64);
-        } else if (commandArgVlUint64(arg, "+verilator+prof+exec+window+", u64, 1)) {
-            profExecWindow(u64);
-        } else if (commandArgVlString(arg, "+verilator+prof+exec+file+", str)) {
-            profExecFilename(str);
-        } else if (commandArgVlString(arg, "+verilator+prof+vlt+file+", str)) {
-            profVltFilename(str);
-        } else if (arg == "+verilator+quiet") {
-            quiet(true);
-        } else if (commandArgVlUint64(arg, "+verilator+rand+reset+", u64, 0, 2)) {
-            randReset(static_cast(u64));
-        } else if (commandArgVlUint64(arg, "+verilator+wno+unsatconstr+", u64, 0, 1)) {
-            warnUnsatConstr(u64 == 0);  // wno means disable, so invert
-        } else if (commandArgVlUint64(arg, "+verilator+seed+", u64, 1,
-                                      std::numeric_limits::max())) {
-            randSeed(static_cast(u64));
-        } else if (arg == "+verilator+V") {
-            VerilatedImp::versionDump();  // Someday more info too
-            VL_FATAL_MT("COMMAND_LINE", 0, "",
-                        "Exiting due to command line argument (not an error)");
-        } else if (arg == "+verilator+version") {
-            VerilatedImp::versionDump();
-            VL_FATAL_MT("COMMAND_LINE", 0, "",
-                        "Exiting due to command line argument (not an error)");
-        } else {
-            const std::string msg = "Unknown runtime argument: " + arg;
-            VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str());
-        }
-    }
-}
-
-bool VerilatedContextImp::commandArgVlString(const std::string& arg, const std::string& prefix,
-                                             std::string& valuer) {
-    const size_t len = prefix.length();
-    if (0 == std::strncmp(prefix.c_str(), arg.c_str(), len)) {
-        valuer = arg.substr(len);
-        return true;
-    } else {
-        return false;
-    }
-}
-
-bool VerilatedContextImp::commandArgVlUint64(const std::string& arg, const std::string& prefix,
-                                             uint64_t& valuer, uint64_t min, uint64_t max) {
-    std::string str;
-    if (commandArgVlString(arg, prefix, str)) {
-        const auto fail = [&](const std::string& extra = "") {
-            std::stringstream ss;
-            ss << "Argument '" << prefix << "' must be an unsigned integer";
-            if (min != std::numeric_limits::min()) ss << ", greater than " << min - 1;
-            if (max != std::numeric_limits::max()) ss << ", less than " << max + 1;
-            if (!extra.empty()) ss << ". " << extra;
-            const std::string& msg = ss.str();
-            VL_FATAL_MT("COMMAND_LINE", 0, "", msg.c_str());
-        };
-
-        if (std::any_of(str.cbegin(), str.cend(), [](int c) { return !std::isdigit(c); })) fail();
-        char* end;
-        valuer = std::strtoull(str.c_str(), &end, 10);
-        if (errno == ERANGE) fail("Value out of range of uint64_t");
-        if (valuer < min || valuer > max) fail();
-        return true;
-    }
-    return false;
-}
-
-//======================================================================
-// VerilatedContext:: + VerilatedContextImp:: Methods - random
-
-void VerilatedContext::randSeed(int val) VL_MT_SAFE {
-    // As we have per-thread state, the epoch must be static,
-    // and so the rand seed's mutex must also be static
-    const VerilatedLockGuard lock{VerilatedContextImp::s().s_randMutex};
-    m_s.m_randSeed = val;
-    const uint64_t newEpoch = VerilatedContextImp::s().s_randSeedEpoch + 1;
-    // Observers must see new epoch AFTER seed updated
-    std::atomic_signal_fence(std::memory_order_release);
-    VerilatedContextImp::s().s_randSeedEpoch = newEpoch;
-}
-uint64_t VerilatedContextImp::randSeedDefault64() const VL_MT_SAFE {
-    if (randSeed() != 0) {
-        return ((static_cast(randSeed()) << 32) ^ (static_cast(randSeed())));
-    } else {
-        return ((static_cast(vl_sys_rand32()) << 32)
-                ^ (static_cast(vl_sys_rand32())));
-    }
-}
-
-//======================================================================
-// VerilatedContext:: Statistics
-
-double VerilatedContext::statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    return m_ns.m_cpuTimeStart.deltaTime();
-}
-double VerilatedContext::statWallTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    return m_ns.m_wallTimeStart.deltaTime();
-}
-void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
-    if (quiet()) return;
-    VL_PRINTF("- S i m u l a t i o n   R e p o r t: %s %s\n", Verilated::productName(),
-              Verilated::productVersion());
-    const std::string endwhy = gotError() ? "$stop" : gotFinish() ? "$finish" : "end";
-    const double simtimeInUnits = VL_TIME_Q() * vl_time_multiplier(timeunit())
-                                  * vl_time_multiplier(timeprecision() - timeunit());
-    const std::string simtime = vl_timescaled_double(simtimeInUnits);
-    const double walltime = statWallTimeSinceStart();
-    const double cputime = statCpuTimeSinceStart();
-    const std::string simtimePerf
-        = vl_timescaled_double((cputime != 0.0) ? (simtimeInUnits / cputime) : 0, "%0.3f %s");
-    VL_PRINTF("- Verilator: %s at %s; walltime %0.3f s; speed %s/s\n", endwhy.c_str(),
-              simtime.c_str(), walltime, simtimePerf.c_str());
-    uint64_t memPeak, memCurrent;
-    VlOs::memUsageBytes(memPeak /*ref*/, memCurrent /*ref*/);
-    const double modelMB = memPeak / 1024.0 / 1024.0;
-    VL_PRINTF("- Verilator: cpu %0.3f s on %u threads; allocated %0.0f MB\n", cputime,
-              threadsInModels(), modelMB);
-}
-
-//======================================================================
-// VerilatedContext:: Methods - scopes
-
-void VerilatedContext::scopesDump() const VL_MT_SAFE {
-    const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
-    VL_PRINTF_MT("  scopesDump:\n");
-    for (const auto& i : m_impdatap->m_nameMap) {
-        const VerilatedScope* const scopep = i.second;
-        scopep->scopeDump();
-    }
-    VL_PRINTF_MT("\n");
-}
-
-void VerilatedContextImp::scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE {
-    // Slow ok - called once/scope at construction
-    const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
-    const auto it = m_impdatap->m_nameMap.find(scopep->name());
-    if (it == m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.emplace(scopep->name(), scopep);
-}
-void VerilatedContextImp::scopeErase(const VerilatedScope* scopep) VL_MT_SAFE {
-    // Slow ok - called once/scope at destruction
-    const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
-    VerilatedImp::userEraseScope(scopep);
-    const auto it = m_impdatap->m_nameMap.find(scopep->name());
-    if (it != m_impdatap->m_nameMap.end()) m_impdatap->m_nameMap.erase(it);
-}
-const VerilatedScope* VerilatedContext::scopeFind(const char* namep) const VL_MT_SAFE {
-    // Thread save only assuming this is called only after model construction completed
-    const VerilatedLockGuard lock{m_impdatap->m_nameMutex};
-    // If too slow, can assume this is only VL_MT_SAFE_POSINIT
-    const auto& it = m_impdatap->m_nameMap.find(namep);
-    if (VL_UNLIKELY(it == m_impdatap->m_nameMap.end())) return nullptr;
-    return it->second;
-}
-const VerilatedScopeNameMap* VerilatedContext::scopeNameMap() VL_MT_SAFE {
-    return &(impp()->m_impdatap->m_nameMap);
-}
-
-//======================================================================
-// VerilatedContext:: Methods - trace
-
-void VerilatedContext::trace(VerilatedTraceBaseC* tfp, int levels, int options) {
-    VL_DEBUG_IF(VL_DBG_MSGF("+ VerilatedContext::trace\n"););
-    if (tfp->isOpen()) {
-        VL_FATAL_MT("", 0, "",
-                    "Testbench C call to 'VerilatedContext::trace()' must not be called"
-                    " after 'VerilatedTrace*::open()'\n");
-    }
-    {
-        // Legacy usage may call {modela}->trace(...) then {modelb}->trace(...)
-        // So check for and suppress second and later calls
-        if (tfp->modelConnected()) return;
-        tfp->modelConnected(true);
-    }
-    // We rely on m_ns.m_traceBaseModelCbs being stable when trace() is called
-    // nope: const VerilatedLockGuard lock{m_mutex};
-    if (m_ns.m_traceBaseModelCbs.empty())
-        VL_FATAL_MT("", 0, "",
-                    "Testbench C call to 'VerilatedContext::trace()' requires model(s) Verilated"
-                    " with --trace-fst or --trace-vcd option");
-    for (const auto& cbr : m_ns.m_traceBaseModelCbs) cbr(tfp, levels, options);
-}
-void VerilatedContext::traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE {
-    // Model creation registering a callback for when Verilated::trace() called
-    const VerilatedLockGuard lock{m_mutex};
-    m_ns.m_traceBaseModelCbs.push_back(cb);
-}
-
-//======================================================================
-// VerilatedSyms:: Methods
-
-VerilatedSyms::VerilatedSyms(VerilatedContext* contextp)
-    : _vm_contextp__(contextp ? contextp : Verilated::threadContextp()) {
-    VerilatedContext::checkMagic(_vm_contextp__);
-    Verilated::threadContextp(_vm_contextp__);
-    __Vm_evalMsgQp = new VerilatedEvalMsgQueue;
-}
-
-VerilatedSyms::~VerilatedSyms() {
-    VerilatedContext::checkMagic(_vm_contextp__);
-    delete __Vm_evalMsgQp;
-}
-
-//===========================================================================
-// Verilated:: Methods
-
-void Verilated::debug(int level) VL_MT_SAFE {
-    s_debug = level;
-    if (level) {
-#ifdef VL_DEBUG
-        VL_DEBUG_IF(VL_DBG_MSGF("- Verilated::debug is on."
-                                " Message prefix indicates {,}.\n"););
-#else
-        VL_PRINTF_MT("- Verilated::debug attempted,"
-                     " but compiled without VL_DEBUG, so messages suppressed.\n"
-                     "- Suggest remake using 'make ... CPPFLAGS=-DVL_DEBUG'\n");
-#endif
-    }
-}
-
-const char* Verilated::catName(const char* n1, const char* n2, const char* delimiter) VL_MT_SAFE {
-    // Used by symbol table creation to make module names
-    static thread_local char* t_strp = nullptr;
-    static thread_local size_t t_len = 0;
-    const size_t newlen = std::strlen(n1) + std::strlen(n2) + std::strlen(delimiter) + 1;
-    if (VL_UNLIKELY(!t_strp || newlen > t_len)) {
-        if (t_strp) delete[] t_strp;
-        t_strp = new char[newlen];
-        t_len = newlen;
-    }
-    char* dp = t_strp;
-    for (const char* sp = n1; *sp;) *dp++ = *sp++;
-    for (const char* sp = delimiter; *sp;) *dp++ = *sp++;
-    for (const char* sp = n2; *sp;) *dp++ = *sp++;
-    *dp++ = '\0';
-    return t_strp;
-}
-
-//=========================================================================
-// Flush and exit callbacks
-
-// Keeping these out of class Verilated to avoid having to include 
-// in verilated.h (for compilation speed)
-using VoidPCbList = std::list>;
-static struct {
-    VerilatedMutex s_flushMutex;
-    VoidPCbList s_flushCbs VL_GUARDED_BY(s_flushMutex);
-    VerilatedMutex s_exitMutex;
-    VoidPCbList s_exitCbs VL_GUARDED_BY(s_exitMutex);
-} VlCbStatic;
-
-static void addCbFlush(Verilated::VoidPCb cb, void* datap)
-    VL_MT_SAFE_EXCLUDES(VlCbStatic.s_flushMutex) {
-    const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
-    std::pair pair(cb, datap);
-    VlCbStatic.s_flushCbs.remove(pair);  // Just in case it's a duplicate
-    VlCbStatic.s_flushCbs.push_back(pair);
-}
-static void addCbExit(Verilated::VoidPCb cb, void* datap)
-    VL_MT_SAFE_EXCLUDES(VlCbStatic.s_exitMutex) {
-    const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
-    std::pair pair(cb, datap);
-    VlCbStatic.s_exitCbs.remove(pair);  // Just in case it's a duplicate
-    VlCbStatic.s_exitCbs.push_back(pair);
-}
-static void removeCbFlush(Verilated::VoidPCb cb, void* datap)
-    VL_MT_SAFE_EXCLUDES(VlCbStatic.s_flushMutex) {
-    const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
-    std::pair pair(cb, datap);
-    VlCbStatic.s_flushCbs.remove(pair);
-}
-static void removeCbExit(Verilated::VoidPCb cb, void* datap)
-    VL_MT_SAFE_EXCLUDES(VlCbStatic.s_exitMutex) {
-    const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
-    std::pair pair(cb, datap);
-    VlCbStatic.s_exitCbs.remove(pair);
-}
-static void runCallbacks(const VoidPCbList& cbs) VL_MT_SAFE {
-    for (const auto& i : cbs) i.first(i.second);
-}
-
-void Verilated::addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE { addCbFlush(cb, datap); }
-void Verilated::removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE { removeCbFlush(cb, datap); }
-void Verilated::runFlushCallbacks() VL_MT_SAFE {
-    // Flush routines may call flush, so avoid mutex deadlock
-    static std::atomic s_recursing;
-    if (!s_recursing++) {
-        const VerilatedLockGuard lock{VlCbStatic.s_flushMutex};
-        runCallbacks(VlCbStatic.s_flushCbs);
-    }
-    --s_recursing;
-    std::fflush(stderr);
-    std::fflush(stdout);
-    // When running internal code coverage (gcc --coverage, as opposed to
-    // verilator --coverage), dump coverage data to properly cover failing
-    // tests.
-    VL_GCOV_DUMP();
-}
-
-void Verilated::addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { addCbExit(cb, datap); }
-void Verilated::removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE { removeCbExit(cb, datap); }
-void Verilated::runExitCallbacks() VL_MT_SAFE {
-    static std::atomic s_recursing;
-    if (!s_recursing++) {
-        const VerilatedLockGuard lock{VlCbStatic.s_exitMutex};
-        runCallbacks(VlCbStatic.s_exitCbs);
-    }
-    --s_recursing;
-}
-
-const char* Verilated::productName() VL_PURE { return VERILATOR_PRODUCT; }
-const char* Verilated::productVersion() VL_PURE { return VERILATOR_VERSION; }
-
-void Verilated::nullPointerError(const char* filename, int linenum) VL_MT_SAFE {
-    // Slowpath - Called only on error
-    VL_FATAL_MT(filename, linenum, "", "Null pointer dereferenced");
-    VL_UNREACHABLE;
-}
-
-void Verilated::overWidthError(const char* signame) VL_MT_SAFE {
-    // Slowpath - Called only when signal sets too high of a bit
-    const std::string msg = ("Testbench C set input '"s + signame
-                             + "' to value that overflows what the signal's width can fit");
-    VL_FATAL_MT("unknown", 0, "", msg.c_str());
-    VL_UNREACHABLE;
-}
-
-void Verilated::scTimePrecisionError(int sc_prec, int vl_prec) VL_MT_SAFE {
-    std::ostringstream msg;
-    msg << "SystemC's sc_set_time_resolution is 10^-" << sc_prec
-        << ", which does not match Verilog timeprecision 10^-" << vl_prec
-        << ". Suggest use 'sc_set_time_resolution(" << vl_time_str(vl_prec)
-        << ")', or Verilator '--timescale-override " << vl_time_str(sc_prec) << "/"
-        << vl_time_str(sc_prec) << "'";
-    const std::string msgs = msg.str();
-    VL_FATAL_MT("", 0, "", msgs.c_str());
-    VL_UNREACHABLE;
-}
-
-void Verilated::scTraceBeforeElaborationError() VL_MT_SAFE {
-    // Slowpath - Called only when trace file opened before SystemC elaboration
-    VL_FATAL_MT("unknown", 0, "",
-                "%Error: Verilated*Sc::open(...) was called before sc_core::sc_start(). "
-                "Run sc_core::sc_start(sc_core::SC_ZERO_TIME) before opening a wave file.");
-    VL_UNREACHABLE;
-}
-
-void Verilated::stackCheck(QData needSize) VL_MT_UNSAFE {
-    // Slowpath - Called only when constructing
-#ifdef _VL_HAVE_GETRLIMIT
-    QData haveSize = 0;
-    rlimit rlim;
-    if (0 == getrlimit(RLIMIT_STACK, &rlim)) {
-        haveSize = rlim.rlim_cur;
-        if (haveSize == RLIM_INFINITY) haveSize = rlim.rlim_max;
-        if (haveSize == RLIM_INFINITY) haveSize = 0;
-    }
-    // VL_PRINTF_MT("-Info: stackCheck(%" PRIu64 ") have %" PRIu64 "\n", needSize, haveSize);
-    // Check and request for 1.5x need. This is automated so the user doesn't need to do anything.
-    QData requestSize = needSize + needSize / 2;
-    if (VL_UNLIKELY(haveSize && needSize && haveSize < requestSize)) {
-        // Try to increase the stack limit to the requested size
-        rlim.rlim_cur = requestSize;
-        if (
-#ifdef _VL_TEST_RLIMIT_FAIL
-            true ||
-#endif
-            setrlimit(RLIMIT_STACK, &rlim)) {
-            VL_PRINTF_MT("%%Warning: System has stack size %" PRIu64 " kb"
-                         " which may be too small; failed to request more"
-                         " using 'ulimit -s %" PRIu64 "'\n",
-                         haveSize / 1024, requestSize);
-        }
-    }
-#else
-    (void)needSize;  // Unused argument
-#endif
-}
-
-void Verilated::mkdir(const char* dirname) VL_MT_UNSAFE {
-#if defined(_WIN32) || defined(__MINGW32__)
-    ::mkdir(dirname);
-#else
-    ::mkdir(dirname, 0777);
-#endif
-}
-
-void Verilated::quiesce() VL_MT_SAFE {
-    // Wait until all threads under this evaluation are quiet
-}
-
-int Verilated::exportFuncNum(const char* namep) VL_MT_SAFE {
-    return VerilatedImp::exportFindNum(namep);
-}
-
-void Verilated::endOfThreadMTaskGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
-    VL_DEBUG_IF(VL_DBG_MSGF("End of thread mtask\n"););
-    VerilatedThreadMsgQueue::flush(evalMsgQp);
-}
-
-void Verilated::endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
-    // It doesn't work to set endOfEvalReqd on the threadpool thread
-    // and then check it on the eval thread since it's thread local.
-    // It should be ok to call into endOfEvalGuts, it returns immediately
-    // if there are no transactions.
-    VL_DEBUG_IF(VL_DBG_MSGF("End-of-eval cleanup\n"););
-    VerilatedThreadMsgQueue::flush(evalMsgQp);
-    evalMsgQp->process();
-}
-
-//===========================================================================
-// VerilatedImp:: Methods
-
-void VerilatedImp::versionDump() VL_MT_SAFE {
-    VL_PRINTF_MT("  Version: %s %s\n", Verilated::productName(), Verilated::productVersion());
-}
-
-//===========================================================================
-// VerilatedModel:: Methods
-
-VerilatedModel::VerilatedModel(VerilatedContext& context)
-    : m_context{context} {}
-
-std::unique_ptr VerilatedModel::traceConfig() const { return nullptr; }
-
-//======================================================================
-// VerilatedVar:: Methods
-
-// cppcheck-suppress unusedFunction  // Used by applications
-uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE {
-    uint32_t size = 1;
-    switch (vltype()) {
-    case VLVT_PTR: size = sizeof(void*); break;
-    case VLVT_UINT8: size = sizeof(CData); break;
-    case VLVT_UINT16: size = sizeof(SData); break;
-    case VLVT_UINT32: size = sizeof(IData); break;
-    case VLVT_UINT64: size = sizeof(QData); break;
-    case VLVT_WDATA: size = VL_WORDS_I(entBits()) * sizeof(IData); break;
-    default: size = 0; break;  // LCOV_EXCL_LINE
-    }
-    return size;
-}
-
-size_t VerilatedVarProps::totalSize() const {
-    size_t size = entSize();
-    for (int udim = 0; udim < udims(); ++udim) size *= m_unpacked[udim].elements();
-    return size;
-}
-
-void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE {
-    if (VL_UNLIKELY(dim <= 0 || dim > udims())) return nullptr;
-    if (VL_UNLIKELY(indx < low(dim) || indx > high(dim))) return nullptr;
-    const int indxAdj = indx - low(dim);
-    uint8_t* bytep = reinterpret_cast(datap);
-    // If on index 1 of a 2 index array, then each index 1 is index2sz*entsz
-    size_t slicesz = entSize();
-    for (int d = dim + 1; d <= udims(); ++d) slicesz *= elements(d);
-    bytep += indxAdj * slicesz;
-    return bytep;
-}
-
-//======================================================================
-// VerilatedScope:: Methods
-
-VerilatedScope::VerilatedScope(VerilatedSyms* symsp, const char* suffixp, const char* identifier,
-                               const char* defnamep, int8_t timeunit, Type type)
-    : m_symsp{symsp}
-    , m_namep{[symsp, suffixp]() {
-        // We don't want the space and reference-count access overhead of strings.
-        const char* prefixp = symsp->name();
-        char* const namep = new char[std::strlen(prefixp) + std::strlen(suffixp) + 2];
-        char* dp = namep;
-        for (const char* sp = prefixp; *sp;) *dp++ = *sp++;
-        if (*prefixp && *suffixp) *dp++ = '.';
-        for (const char* sp = suffixp; *sp;) *dp++ = *sp++;
-        *dp++ = '\0';
-        return namep;
-    }()}
-    , m_identifierp{identifier}
-    , m_defnamep{defnamep}
-    , m_timeunit{timeunit}
-    , m_type{type} {
-    Verilated::threadContextp()->impp()->scopeInsert(this);
-}
-
-VerilatedScope::~VerilatedScope() {
-    // Memory cleanup - not called during normal operation
-    Verilated::threadContextp()->impp()->scopeErase(this);
-    VL_DO_DANGLING(delete[] m_namep, m_namep);
-    VL_DO_DANGLING(delete[] m_callbacksp, m_callbacksp);
-    VL_DO_DANGLING(delete m_varsp, m_varsp);
-    VL_DEBUG_IFDEF(m_funcnumMax = 0;);
-}
-
-void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE {
-    // Slowpath - called once/scope*export at construction
-    // Insert a exported function into scope table
-    const int funcnum = VerilatedImp::exportInsert(namep, cb);
-    if (!finalize) {
-        // Need two passes so we know array size to create
-        // Alternative is to dynamically stretch the array, which is more code, and slower.
-        if (funcnum >= m_funcnumMax) m_funcnumMax = funcnum + 1;
-    } else {
-        if (VL_UNCOVERABLE(funcnum >= m_funcnumMax)) {
-            VL_FATAL_MT(__FILE__, __LINE__, "",  // LCOV_EXCL_LINE
-                        "Internal: Bad funcnum vs. pre-finalize maximum");
-        }
-        if (VL_UNLIKELY(!m_callbacksp)) {  // First allocation
-            m_callbacksp = new void*[m_funcnumMax];
-            std::memset(m_callbacksp, 0, m_funcnumMax * sizeof(void*));
-        }
-        m_callbacksp[funcnum] = cb;
-    }
-}
-
-void VerilatedScope::varInsert(const char* namep, void* datap, bool isParam,
-                               VerilatedVarType vltype, int vlflags, int udims,
-                               int pdims...) VL_MT_UNSAFE {
-    // Grab dimensions
-    // In the future we may just create a large table at emit time and
-    // statically construct from that.
-
-    if (!m_varsp) m_varsp = new VerilatedVarNameMap;
-    VerilatedVar var(namep, datap, vltype, static_cast(vlflags), udims, pdims,
-                     isParam);
-
-    va_list ap;
-    va_start(ap, pdims);
-    for (int i = 0; i < udims; ++i) {
-        const int msb = va_arg(ap, int);
-        const int lsb = va_arg(ap, int);
-        var.m_unpacked[i].m_left = msb;
-        var.m_unpacked[i].m_right = lsb;
-    }
-    for (int i = 0; i < pdims; ++i) {
-        const int msb = va_arg(ap, int);
-        const int lsb = va_arg(ap, int);
-        var.m_packed[i].m_left = msb;
-        var.m_packed[i].m_right = lsb;
-    }
-    va_end(ap);
-
-    m_varsp->emplace(namep, var);
-}
-
-// cppcheck-suppress unusedFunction  // Used by applications
-VerilatedVar* VerilatedScope::varFind(const char* namep) const VL_MT_SAFE_POSTINIT {
-    if (VL_LIKELY(m_varsp)) {
-        const auto it = m_varsp->find(namep);
-        if (VL_LIKELY(it != m_varsp->end())) return &(it->second);
-    }
-    return nullptr;
-}
-
-void* VerilatedScope::exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE {
-    if (VL_UNLIKELY(!scopep)) return exportFindNullError(funcnum);
-    // If function is registered only once across all scopes, fast path it.
-    // UVM for example expects to find uvm_polling_value_change_notify
-    // from a different scope than where decared.
-    VL_DEBUG_IFDEF(assert(funcnum < VerilatedImp::exportFlatCbs().size()););
-    {
-        void* const cbp = VerilatedImp::exportFlatCbs()[funcnum];
-        if (VL_LIKELY(cbp)) return cbp;
-    }
-    // Else specific scope-based export call
-    if (VL_LIKELY(funcnum < scopep->m_funcnumMax)) {
-        // m_callbacksp must be declared, as Max'es are > 0
-        void* const cbp = scopep->m_callbacksp[funcnum];
-        if (VL_LIKELY(cbp)) return cbp;
-    }
-    return scopep->exportFindError(funcnum);  // LCOV_EXCL_LINE
-}
-
-void* VerilatedScope::exportFindNullError(int funcnum) VL_MT_SAFE {
-    // Slowpath - Called only when find has failed
-    const std::string msg = ("Testbench C called '"s + VerilatedImp::exportName(funcnum)
-                             + "' but scope wasn't set, perhaps due to dpi import call without "
-                             + "'context', or missing svSetScope. See IEEE 1800-2023 35.5.3.");
-    VL_FATAL_MT("unknown", 0, "", msg.c_str());
-    return nullptr;
-}
-
-void* VerilatedScope::exportFindError(int funcnum) const VL_MT_SAFE {
-    // Slowpath - Called only when find has failed
-    const std::string msg
-        = ("Testbench C called '"s + VerilatedImp::exportName(funcnum)
-           + "' but this DPI export function exists only in other scopes, not scope '" + name()
-           + "'");
-    VL_FATAL_MT("unknown", 0, "", msg.c_str());
-    return nullptr;
-}
-
-void VerilatedScope::scopeDump() const {
-    VL_PRINTF_MT("    SCOPE %p: %s\n", this, name());
-    for (int i = 0; i < m_funcnumMax; ++i) {
-        if (m_callbacksp && m_callbacksp[i]) {
-            VL_PRINTF_MT("       DPI-EXPORT %p: %s\n", m_callbacksp[i],
-                         VerilatedImp::exportName(i));
-        }
-    }
-    if (const VerilatedVarNameMap* const ivarsp = this->varsp()) {
-        for (const auto& i : *ivarsp) VL_PRINTF_MT("       VAR %p: %s\n", &(i.second), i.first);
-    }
-}
-
-void VerilatedHierarchy::add(const VerilatedScope* fromp, const VerilatedScope* top) {
-    VerilatedImp::hierarchyAdd(fromp, top);
-}
-
-void VerilatedHierarchy::remove(const VerilatedScope* fromp, const VerilatedScope* top) {
-    VerilatedImp::hierarchyRemove(fromp, top);
-}
-
-void VerilatedHierarchy::clear() { VerilatedImp::hierarchyClear(); }
-
-//===========================================================================
-// VerilatedOneThreaded:: Methods
-
-#ifdef VL_DEBUG
-void VerilatedAssertOneThread::fatal_different() VL_MT_SAFE {
-    VL_FATAL_MT(__FILE__, __LINE__, "",
-                "Routine called that is single threaded, but called from"
-                " a different thread than the expected constructing thread");
-}
-#endif
-
-//===========================================================================
-// VlDeleter:: Methods
-
-void VlDeleter::deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE {
-    while (true) {
-        {
-            VerilatedLockGuard lock{m_mutex};
-            if (m_newGarbage.empty()) break;
-            m_deleteMutex.lock();
-            std::swap(m_newGarbage, m_deleteNow);
-            // m_mutex is unlocked here, so destructors can enqueue new objects
-        }
-        for (VlDeletable* const objp : m_deleteNow) delete objp;
-        m_deleteNow.clear();
-        m_deleteMutex.unlock();
-    }
-}
-
-//===========================================================================
-// OS functions (last, so we have minimal OS dependencies above)
-
-#define VL_ALLOW_VERILATEDOS_C
-#include "verilatedos_c.h"
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.h
deleted file mode 100644
index 15fdab26782..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated.h
+++ /dev/null
@@ -1,1056 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilated common header, include for all Verilated C files
-///
-/// This file is included automatically by Verilator at the top of all C++
-/// files it generates.  It contains standard macros and classes required
-/// by the Verilated code.
-///
-/// User wrapper code may need to include this to get appropriate
-/// structures, however they would generally just include the
-/// Verilated-model's header instead (which then includes this).
-///
-/// Those macro/function/variable starting or ending in _ are internal,
-/// however many of the other function/macros here are also internal.
-///
-//*************************************************************************
-
-#ifndef VERILATOR_VERILATED_H_
-#define VERILATOR_VERILATED_H_
-#define VERILATOR_VERILATED_H_INTERNAL_
-#ifdef VERILATOR_INTERNAL_
-// This file contains definition of VerilationMutex that should
-// only be used by verilated code. Verilator itself should use
-// mutex from V3Mutex.h. Make sure this file isn't included in
-// verilator code.
-#error "verilated.h should only be included in verilated code"
-#endif
-
-// clang-format off
-#include "verilated_config.h"
-#include "verilatedos.h"
-#if VM_SC
-# include "verilated_sc.h"  // Get SYSTEMC_VERSION and time declarations
-#endif
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-//  avoided to reduce compile time
-#include 
-#include 
-#include 
-
-// Allow user to specify their own include file
-#ifdef VL_VERILATED_INCLUDE
-// cppcheck-suppress preprocessorErrorDirective
-# include VL_VERILATED_INCLUDE
-#endif
-// clang-format on
-
-using namespace std::literals;  // ""s; see SF.7 core guideline
-
-//=============================================================================
-// Switches
-
-// clang-format off
-#if VM_TRACE  // Verilator tracing requested
-# define WAVES 1  // Set backward compatibility flag
-#endif
-
-// Version check
-#if defined(SYSTEMC_VERSION) && (SYSTEMC_VERSION < 20111121)
-# warning "Verilator requires SystemC 2.3.* or newer."
-#endif
-// clang-format on
-
-class VerilatedContext;
-class VerilatedContextImp;
-class VerilatedContextImpData;
-class VerilatedCovContext;
-class VerilatedEvalMsgQueue;
-class VerilatedFst;
-class VerilatedFstC;
-class VerilatedFstSc;
-class VerilatedScope;
-class VerilatedScopeNameMap;
-template 
-class VerilatedTrace;
-class VerilatedTraceBaseC;
-class VerilatedTraceConfig;
-class VerilatedVar;
-class VerilatedVarNameMap;
-class VerilatedVcd;
-class VerilatedVcdC;
-class VerilatedVcdSc;
-
-//=========================================================================
-// Basic types
-
-// Type letters
-// clang-format off
-//    P                     // Packed data of bit type (C/S/I/Q/W)
-using CData = uint8_t;    ///< Data representing 'bit' of 1-8 packed bits
-using SData = uint16_t;   ///< Data representing 'bit' of 9-16 packed bits
-using IData = uint32_t;   ///< Data representing 'bit' of 17-32 packed bits
-using QData = uint64_t;   ///< Data representing 'bit' of 33-64 packed bits
-using EData = uint32_t;   ///< Data representing one element of WData array
-using WData = EData;        ///< Data representing >64 packed bits (used as pointer)
-//    F     = float;        // No typedef needed; Verilator uses float
-//    D     = double;       // No typedef needed; Verilator uses double
-//    N     = std::string;  // No typedef needed; Verilator uses string
-//    U     = VlUnpacked;
-//    R     = VlQueue;
-// clang-format on
-
-using WDataInP = const WData*;  ///< 'bit' of >64 packed bits as array input to a function
-using WDataOutP = WData*;  ///< 'bit' of >64 packed bits as array output from a function
-
-enum VerilatedVarType : uint8_t {
-    VLVT_UNKNOWN = 0,
-    VLVT_PTR,  // Pointer to something
-    VLVT_UINT8,  // AKA CData
-    VLVT_UINT16,  // AKA SData
-    VLVT_UINT32,  // AKA IData
-    VLVT_UINT64,  // AKA QData
-    VLVT_WDATA,  // AKA WData
-    VLVT_STRING,  // C++ string
-    VLVT_REAL  // AKA double
-};
-
-enum VerilatedVarFlags {
-    VLVD_0 = 0,  // None
-    VLVD_IN = 1,  // == vpiInput
-    VLVD_OUT = 2,  // == vpiOutput
-    VLVD_INOUT = 3,  // == vpiInOut
-    VLVD_NODIR = 5,  // == vpiNoDirection
-    VLVF_MASK_DIR = 7,  // Bit mask for above directions
-    // Flags
-    VLVF_PUB_RD = (1 << 8),  // Public readable
-    VLVF_PUB_RW = (1 << 9),  // Public writable
-    VLVF_DPI_CLAY = (1 << 10),  // DPI compatible C standard layout
-    VLVF_CONTINUOUSLY = (1 << 11),  // Is continously assigned
-    VLVF_FORCEABLE = (1 << 12),  // Forceable
-    VLVF_SIGNED = (1 << 13),  // Signed integer
-    VLVF_BITVAR = (1 << 14)  // Four state bit (vs two state logic)
-};
-
-// IEEE 1800-2023 Table 20-6
-enum class VerilatedAssertType : uint8_t {
-    ASSERT_TYPE_CONCURRENT = (1 << 0),
-    ASSERT_TYPE_SIMPLE_IMMEDIATE = (1 << 1),
-    ASSERT_TYPE_OBSERVED_DEFERRED_IMMEDIATE = (1 << 2),
-    ASSERT_TYPE_FINAL_DEFERRED_IMMEDIATE = (1 << 3),
-    ASSERT_TYPE_EXPECT = (1 << 4),
-    ASSERT_TYPE_UNIQUE = (1 << 5),
-    ASSERT_TYPE_UNIQUE0 = (1 << 6),
-    ASSERT_TYPE_PRIORITY = (1 << 7),
-};
-
-// IEEE 1800-2023 Table 20-7
-enum class VerilatedAssertDirectiveType : uint8_t {
-    DIRECTIVE_TYPE_ASSERT = (1 << 0),
-    DIRECTIVE_TYPE_COVER = (1 << 1),
-    DIRECTIVE_TYPE_ASSUME = (1 << 2),
-};
-using VerilatedAssertType_t = std::underlying_type::type;
-using VerilatedAssertDirectiveType_t = std::underlying_type::type;
-
-// Type trait: whether T is a user-defined custom struct
-template 
-struct VlIsCustomStruct : public std::false_type {};
-
-// Type trait: used to detect if array element is a custom struct (e.g. for struct arrays)
-template 
-struct VlContainsCustomStruct : VlIsCustomStruct {};
-
-//=========================================================================
-// Mutex and threading support
-
-// Return current thread ID (or 0), not super fast, cache if needed
-extern uint32_t VL_THREAD_ID() VL_MT_SAFE;
-
-#ifndef VL_LOCK_SPINS
-#define VL_LOCK_SPINS 50000  /// Number of times to spin for a mutex before yielding
-#endif
-
-/// Mutex, wrapped to allow -fthread_safety checks
-class VL_CAPABILITY("mutex") VerilatedMutex final {
-private:
-    std::mutex m_mutex;  // Mutex
-
-public:
-    /// Construct mutex (without locking it)
-    VerilatedMutex() = default;
-    ~VerilatedMutex() = default;
-    VL_UNCOPYABLE(VerilatedMutex);
-    const VerilatedMutex& operator!() const { return *this; }  // For -fthread_safety
-    /// Acquire/lock mutex
-    void lock() VL_ACQUIRE() VL_MT_SAFE {
-        // Try to acquire the lock by spinning.  If the wait is short,
-        // avoids a trap to the OS plus OS scheduler overhead.
-        if (VL_LIKELY(try_lock())) return;  // Short circuit loop
-        for (int i = 0; i < VL_LOCK_SPINS; ++i) {
-            if (VL_LIKELY(try_lock())) return;
-            VL_CPU_RELAX();
-        }
-        // Spinning hasn't worked, pay the cost of blocking.
-        m_mutex.lock();
-    }
-    /// Release/unlock mutex
-    void unlock() VL_RELEASE() VL_MT_SAFE { m_mutex.unlock(); }
-    /// Try to acquire mutex.  Returns true on success, and false on failure.
-    bool try_lock() VL_TRY_ACQUIRE(true) VL_MT_SAFE { return m_mutex.try_lock(); }
-};
-
-/// Lock guard for mutex (ala std::unique_lock), wrapped to allow -fthread_safety checks
-class VL_SCOPED_CAPABILITY VerilatedLockGuard final {
-    VL_UNCOPYABLE(VerilatedLockGuard);
-
-private:
-    VerilatedMutex& m_mutexr;
-
-public:
-    /// Construct and hold given mutex lock until destruction or unlock()
-    explicit VerilatedLockGuard(VerilatedMutex& mutexr) VL_ACQUIRE(mutexr) VL_MT_SAFE
-        : m_mutexr(mutexr) {  // Need () or GCC 4.8 false warning
-        mutexr.lock();
-    }
-    /// Destruct and unlock the mutex
-    ~VerilatedLockGuard() VL_RELEASE() { m_mutexr.unlock(); }
-};
-
-// Internals: Remember the calling thread at construction time, and make
-// sure later calls use same thread
-
-class VerilatedAssertOneThread final {
-    // MEMBERS
-#ifdef VL_DEBUG
-    uint32_t m_threadid;  // Thread that is legal
-public:
-    // CONSTRUCTORS
-    // The constructor establishes the thread id for all later calls.
-    // If necessary, a different class could be made that inits it otherwise.
-    VerilatedAssertOneThread()
-        : m_threadid{VL_THREAD_ID()} {}
-    ~VerilatedAssertOneThread() { check(); }
-    // METHODS
-    // Check that the current thread ID is the same as the construction thread ID
-    void check() VL_MT_UNSAFE_ONE {
-        if (VL_UNCOVERABLE(m_threadid != VL_THREAD_ID())) {
-            if (m_threadid == 0) {
-                m_threadid = VL_THREAD_ID();
-            } else {
-                fatal_different();  // LCOV_EXCL_LINE
-            }
-        }
-    }
-    static void fatal_different() VL_MT_SAFE;
-#else  // !VL_DEBUG
-public:
-    void check() {}
-#endif
-};
-
-//=========================================================================
-/// Base class of a Verilator generated (Verilated) model.
-///
-/// VerilatedModel is a base class of the user facing primary class generated
-/// by Verilator.
-
-class VerilatedModel VL_NOT_FINAL {
-    VL_UNCOPYABLE(VerilatedModel);
-
-    VerilatedContext& m_context;  // The VerilatedContext this model is instantiated under
-
-protected:
-    explicit VerilatedModel(VerilatedContext& context);
-    virtual ~VerilatedModel() = default;
-
-public:
-    /// Returns the VerilatedContext this model is instantiated under
-    /// Used to get to e.g. simulation time via contextp()->time()
-    VerilatedContext* contextp() const VL_MT_SAFE { return &m_context; }
-    /// Returns the hierarchical name of this module instance.
-    virtual const char* hierName() const = 0;
-    /// Returns the name of this model (the name of the generated model class).
-    virtual const char* modelName() const = 0;
-    /// Returns the thread level parallelism, this model was Verilated with. Always 1 or higher.
-    virtual unsigned threads() const = 0;
-
-private:
-    // The following are for use by Verilator internals only
-    template 
-    friend class VerilatedTrace;
-    // Run-time trace configuration requested by this model
-    virtual std::unique_ptr traceConfig() const;
-};
-
-//=========================================================================
-// Functions overridable by user defines
-// (Internals however must use VL_PRINTF_MT, which calls these.)
-
-// clang-format off
-#ifndef VL_PRINTF
-# define VL_PRINTF printf  ///< Print ala printf, called from main thread; redefine if desired
-#endif
-#ifndef VL_VPRINTF
-# define VL_VPRINTF vprintf  ///< Print ala vprintf, called from main thread; redefine if desired
-#endif
-// clang-format on
-
-//===========================================================================
-// Internal: Base class to allow virtual destruction
-
-class VerilatedVirtualBase VL_NOT_FINAL {
-public:
-    VerilatedVirtualBase() = default;
-    virtual ~VerilatedVirtualBase() = default;
-};
-
-//===========================================================================
-/// Verilator simulation context
-///
-/// The VerilatedContext contains the information common across all models
-/// that are interconnected, for example this contains the simulation time
-/// and if $finish was executed.
-///
-/// VerilatedContexts maybe created by the user wrapper code and passed
-/// when a model is created.  If this is not done, then Verilator will use
-/// the Verilated::defaultContextp()'s global context.
-
-class VerilatedContext VL_NOT_FINAL {
-    friend class VerilatedContextImp;
-
-private:
-    // MEMBERS
-    // Numer of assertion directive type members. Then each of them will represented as 1-bit in a
-    // mask.
-    static constexpr size_t ASSERT_DIRECTIVE_TYPE_MASK_WIDTH = 3;
-    // Specifies how many groups of directive type bit groups there are based on a number of
-    // assertion types.
-    // Note: we add one bit to store information whether Verilator's internal
-    // directive types are enabled, for example `violation if`s.
-    static constexpr size_t ASSERT_ON_WIDTH
-        = ASSERT_DIRECTIVE_TYPE_MASK_WIDTH * std::numeric_limits::digits
-          + 1;
-
-protected:
-    // TYPES
-    using traceBaseModelCb_t
-        = std::function;  // Type of traceBaseModel callbacks
-
-    // MEMBERS
-    // Slow path variables
-    mutable VerilatedMutex m_mutex;  // Mutex for most s_s/s_ns members
-
-    struct Serialized final {  // All these members serialized/deserialized
-        // No std::strings or pointers or will serialize badly!
-        // Fast path
-        uint64_t m_time = 0;  // Current $time (unscaled), 0=at zero, or legacy
-        std::atomic m_assertOn{
-            std::numeric_limits::max()};  // Enabled assertions,
-                                                    // for each VerilatedAssertType we store
-                                                    // 3-bits, one for each directive type. Last
-                                                    // bit guards internal directive types.
-        bool m_calcUnusedSigs = false;  // Waves file on, need all signals calculated
-        bool m_fatalOnError = true;  // Fatal on $stop/non-fatal error
-        bool m_fatalOnVpiError = true;  // Fatal on vpi error/unsupported
-        bool m_gotError = false;  // A $finish statement executed
-        bool m_gotFinish = false;  // A $finish or $stop statement executed
-        bool m_quiet = false;  // Quiet, no summary report
-        // Slow path
-        int8_t m_timeunit;  // Time unit as 0..15
-        int8_t m_timeprecision;  // Time precision as 0..15
-        int m_errorCount = 0;  // Number of errors
-        int m_errorLimit = 1;  // Stop on error number
-        int m_randReset = 0;  // Random reset: 0=all 0s, 1=all 1s, 2=random
-        int m_randSeed = 0;  // Random seed: 0=random
-        enum { UNITS_NONE = 99 };  // Default based on precision
-        int m_timeFormatUnits = UNITS_NONE;  // $timeformat units
-        int m_timeFormatPrecision = 0;  // $timeformat number of decimal places
-        int m_timeFormatWidth = 20;  // $timeformat character width
-        // CONSTRUCTORS
-        Serialized();
-        ~Serialized() = default;
-    } m_s;
-
-    mutable VerilatedMutex m_timeDumpMutex;  // Protect misc slow strings
-    std::string m_timeFormatSuffix VL_GUARDED_BY(m_timeDumpMutex);  // $timeformat printf format
-    std::string m_dumpfile VL_GUARDED_BY(m_timeDumpMutex);  // $dumpfile setting
-
-    struct NonSerialized final {  // Non-serialized information
-        // These are reloaded from on command-line settings, so do not need to persist
-        // Fast path
-        uint64_t m_profExecStart = 1;  // +prof+exec+start time
-        uint32_t m_profExecWindow = 2;  // +prof+exec+window size
-        // Slow path
-        std::string m_coverageFilename;  // +coverage+file filename
-        std::string m_profExecFilename;  // +prof+exec+file filename
-        std::string m_profVltFilename;  // +prof+vlt filename
-        std::string m_solverProgram;  // SMT solver program
-        bool m_warnUnsatConstr = true;  // Warn on unsatisfied constraints
-        VlOs::DeltaCpuTime m_cpuTimeStart{false};  // CPU time, starts when create first model
-        VlOs::DeltaWallTime m_wallTimeStart{false};  // Wall time, starts when create first model
-        std::vector m_traceBaseModelCbs;  // Callbacks to traceRegisterModel
-    } m_ns;
-
-    mutable VerilatedMutex m_argMutex;  // Protect m_argVec, m_argVecLoaded
-    // no need to be save-restored (serialized) the
-    // assumption is that the restore is allowed to pass different arguments
-    struct NonSerializedCommandArgs final {
-        // Medium speed
-        std::vector m_argVec;  // Argument list
-        bool m_argVecLoaded = false;  // Ever loaded argument list
-    } m_args VL_GUARDED_BY(m_argMutex);
-
-    // Implementation details
-    const std::unique_ptr m_impdatap;
-    // Number of threads to use for simulation (size of m_threadPool + 1 for main thread)
-    unsigned m_threads = VlOs::getProcessDefaultParallelism();
-    // Use numa automatic CPU-to-thread assignment
-    bool m_useNumaAssign = false;
-    // Number of threads in added models
-    unsigned m_threadsInModels = 0;
-    // The thread pool shared by all models added to this context
-    std::unique_ptr m_threadPool;
-    // The execution profiler shared by all models added to this context
-    std::unique_ptr m_executionProfiler;
-    // Coverage access
-    std::unique_ptr m_coveragep;  // Pointer for coveragep()
-
-    // File I/O
-    // Not serialized
-    mutable VerilatedMutex m_fdMutex;  // Protect m_fdps, m_fdFree
-    std::vector m_fdps VL_GUARDED_BY(m_fdMutex);  // File descriptors
-    // List of free descriptors (SLOW - FOPEN/CLOSE only)
-    std::vector m_fdFree VL_GUARDED_BY(m_fdMutex);
-    // List of free descriptors in the MCT region [4, 32)
-    std::vector m_fdFreeMct VL_GUARDED_BY(m_fdMutex);
-
-    // Magic to check for bad construction
-    static constexpr uint64_t MAGIC = 0xC35F9A6E5298EE6EULL;  // SHA256 "VerilatedContext"
-    uint64_t m_magic = MAGIC;
-
-private:
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedContext);
-
-public:
-    /// Construct context. Also sets Verilated::threadContextp to the created context.
-    VerilatedContext();
-    ~VerilatedContext();
-
-    // METHODS - User called
-
-    /// Return if assertions enabled
-    bool assertOn() const VL_MT_SAFE;
-    /// Enable all assertion types
-    void assertOn(bool flag) VL_MT_SAFE;
-    /// Get enabled status for given assertion types
-    bool assertOnGet(VerilatedAssertType_t type,
-                     VerilatedAssertDirectiveType_t directive) const VL_MT_SAFE;
-    /// Set enabled status for given assertion types
-    void assertOnSet(VerilatedAssertType_t types,
-                     VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
-    /// Clear enabled status for given assertion types
-    void assertOnClear(VerilatedAssertType_t types,
-                       VerilatedAssertDirectiveType_t directives) VL_MT_SAFE;
-    /// Return if calculating of unused signals (for traces)
-    bool calcUnusedSigs() const VL_MT_SAFE { return m_s.m_calcUnusedSigs; }
-    /// Enable calculation of unused signals (for traces)
-    void calcUnusedSigs(bool flag) VL_MT_SAFE;
-    /// Record command-line arguments, for retrieval by $test$plusargs/$value$plusargs,
-    /// and for parsing +verilator+ run-time arguments.
-    /// This should be called before the first model is created.
-    void commandArgs(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex);
-    void commandArgs(int argc, char** argv) VL_MT_SAFE {
-        commandArgs(argc, const_cast(argv));
-    }
-    /// Add a command-line argument to existing arguments
-    void commandArgsAdd(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex);
-    /// Match plusargs with a given prefix. Returns static char* valid only for a single call
-    const char* commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex);
-    /// Return VerilatedCovContext, allocate if needed
-    /// Note if get unresolved reference then likely forgot to link verilated_cov.cpp
-    VerilatedCovContext* coveragep() VL_MT_SAFE;
-    /// Return debug level
-    static inline int debug() VL_MT_SAFE;  /// Set debug level
-    /// Debug is currently global, but for forward compatibility have a per-context method
-    static inline void debug(int val) VL_MT_SAFE;
-    /// Return current number of errors/assertions
-    int errorCount() const VL_MT_SAFE { return m_s.m_errorCount; }
-    /// Set current number of errors/assertions
-    void errorCount(int val) VL_MT_SAFE;
-    /// Increment current number of errors/assertions
-    void errorCountInc() VL_MT_SAFE;
-    /// Return number of errors/assertions before stop
-    int errorLimit() const VL_MT_SAFE { return m_s.m_errorLimit; }
-    /// Set number of errors/assertions before stop
-    void errorLimit(int val) VL_MT_SAFE;
-    /// Return if to throw fatal error on $stop/non-fatal
-    bool fatalOnError() const VL_MT_SAFE { return m_s.m_fatalOnError; }
-    /// Set to throw fatal error on $stop/non-fatal error
-    void fatalOnError(bool flag) VL_MT_SAFE;
-    /// Return if to throw fatal error on VPI errors
-    bool fatalOnVpiError() const VL_MT_SAFE { return m_s.m_fatalOnVpiError; }
-    /// Set to throw fatal error on VPI errors
-    void fatalOnVpiError(bool flag) VL_MT_SAFE;
-    /// Return if got a $stop or non-fatal error
-    bool gotError() const VL_MT_SAFE { return m_s.m_gotError; }
-    /// Set if got a $stop or non-fatal error
-    void gotError(bool flag) VL_MT_SAFE;
-    /// Return if got a $finish or $stop/error
-    bool gotFinish() const VL_MT_SAFE { return m_s.m_gotFinish; }
-    /// Set if got a $finish or $stop/error
-    void gotFinish(bool flag) VL_MT_SAFE;
-    /// Return if quiet enabled
-    bool quiet() const VL_MT_SAFE { return m_s.m_quiet; }
-    /// Enable quiet (also prevents need for OS calls to get CPU time)
-    void quiet(bool flag) VL_MT_SAFE;
-    /// Return randReset value
-    int randReset() VL_MT_SAFE { return m_s.m_randReset; }
-    /// Select initial value of otherwise uninitialized signals.
-    /// 0 = Set to zeros
-    /// 1 = Set all bits to one
-    /// 2 = Randomize all bits
-    void randReset(int val) VL_MT_SAFE;
-    /// Return default random seed
-    int randSeed() const VL_MT_SAFE { return m_s.m_randSeed; }
-    /// Set default random seed, 0 = seed it automatically
-    void randSeed(int val) VL_MT_SAFE;
-
-    /// Return statistic: CPU time delta from model created until now
-    double statCpuTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex);
-    /// Return statistic: Wall time delta from model created until now
-    double statWallTimeSinceStart() const VL_MT_SAFE_EXCLUDES(m_mutex);
-    /// Print statistics summary (if not quiet)
-    void statsPrintSummary() VL_MT_UNSAFE;
-
-    // Time handling
-    /// Returns current simulation time in units of timeprecision().
-    ///
-    /// How Verilator runtime gets the current simulation time:
-    ///
-    /// * If using SystemC, time comes from the SystemC kernel-defined
-    /// sc_time_stamp64(). User's wrapper must not call
-    /// SimulationContext::time(value) nor timeInc(value).
-    ///
-    /// * Else, if SimulationContext::time(value) or
-    /// SimulationContext::timeInc(value) is ever called with non-zero,
-    /// then time will come via the context.  This allows multiple contexts
-    /// to exist and have different simulation times. This must not be used
-    /// with SystemC.  Note Verilated::time(value) and
-    /// Verilated::timeInc(value) call into SimulationContext::time and
-    /// timeInc, operating on the thread's context.
-    ///
-    /// * Else, if VL_TIME_STAMP64 is defined, time comes from the legacy
-    /// 'uint64_t vl_time_stamp64()' which must a function be defined by
-    /// the user's wrapper.
-    ///
-    /// * Else, time comes from the legacy 'double sc_time_stamp()' which
-    /// must be a function defined by the user's wrapper.
-    inline uint64_t time() const VL_MT_SAFE;
-    /// Set current simulation time. See time() for side effect details
-    void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; }
-    /// Advance current simulation time. See time() for side effect details
-    void timeInc(uint64_t add) VL_MT_UNSAFE { m_s.m_time += add; }
-    /// Return time units as power-of-ten
-    int timeunit() const VL_MT_SAFE { return -m_s.m_timeunit; }
-    /// Set time units as power-of-ten
-    void timeunit(int value) VL_MT_SAFE;
-    /// Return time units as IEEE-standard text
-    const char* timeunitString() const VL_MT_SAFE;
-    /// Get time precision as power-of-ten
-    int timeprecision() const VL_MT_SAFE { return -m_s.m_timeprecision; }
-    /// Return time precision as power-of-ten
-    inline void timeprecision(int value) VL_MT_SAFE;
-    /// Get time precision as IEEE-standard text
-    const char* timeprecisionString() const VL_MT_SAFE;
-
-    /// Get number of threads used for simulation (including the main thread)
-    unsigned threads() const { return m_threads; }
-    /// Get number of threads in added models (for statistical use only)
-    unsigned threadsInModels() const { return m_threadsInModels; }
-    /// Set number of threads used for simulation (including the main thread)
-    /// Can only be called before the thread pool is created (before first model is added).
-    void threads(unsigned n);
-
-    /// Use numa automatic CPU-to-thread assignment.
-    bool useNumaAssign() const VL_MT_SAFE { return m_useNumaAssign; }
-    /// Set numa assignment of threads to cores
-    /// Defaults false; set true automatically when threads() called;
-    /// call this to override back to false if numa assignment not wanted.
-    void useNumaAssign(bool flag);
-
-    /// Trace signals in models within the context; called by application code
-    void trace(VerilatedTraceBaseC* tfp, int levels, int options = 0);
-    /// Allow traces to at some point be enabled (disables some optimizations)
-    void traceEverOn(bool flag) VL_MT_SAFE {
-        if (flag) calcUnusedSigs(true);
-    }
-
-    /// For debugging, print much of the Verilator internal state.
-    /// The output of this function may change in future
-    /// releases - contact the authors before production use.
-    void internalsDump() const VL_MT_SAFE;
-
-    /// For debugging, print text list of all scope names with
-    /// dpiImport/Export context.  This function may change in future
-    /// releases - contact the authors before production use.
-    void scopesDump() const VL_MT_SAFE;
-
-    // METHODS - public but for internal use only
-
-    // Internal: access to implementation class
-    VerilatedContextImp* impp() VL_MT_SAFE { return reinterpret_cast(this); }
-    const VerilatedContextImp* impp() const VL_MT_SAFE {
-        return reinterpret_cast(this);
-    }
-
-    // Internal: Model and thread setup
-    void addModel(const VerilatedModel* modelp);
-    VerilatedVirtualBase* threadPoolp();
-    void prepareClone();
-    VerilatedVirtualBase* threadPoolpOnClone();
-    VerilatedVirtualBase*
-    enableExecutionProfiler(VerilatedVirtualBase* (*construct)(VerilatedContext&));
-
-    // Internal: coverage
-    std::string coverageFilename() const VL_MT_SAFE;
-    void coverageFilename(const std::string& flag) VL_MT_SAFE;
-
-    // Internal: $dumpfile
-    std::string dumpfile() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
-    void dumpfile(const std::string& flag) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
-    std::string dumpfileCheck() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex);
-
-    // Internal: --prof-exec related settings
-    uint64_t profExecStart() const VL_MT_SAFE { return m_ns.m_profExecStart; }
-    void profExecStart(uint64_t flag) VL_MT_SAFE;
-    uint32_t profExecWindow() const VL_MT_SAFE { return m_ns.m_profExecWindow; }
-    void profExecWindow(uint64_t flag) VL_MT_SAFE;
-    std::string profExecFilename() const VL_MT_SAFE;
-    void profExecFilename(const std::string& flag) VL_MT_SAFE;
-    std::string profVltFilename() const VL_MT_SAFE;
-    void profVltFilename(const std::string& flag) VL_MT_SAFE;
-
-    // Internal: SMT solver program
-    std::string solverProgram() const VL_MT_SAFE;
-    void solverProgram(const std::string& flag) VL_MT_SAFE;
-    // Internal: Control display of unsatisfied constraints
-    bool warnUnsatConstr() const VL_MT_SAFE { return m_ns.m_warnUnsatConstr; }
-    void warnUnsatConstr(bool flag) VL_MT_SAFE { m_ns.m_warnUnsatConstr = flag; }
-
-    // Internal: Find scope
-    const VerilatedScope* scopeFind(const char* namep) const VL_MT_SAFE;
-    const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE;
-
-    // Internal: Serialization setup
-    static constexpr size_t serialized1Size() VL_PURE { return sizeof(m_s); }
-    void* serialized1Ptr() VL_MT_UNSAFE { return &m_s; }
-
-    // Internal: trace registration
-    void traceBaseModelCbAdd(traceBaseModelCb_t cb) VL_MT_SAFE;
-
-    // Internal: Check magic number
-    static void checkMagic(const VerilatedContext* contextp);
-    void selfTestClearMagic() { m_magic = 0x2; }
-};
-
-//===========================================================================
-// Verilator symbol table base class
-// Used for internal VPI implementation, and introspection into scopes
-
-class VerilatedSyms VL_NOT_FINAL {
-public:  // But for internal use only
-    // MEMBERS
-    // Keep first so is at zero offset for fastest code
-    VerilatedContext* const _vm_contextp__;  // Context for current model
-    VerilatedEvalMsgQueue* __Vm_evalMsgQp;
-    explicit VerilatedSyms(VerilatedContext* contextp);  // Pass null for default context
-    ~VerilatedSyms();
-    VL_UNCOPYABLE(VerilatedSyms);
-
-    virtual const char* name() const = 0;
-};
-
-//===========================================================================
-// Verilator scope information class
-// Used for internal VPI implementation, and introspection into scopes
-
-class VerilatedScope final {
-public:
-    enum Type : uint8_t {
-        SCOPE_MODULE,
-        SCOPE_OTHER,
-        SCOPE_PACKAGE
-    };  // Type of a scope, currently only module and package are interesting
-private:
-    // Fastpath:
-    VerilatedSyms* const m_symsp;  // Symbol table
-    void** m_callbacksp = nullptr;  // Callback table pointer (Fastpath)
-    int m_funcnumMax = 0;  // Maximum function number stored (Fastpath)
-    // 4 bytes padding (on -m64), for rent.
-    VerilatedVarNameMap* m_varsp = nullptr;  // Variable map
-    const char* const m_namep;  // Scope name (Slowpath)
-    const char* const m_identifierp;  // Identifier of scope (with escapes removed)
-    const char* const m_defnamep;  // Definition name (SCOPE_MODULE only)
-    const int8_t m_timeunit;  // Timeunit in negative power-of-10
-    const Type m_type;  // Type of the scope
-
-public:  // But internals only - called from verilated modules, VerilatedSyms
-    VerilatedScope(VerilatedSyms* symsp, const char* suffixp, const char* identifier,
-                   const char* defnamep, int8_t timeunit, Type type);
-    ~VerilatedScope();
-
-    void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE;
-    void varInsert(const char* namep, void* datap, bool isParam, VerilatedVarType vltype,
-                   int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE;
-    // ACCESSORS
-    const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; }
-    const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; }
-    const char* defname() const VL_MT_SAFE_POSTINIT { return m_defnamep; }
-    int8_t timeunit() const VL_MT_SAFE_POSTINIT { return m_timeunit; }
-    VerilatedSyms* symsp() const VL_MT_SAFE_POSTINIT { return m_symsp; }
-    VerilatedVar* varFind(const char* namep) const VL_MT_SAFE_POSTINIT;
-    VerilatedVarNameMap* varsp() const VL_MT_SAFE_POSTINIT { return m_varsp; }
-    void scopeDump() const;
-    void* exportFindError(int funcnum) const VL_MT_SAFE;
-    static void* exportFindNullError(int funcnum) VL_MT_SAFE;
-    static void* exportFind(const VerilatedScope* scopep, int funcnum) VL_MT_SAFE;
-    Type type() const { return m_type; }
-};
-
-class VerilatedHierarchy final {
-public:
-    static void add(const VerilatedScope* fromp, const VerilatedScope* top);
-    static void remove(const VerilatedScope* fromp, const VerilatedScope* top);
-    static void clear();
-};
-
-//===========================================================================
-/// Verilator global static information class
-
-class Verilated final {
-    // MEMBERS
-
-    // Internal Note: There should be no Serialized state in Verilated::,
-    // instead serialized state should all be in VerilatedContext:: as by
-    // definition it needs to vary per-simulation
-
-    // Internal note: Globals may multi-construct, see verilated.cpp top.
-
-    // Debug is reloaded from on command-line settings, so do not need to persist
-    static int s_debug;  // See accessors... only when VL_DEBUG set
-
-    static VerilatedContext* s_lastContextp;  // Last context constructed/attached
-
-    // Not covered by mutex, as per-thread
-    static thread_local struct ThreadLocal {
-        // No non-POD objects here due to this:
-        // Internal note: Globals may multi-construct, see verilated.cpp top.
-
-        // Fast path
-        VerilatedContext* t_contextp = nullptr;  // Thread's context
-        uint32_t t_mtaskId = 0;  // mtask# executing on this thread
-        // Messages maybe pending on thread, needs end-of-eval calls
-        uint32_t t_endOfEvalReqd = 0;
-        const VerilatedScope* t_dpiScopep = nullptr;  // DPI context scope
-        const char* t_dpiFilename = nullptr;  // DPI context filename
-        int t_dpiLineno = 0;  // DPI context line number
-
-        ThreadLocal() = default;
-        ~ThreadLocal() = default;
-    } t_s;
-
-    friend struct VerilatedInitializer;
-
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(Verilated);
-
-public:
-    // METHODS - User called
-
-#ifdef VL_DEBUG
-    /// Return debug level
-    /// When multithreaded this may not immediately react to another thread
-    /// changing the level (no mutex)
-    static int debug() VL_MT_SAFE { return s_debug; }
-#else
-    /// Return constant 0 debug level, so C++'s optimizer rips up
-    static constexpr int debug() VL_PURE { return 0; }
-#endif
-    /// Enable debug of internal verilated code
-    static void debug(int level) VL_MT_SAFE;
-
-    /// Set the last VerilatedContext accessed
-    /// Generally threadContextp(value) should be called instead
-    static void lastContextp(VerilatedContext* contextp) VL_MT_SAFE { s_lastContextp = contextp; }
-    /// Return the last VerilatedContext accessed
-    /// Generally threadContextp() should be called instead
-    static VerilatedContext* lastContextp() VL_MT_SAFE {
-        if (!s_lastContextp) lastContextp(defaultContextp());
-        return s_lastContextp;
-    }
-    /// Set the VerilatedContext used by the current thread
-
-    /// If using multiple contexts, and threads are created by the user's
-    /// wrapper (not Verilator itself) then this must be called to set the
-    /// context that applies to each thread
-    static void threadContextp(VerilatedContext* contextp) VL_MT_SAFE {
-        t_s.t_contextp = contextp;
-        lastContextp(contextp);
-    }
-    /// Return the VerilatedContext for the current thread
-    static VerilatedContext* threadContextp() VL_MT_SAFE {
-        if (VL_UNLIKELY(!t_s.t_contextp)) t_s.t_contextp = lastContextp();
-        return t_s.t_contextp;
-    }
-    /// Return the global VerilatedContext, used if none created by user
-    static VerilatedContext* defaultContextp() VL_MT_SAFE {
-        static VerilatedContext s_s;
-        return &s_s;
-    }
-
-#ifndef VL_NO_LEGACY
-    /// Return VerilatedContext::assertOn() using current thread's VerilatedContext
-    static bool assertOn() VL_MT_SAFE { return Verilated::threadContextp()->assertOn(); }
-    /// Call VerilatedContext::assertOn using current thread's VerilatedContext
-    static void assertOn(bool flag) VL_MT_SAFE { Verilated::threadContextp()->assertOn(flag); }
-    /// Return VerilatedContext::calcUnusedSigs using current thread's VerilatedContext
-    static bool calcUnusedSigs() VL_MT_SAFE {
-        return Verilated::threadContextp()->calcUnusedSigs();
-    }
-    /// Call VerilatedContext::calcUnusedSigs using current thread's VerilatedContext
-    static void calcUnusedSigs(bool flag) VL_MT_SAFE {
-        Verilated::threadContextp()->calcUnusedSigs(flag);
-    }
-    /// Call VerilatedContext::commandArgs using current thread's VerilatedContext
-    static void commandArgs(int argc, const char** argv) VL_MT_SAFE {
-        Verilated::threadContextp()->commandArgs(argc, argv);
-    }
-    static void commandArgs(int argc, char** argv) VL_MT_SAFE {
-        commandArgs(argc, const_cast(argv));
-    }
-    /// Call VerilatedContext::commandArgsAdd using current thread's VerilatedContext
-    static void commandArgsAdd(int argc, const char** argv) {
-        Verilated::threadContextp()->commandArgsAdd(argc, argv);
-    }
-    /// Return VerilatedContext::commandArgsPlusMatch using current thread's VerilatedContext
-    static const char* commandArgsPlusMatch(const char* prefixp) VL_MT_SAFE {
-        return Verilated::threadContextp()->commandArgsPlusMatch(prefixp);
-    }
-    /// Return VerilatedContext::errorLimit using current thread's VerilatedContext
-    static int errorLimit() VL_MT_SAFE { return Verilated::threadContextp()->errorLimit(); }
-    /// Call VerilatedContext::errorLimit using current thread's VerilatedContext
-    static void errorLimit(int val) VL_MT_SAFE { Verilated::threadContextp()->errorLimit(val); }
-    /// Return VerilatedContext::fatalOnError using current thread's VerilatedContext
-    static bool fatalOnError() VL_MT_SAFE { return Verilated::threadContextp()->fatalOnError(); }
-    /// Call VerilatedContext::fatalOnError using current thread's VerilatedContext
-    static void fatalOnError(bool flag) VL_MT_SAFE {
-        Verilated::threadContextp()->fatalOnError(flag);
-    }
-    /// Return VerilatedContext::fatalOnVpiError using current thread's VerilatedContext
-    static bool fatalOnVpiError() VL_MT_SAFE {
-        return Verilated::threadContextp()->fatalOnVpiError();
-    }
-    /// Call VerilatedContext::fatalOnVpiError using current thread's VerilatedContext
-    static void fatalOnVpiError(bool flag) VL_MT_SAFE {
-        Verilated::threadContextp()->fatalOnVpiError(flag);
-    }
-    /// Return VerilatedContext::gotError using current thread's VerilatedContext
-    static bool gotError() VL_MT_SAFE { return Verilated::threadContextp()->gotError(); }
-    /// Call VerilatedContext::gotError using current thread's VerilatedContext
-    static void gotError(bool flag) VL_MT_SAFE { Verilated::threadContextp()->gotError(flag); }
-    /// Return VerilatedContext::gotFinish using current thread's VerilatedContext
-    static bool gotFinish() VL_MT_SAFE { return Verilated::threadContextp()->gotFinish(); }
-    /// Call VerilatedContext::gotFinish using current thread's VerilatedContext
-    static void gotFinish(bool flag) VL_MT_SAFE { Verilated::threadContextp()->gotFinish(flag); }
-    /// Return VerilatedContext::randReset using current thread's VerilatedContext
-    static int randReset() VL_MT_SAFE { return Verilated::threadContextp()->randReset(); }
-    /// Call VerilatedContext::randReset using current thread's VerilatedContext
-    static void randReset(int val) VL_MT_SAFE { Verilated::threadContextp()->randReset(val); }
-    /// Return VerilatedContext::randSeed using current thread's VerilatedContext
-    static int randSeed() VL_MT_SAFE { return Verilated::threadContextp()->randSeed(); }
-    /// Call VerilatedContext::randSeed using current thread's VerilatedContext
-    static void randSeed(int val) VL_MT_SAFE { Verilated::threadContextp()->randSeed(val); }
-    /// Return VerilatedContext::time using current thread's VerilatedContext
-    static uint64_t time() VL_MT_SAFE { return Verilated::threadContextp()->time(); }
-    /// Call VerilatedContext::time using current thread's VerilatedContext
-    static void time(uint64_t val) VL_MT_SAFE { Verilated::threadContextp()->time(val); }
-    /// Call VerilatedContext::timeInc using current thread's VerilatedContext
-    static void timeInc(uint64_t add) VL_MT_UNSAFE { Verilated::threadContextp()->timeInc(add); }
-    // Deprecated
-    static int timeunit() VL_MT_SAFE { return Verilated::threadContextp()->timeunit(); }
-    static int timeprecision() VL_MT_SAFE { return Verilated::threadContextp()->timeprecision(); }
-    /// Call VerilatedContext::tracesEverOn using current thread's VerilatedContext
-    static void traceEverOn(bool flag) VL_MT_SAFE {
-        Verilated::threadContextp()->traceEverOn(flag);
-    }
-#endif
-
-    /// Callback typedef for addFlushCb, addExitCb
-    using VoidPCb = void (*)(void*);
-    /// Add callback to run on global flush
-    static void addFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE;
-    /// Remove callback to run on global flush
-    static void removeFlushCb(VoidPCb cb, void* datap) VL_MT_SAFE;
-    /// Run flush callbacks registered with addFlushCb
-    static void runFlushCallbacks() VL_MT_SAFE;
-#ifndef VL_NO_LEGACY
-    static void flushCall() VL_MT_SAFE { runFlushCallbacks(); }  // Deprecated
-#endif
-    /// Add callback to run prior to exit termination
-    static void addExitCb(VoidPCb cb, void* datap) VL_MT_SAFE;
-    /// Remove callback to run prior to exit termination
-    static void removeExitCb(VoidPCb cb, void* datap) VL_MT_SAFE;
-    /// Run exit callbacks registered with addExitCb
-    static void runExitCallbacks() VL_MT_SAFE;
-
-    /// Return product name for (at least) VPI
-    static const char* productName() VL_PURE;
-    /// Return product version for (at least) VPI
-    static const char* productVersion() VL_PURE;
-
-    /// Call OS to make a directory
-    static void mkdir(const char* dirname) VL_MT_UNSAFE;
-
-    /// When multithreaded, quiesce the model to prepare for trace/saves/coverage
-    /// This may only be called when no locks are held.
-    static void quiesce() VL_MT_SAFE;
-
-#ifndef VL_NO_LEGACY
-    /// For debugging, print much of the Verilator internal state.
-    /// The output of this function may change in future
-    /// releases - contact the authors before production use.
-    static void internalsDump() VL_MT_SAFE { Verilated::threadContextp()->internalsDump(); }
-    /// For debugging, print text list of all scope names with
-    /// dpiImport/Export context.  This function may change in future
-    /// releases - contact the authors before production use.
-    static void scopesDump() VL_MT_SAFE { Verilated::threadContextp()->scopesDump(); }
-    // Internal: Find scope
-    static const VerilatedScope* scopeFind(const char* namep) VL_MT_SAFE {
-        return Verilated::threadContextp()->scopeFind(namep);
-    }
-    static const VerilatedScopeNameMap* scopeNameMap() VL_MT_SAFE {
-        return Verilated::threadContextp()->scopeNameMap();
-    }
-#endif
-
-    // METHODS - INTERNAL USE ONLY (but public due to what uses it)
-    // Internal: Create a new module name by concatenating two strings
-    // Returns pointer to thread-local static data (overwritten on next call)
-    static const char* catName(const char* n1, const char* n2,
-                               const char* delimiter = ".") VL_MT_SAFE;
-
-    // Internal: Throw signal assertion
-    static void nullPointerError(const char* filename, int linenum) VL_ATTR_NORETURN VL_MT_SAFE;
-    static void overWidthError(const char* signame) VL_ATTR_NORETURN VL_MT_SAFE;
-    static void scTimePrecisionError(int sc_prec, int vl_prec) VL_ATTR_NORETURN VL_MT_SAFE;
-    static void scTraceBeforeElaborationError() VL_ATTR_NORETURN VL_MT_SAFE;
-    static void stackCheck(QData needSize) VL_MT_UNSAFE;
-
-    // Internal: Get and set DPI context
-    static const VerilatedScope* dpiScope() VL_MT_SAFE { return t_s.t_dpiScopep; }
-    static void dpiScope(const VerilatedScope* scopep) VL_MT_SAFE { t_s.t_dpiScopep = scopep; }
-    static void dpiContext(const VerilatedScope* scopep, const char* filenamep,
-                           int lineno) VL_MT_SAFE {
-        t_s.t_dpiScopep = scopep;
-        t_s.t_dpiFilename = filenamep;
-        t_s.t_dpiLineno = lineno;
-    }
-    static void dpiClearContext() VL_MT_SAFE { t_s.t_dpiScopep = nullptr; }
-    static bool dpiInContext() VL_MT_SAFE { return t_s.t_dpiScopep != nullptr; }
-    static const char* dpiFilenamep() VL_MT_SAFE { return t_s.t_dpiFilename; }
-    static int dpiLineno() VL_MT_SAFE { return t_s.t_dpiLineno; }
-    static int exportFuncNum(const char* namep) VL_MT_SAFE;
-
-    // Internal: Set the mtaskId, called when an mtask starts
-    // Per thread, so no need to be in VerilatedContext
-    static uint32_t mtaskId() VL_MT_SAFE { return t_s.t_mtaskId; }
-    static void mtaskId(uint32_t id) VL_MT_SAFE { t_s.t_mtaskId = id; }
-    static void endOfEvalReqdInc() VL_MT_SAFE { ++t_s.t_endOfEvalReqd; }
-    static void endOfEvalReqdDec() VL_MT_SAFE { --t_s.t_endOfEvalReqd; }
-
-    // Internal: Called at end of each thread mtask, before finishing eval
-    static void endOfThreadMTask(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
-        mtaskId(0);
-        if (VL_UNLIKELY(t_s.t_endOfEvalReqd)) endOfThreadMTaskGuts(evalMsgQp);
-    }
-    // Internal: Called at end of eval loop
-    static void endOfEval(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE;
-
-private:
-    static void endOfThreadMTaskGuts(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE;
-};
-
-void VerilatedContext::debug(int val) VL_MT_SAFE { Verilated::debug(val); }
-int VerilatedContext::debug() VL_MT_SAFE { return Verilated::debug(); }
-
-//=========================================================================
-// Data Types
-
-#include "verilated_types.h"
-
-//=========================================================================
-// Functions
-
-#include "verilated_funcs.h"
-
-//======================================================================
-
-void VerilatedContext::timeprecision(int value) VL_MT_SAFE {
-    if (value < 0) value = -value;  // Stored as 0..15
-#if VM_SC
-    int sc_prec = 99;
-#endif
-    {
-        const VerilatedLockGuard lock{m_mutex};
-        m_s.m_timeprecision = value;
-#if VM_SC
-        const sc_core::sc_time sc_res = sc_core::sc_get_time_resolution();
-        double mult = 1.0;
-        for (int i = 0; i < 16; i++) {
-            if (sc_res == sc_core::sc_time(mult, sc_core::SC_FS)) {
-                sc_prec = 15 - i;
-                break;
-            }
-            mult *= 10.0;
-        }
-        // SC_AS, SC_ZS, SC_YS not supported as no Verilog equivalent; will error below
-#endif
-    }
-#if VM_SC
-    if (VL_UNLIKELY(value != sc_prec)) Verilated::scTimePrecisionError(sc_prec, value);
-#endif
-}
-
-#undef VERILATOR_VERILATED_H_INTERNAL_
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_config.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_config.h
deleted file mode 100644
index 2b14db5a74b..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_config.h
+++ /dev/null
@@ -1,30 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilator program version information header
-///
-//*************************************************************************
-
-/// Verilator product name, e.g. "Verilator"
-// Autoconf substitutes this with the strings from AC_INIT.
-#define VERILATOR_PRODUCT "Verilator"
-
-/// Verilator version name, e.g. "1.002 2000-01-01"
-// Autoconf substitutes this with the strings from AC_INIT.
-#define VERILATOR_VERSION "5.046 2026-02-28"
-
-/// Verilator version number as integer
-/// As major * 100000 + minor * 1000, e.g. 1002000 == 1.002
-// Autoconf substitutes this with the strings from AC_INIT.
-#define VERILATOR_VERSION_INTEGER 5046000
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.cpp
deleted file mode 100644
index 123fa6dbeb7..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.cpp
+++ /dev/null
@@ -1,541 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated coverage analysis implementation code
-///
-/// This file must be compiled and linked against all Verilated objects
-/// that use coverage.
-///
-/// Use "verilator --coverage" to add this to the Makefile for the linker.
-///
-//=============================================================================
-
-#include "verilatedos.h"
-
-#include "verilated_cov.h"
-
-#include "verilated.h"
-#include "verilated_cov_key.h"
-
-#include 
-#include 
-#include 
-#include 
-
-//=============================================================================
-// VerilatedCovConst
-// Implementation constants
-
-struct VerilatedCovConst VL_NOT_FINAL {
-    // TYPES
-    enum { MAX_KEYS = 33 };  // Maximum user arguments + filename+lineno
-    enum { KEY_UNDEF = 0 };  // Magic key # for unspecified values
-};
-
-//=============================================================================
-// VerilatedCovImpItem
-// Implementation class for a VerilatedCov item
-
-class VerilatedCovImpItem VL_NOT_FINAL {
-public:  // But only local to this file
-    // MEMBERS
-    int m_keys[VerilatedCovConst::MAX_KEYS];  // Key
-    int m_vals[VerilatedCovConst::MAX_KEYS];  // Value for specified key
-    // CONSTRUCTORS
-    // Derived classes should call zero() in their constructor
-    VerilatedCovImpItem() {
-        for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
-            m_keys[i] = VerilatedCovConst::KEY_UNDEF;
-            m_vals[i] = 0;
-        }
-    }
-    virtual ~VerilatedCovImpItem() = default;
-    virtual uint64_t count() const = 0;
-    virtual void zero() const = 0;
-};
-
-//=============================================================================
-// VerilatedCoverItem templated for a specific class
-// Creates a new coverage item for the specified type.
-// This isn't in the header file for auto-magic conversion because it
-// inlines to too much code and makes compilation too slow.
-
-template 
-class VerilatedCoverItemSpec final : public VerilatedCovImpItem {
-private:
-    // MEMBERS
-    T* m_countp;  // Count value
-public:
-    // METHODS
-    // cppcheck-suppress truncLongCastReturn
-    uint64_t count() const override { return *m_countp; }
-    void zero() const override { *m_countp = 0; }
-    // CONSTRUCTORS
-    // cppcheck-suppress noExplicitConstructor
-    explicit VerilatedCoverItemSpec(T* countp)
-        : m_countp{countp} {
-        *m_countp = 0;
-    }
-    ~VerilatedCoverItemSpec() override = default;
-};
-
-//=============================================================================
-// VerilatedCovImp
-//
-// Implementation class for VerilatedCovContext.  See that class for
-// public method information.  All value and keys are indexed into a
-// unique number.  Thus we can greatly reduce the storage requirements for
-// otherwise identical keys.
-
-class VerilatedCovImp final : public VerilatedCovContext {
-private:
-    // TYPES
-    using ValueIndexMap = std::map;
-    using IndexValueMap = std::map;
-    using ItemList = std::deque;
-
-    // MEMBERS
-    VerilatedContext* const m_contextp;  // Context VerilatedCovImp is pointed-to by
-    mutable VerilatedMutex m_mutex;  // Protects all members
-    ValueIndexMap m_valueIndexes VL_GUARDED_BY(m_mutex);  // Unique arbitrary value for values
-    IndexValueMap m_indexValues VL_GUARDED_BY(m_mutex);  // Unique arbitrary value for keys
-    ItemList m_items VL_GUARDED_BY(m_mutex);  // List of all items
-    int m_nextIndex VL_GUARDED_BY(m_mutex)
-        = (VerilatedCovConst::KEY_UNDEF + 1);  // Next insert value
-
-    VerilatedCovImpItem* m_insertp VL_GUARDED_BY(m_mutex) = nullptr;  // Item about to insert
-    const char* m_insertFilenamep VL_GUARDED_BY(m_mutex) = nullptr;  // Filename about to insert
-    int m_insertLineno VL_GUARDED_BY(m_mutex) = 0;  // Line number about to insert
-    bool m_forcePerInstance VL_GUARDED_BY(m_mutex) = false;  // Force per_instance
-
-public:
-    // CONSTRUCTORS
-    explicit VerilatedCovImp(VerilatedContext* contextp)
-        : m_contextp{contextp} {}
-    VL_UNCOPYABLE(VerilatedCovImp);
-
-protected:
-    friend class VerilatedCovContext;
-    ~VerilatedCovImp() override { clearGuts(); }
-
-private:
-    // PRIVATE METHODS
-    int valueIndex(const std::string& value) VL_REQUIRES(m_mutex) {
-        const auto iter = m_valueIndexes.find(value);
-        if (iter != m_valueIndexes.end()) return iter->second;
-        ++m_nextIndex;
-        assert(m_nextIndex > 0);  // Didn't rollover
-        m_valueIndexes.emplace(value, m_nextIndex);
-        m_indexValues.emplace(m_nextIndex, value);
-        return m_nextIndex;
-    }
-    static std::string dequote(const std::string& text) VL_PURE {
-        // Quote any special characters
-        std::string rtn;
-        for (const char* pos = text.c_str(); *pos; ++pos) {
-            if (!std::isprint(*pos) || *pos == '%' || *pos == '"') {
-                constexpr size_t LEN_MAX_HEX = 20;
-                char hex[LEN_MAX_HEX];
-                VL_SNPRINTF(hex, LEN_MAX_HEX, "%%%02X", pos[0]);
-                rtn += hex;
-            } else {
-                rtn += *pos;
-            }
-        }
-        return rtn;
-    }
-    static bool legalKey(const std::string& key) VL_PURE {
-        // Because we compress long keys to a single letter, and
-        // don't want applications to either get confused if they use
-        // a letter differently, nor want them to rely on our compression...
-        // (Considered using numeric keys, but will remain back compatible.)
-        if (key.length() < 2) return false;
-        if (key.length() == 2 && std::isdigit(key[1])) return false;
-        return true;
-    }
-    static std::string keyValueFormatter(const std::string& key,
-                                         const std::string& value) VL_PURE {
-        std::string name;
-        if (key.length() == 1 && std::isalpha(key[0])) {
-            name += "\001"s + key;
-        } else {
-            name += "\001"s + dequote(key);
-        }
-        name += "\002"s + dequote(value);
-        return name;
-    }
-    static std::string combineHier(const std::string& old, const std::string& add) VL_PURE {
-        // (foo.a.x, foo.b.x) => foo.*.x
-        // (foo.a.x, foo.b.y) => foo.*
-        // (foo.a.x, foo.b)   => foo.*
-        if (old == add) return add;
-        if (old.empty()) return add;
-        if (add.empty()) return old;
-
-        const char* const a = old.c_str();
-        const char* const b = add.c_str();
-
-        // Scan forward to first mismatch
-        const char* apre = a;
-        const char* bpre = b;
-        while (*apre == *bpre) {
-            ++apre;
-            ++bpre;
-        }
-
-        // We used to backup and split on only .'s but it seems better to be verbose
-        // and not assume . is the separator
-        const size_t prefix_len = apre - a;
-        const std::string prefix = std::string{a, prefix_len};
-
-        // Scan backward to last mismatch
-        const char* apost = a + std::strlen(a) - 1;
-        const char* bpost = b + std::strlen(b) - 1;
-        while (*apost == *bpost && apost > apre && bpost > bpre) {
-            --apost;
-            --bpost;
-        }
-
-        // Forward to . so we have a whole word
-        const std::string suffix = *bpost ? std::string{bpost + 1} : "";
-
-        std::string result = prefix + "*" + suffix;
-
-        // std::cout << "\nch pre=" << prefix << "  s=" << suffix << "\nch a="
-        // << old << "\nch b=" << add << "\ncho=" << result << "\n";
-        return result;
-    }
-    bool itemMatchesString(VerilatedCovImpItem* itemp, const std::string& match)
-        VL_REQUIRES(m_mutex) {
-        for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
-            if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) {
-                // We don't compare keys, only values
-                const std::string val = m_indexValues[itemp->m_vals[i]];
-                if (std::string::npos != val.find(match)) {  // Found
-                    return true;
-                }
-            }
-        }
-        return false;
-    }
-    static void selftest() VL_MT_SAFE {
-        // Little selftest
-#define SELF_CHECK(got, exp) \
-    do { \
-        if ((got) != (exp)) VL_FATAL_MT(__FILE__, __LINE__, "", "selftest"); \
-    } while (0)
-        SELF_CHECK(combineHier("a.b.c", "a.b.c"), "a.b.c");
-        SELF_CHECK(combineHier("a.b.c", "a.b"), "a.b*");
-        SELF_CHECK(combineHier("a.x.c", "a.y.c"), "a.*.c");
-        SELF_CHECK(combineHier("a.z.z.z.c", "a.b.c"), "a.*.c");
-        SELF_CHECK(combineHier("z", "a"), "*");
-        SELF_CHECK(combineHier("q.a", "q.b"), "q.*");
-        SELF_CHECK(combineHier("q.za", "q.zb"), "q.z*");
-        SELF_CHECK(combineHier("1.2.3.a", "9.8.7.a"), "*.a");
-#undef SELF_CHECK
-    }
-    void clearGuts() VL_REQUIRES(m_mutex) {
-        for (const auto& itemp : m_items) VL_DO_DANGLING(delete itemp, itemp);
-        m_items.clear();
-        m_indexValues.clear();
-        m_valueIndexes.clear();
-        m_nextIndex = VerilatedCovConst::KEY_UNDEF + 1;
-    }
-
-public:
-    // PUBLIC METHODS
-    // cppcheck-suppress duplInheritedMember
-    std::string defaultFilename() VL_MT_SAFE { return m_contextp->coverageFilename(); }
-    // cppcheck-suppress duplInheritedMember
-    void forcePerInstance(const bool flag) VL_MT_SAFE_EXCLUDES(m_mutex) {
-        Verilated::quiesce();
-        const VerilatedLockGuard lock{m_mutex};
-        m_forcePerInstance = flag;
-    }
-    // cppcheck-suppress duplInheritedMember
-    void clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
-        Verilated::quiesce();
-        const VerilatedLockGuard lock{m_mutex};
-        clearGuts();
-    }
-    // cppcheck-suppress duplInheritedMember
-    void clearNonMatch(const char* const matchp) VL_MT_SAFE_EXCLUDES(m_mutex) {
-        Verilated::quiesce();
-        const VerilatedLockGuard lock{m_mutex};
-        if (matchp && matchp[0]) {
-            ItemList newlist;
-            for (const auto& itemp : m_items) {
-                if (!itemMatchesString(itemp, matchp)) {
-                    VL_DO_DANGLING(delete itemp, itemp);
-                } else {
-                    newlist.push_back(itemp);
-                }
-            }
-            m_items = newlist;
-        }
-    }
-    // cppcheck-suppress duplInheritedMember
-    void zero() VL_MT_SAFE_EXCLUDES(m_mutex) {
-        Verilated::quiesce();
-        const VerilatedLockGuard lock{m_mutex};
-        for (const VerilatedCovImpItem* const itemp : m_items) itemp->zero();
-    }
-
-    // We assume there's always call to i/f/p in that order
-    void inserti(VerilatedCovImpItem* itemp) VL_MT_SAFE_EXCLUDES(m_mutex) {
-        const VerilatedLockGuard lock{m_mutex};
-        assert(!m_insertp);
-        m_insertp = itemp;
-    }
-    void insertf(const char* const filenamep, const int lineno) VL_MT_SAFE_EXCLUDES(m_mutex) {
-        const VerilatedLockGuard lock{m_mutex};
-        m_insertFilenamep = filenamep;
-        m_insertLineno = lineno;
-    }
-    void insertp(const char* ckeyps[VerilatedCovConst::MAX_KEYS],
-                 const char* valps[VerilatedCovConst::MAX_KEYS]) VL_MT_SAFE_EXCLUDES(m_mutex) {
-        const VerilatedLockGuard lock{m_mutex};
-        assert(m_insertp);
-        // First two key/vals are filename
-        ckeyps[0] = "filename";
-        valps[0] = m_insertFilenamep;
-        const std::string linestr = std::to_string(m_insertLineno);
-        ckeyps[1] = "lineno";
-        // cppcheck-suppress autoVariables  // Used only below for insert
-        valps[1] = linestr.c_str();
-        // Default page if not specified
-        const char* fnstartp = m_insertFilenamep;
-        while (const char* foundp = std::strchr(fnstartp, '/')) fnstartp = foundp + 1;
-        const char* fnendp = fnstartp;
-        for (; *fnendp && *fnendp != '.'; ++fnendp) {}
-        const size_t page_len = fnendp - fnstartp;
-        const std::string page_default = "sp_user/" + std::string{fnstartp, page_len};
-        ckeyps[2] = "page";
-        // cppcheck-suppress autoVariables  // Used only below for insert
-        valps[2] = page_default.c_str();
-
-        // Keys -> strings
-        std::array keys;
-        for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
-            if (ckeyps[i] && ckeyps[i][0]) keys[i] = ckeyps[i];
-        }
-        // Ignore empty keys
-        for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
-            if (!keys[i].empty()) {
-                for (int j = i + 1; j < VerilatedCovConst::MAX_KEYS; ++j) {
-                    if (keys[i] == keys[j]) {  // Duplicate key.  Keep the last one
-                        keys[i] = "";
-                        break;
-                    }
-                }
-            }
-        }
-        // Insert the values
-        int addKeynum = 0;
-        for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
-            const std::string key = keys[i];
-            if (!keys[i].empty()) {
-                const std::string val = valps[i];
-                // std::cout << "   " << __FUNCTION__ << "  " << key << " = " << val << "\n";
-                m_insertp->m_keys[addKeynum] = valueIndex(key);
-                m_insertp->m_vals[addKeynum] = valueIndex(val);
-                ++addKeynum;
-                if (VL_UNCOVERABLE(!legalKey(key))) {
-                    const std::string msg
-                        = ("%Error: Coverage keys of one character, or letter+digit are illegal: "
-                           + key);  // LCOV_EXCL_LINE
-                    VL_FATAL_MT("", 0, "", msg.c_str());
-                }
-            }
-        }
-        m_items.push_back(m_insertp);
-        // Prepare for next
-        m_insertp = nullptr;
-    }
-
-    // cppcheck-suppress duplInheritedMember
-    void write(const std::string& filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
-        Verilated::quiesce();
-        const VerilatedLockGuard lock{m_mutex};
-        selftest();
-
-        std::ofstream os{filename};
-        if (os.fail()) {
-            const std::string msg = "%Error: Can't write '"s + filename + "'";
-            VL_FATAL_MT("", 0, "", msg.c_str());
-            return;
-        }
-        os << "# SystemC::Coverage-3\n";
-
-        // Build list of events; totalize if collapsing hierarchy
-        std::map> eventCounts;
-        for (const auto& itemp : m_items) {
-            std::string name;
-            std::string hier;
-            bool per_instance = false;
-            if (m_forcePerInstance) per_instance = true;
-
-            for (int i = 0; i < VerilatedCovConst::MAX_KEYS; ++i) {
-                if (itemp->m_keys[i] != VerilatedCovConst::KEY_UNDEF) {
-                    const std::string key
-                        = VerilatedCovKey::shortKey(m_indexValues[itemp->m_keys[i]]);
-                    const std::string val = m_indexValues[itemp->m_vals[i]];
-                    if (key == VL_CIK_PER_INSTANCE) {
-                        if (val != "0") per_instance = true;
-                    }
-                    if (key == VL_CIK_HIER) {
-                        hier = val;
-                    } else {
-                        // Print it
-                        if (key == "page") {
-                            const std::string type = val.substr(2, val.find('/') - 2);
-                            name += keyValueFormatter(VL_CIK_TYPE, type);
-                        }
-                        name += keyValueFormatter(key, val);
-                    }
-                }
-            }
-            if (per_instance) {  // Not collapsing hierarchies
-                name += keyValueFormatter(VL_CIK_HIER, hier);
-                hier = "";
-            }
-
-            // Group versus point labels don't matter here, downstream
-            // deals with it.  Seems bad for sizing though and doesn't
-            // allow easy addition of new group codes (would be
-            // inefficient)
-
-            // Find or insert the named event
-            const auto cit = eventCounts.find(name);
-            if (cit != eventCounts.end()) {
-                const std::string& oldhier = cit->second.first;
-                cit->second.second += itemp->count();
-                cit->second.first = combineHier(oldhier, hier);
-            } else {
-                eventCounts.emplace(name, std::make_pair(hier, itemp->count()));
-            }
-        }
-
-        // Output body
-        for (const auto& i : eventCounts) {
-            os << "C '" << std::dec;
-            os << i.first;
-            if (!i.second.first.empty()) os << keyValueFormatter(VL_CIK_HIER, i.second.first);
-            os << "' " << i.second.second;
-            os << '\n';
-        }
-    }
-};
-
-//=============================================================================
-// VerilatedCovContext
-
-std::string VerilatedCovContext::defaultFilename() VL_MT_SAFE { return impp()->defaultFilename(); }
-void VerilatedCovContext::forcePerInstance(bool flag) VL_MT_SAFE {
-    impp()->forcePerInstance(flag);
-}
-void VerilatedCovContext::clear() VL_MT_SAFE { impp()->clear(); }
-void VerilatedCovContext::clearNonMatch(const char* matchp) VL_MT_SAFE {
-    impp()->clearNonMatch(matchp);
-}
-void VerilatedCovContext::zero() VL_MT_SAFE { impp()->zero(); }
-void VerilatedCovContext::write(const std::string& filename) VL_MT_SAFE {
-    impp()->write(filename);
-}
-void VerilatedCovContext::_inserti(uint32_t* itemp) VL_MT_SAFE {
-    impp()->inserti(new VerilatedCoverItemSpec{itemp});
-}
-void VerilatedCovContext::_inserti(uint64_t* itemp) VL_MT_SAFE {
-    impp()->inserti(new VerilatedCoverItemSpec{itemp});
-}
-void VerilatedCovContext::_insertf(const char* filename, int lineno) VL_MT_SAFE {
-    impp()->insertf(filename, lineno);
-}
-
-#ifndef DOXYGEN
-#define K(n) const char* key##n
-#define A(n) const char *key##n, const char *valp##n  // Argument list
-#define C(n) key##n, valp##n  // Calling argument list
-#define N(n) "", ""  // Null argument list
-void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9),
-                                   A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18),
-                                   A(19), A(20), A(21), A(22), A(23), A(24), A(25), A(26), A(27),
-                                   A(28), A(29)) VL_MT_SAFE {
-    const char* keyps[VerilatedCovConst::MAX_KEYS]
-        = {nullptr, nullptr, nullptr,  // filename,lineno,page
-           key0,    key1,    key2,    key3,  key4,  key5,  key6,  key7,  key8,  key9,
-           key10,   key11,   key12,   key13, key14, key15, key16, key17, key18, key19,
-           key20,   key21,   key22,   key23, key24, key25, key26, key27, key28, key29};
-    const char* valps[VerilatedCovConst::MAX_KEYS]
-        = {nullptr, nullptr, nullptr,  // filename,lineno,page
-           valp0,   valp1,   valp2,   valp3,  valp4,  valp5,  valp6,  valp7,  valp8,  valp9,
-           valp10,  valp11,  valp12,  valp13, valp14, valp15, valp16, valp17, valp18, valp19,
-           valp20,  valp21,  valp22,  valp23, valp24, valp25, valp26, valp27, valp28, valp29};
-    impp()->insertp(keyps, valps);
-}
-
-// And versions with fewer arguments  (oh for a language with named parameters!)
-void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8),
-                                   A(9)) VL_MT_SAFE {
-    _insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), N(10), N(11), N(12),
-             N(13), N(14), N(15), N(16), N(17), N(18), N(19), N(20), N(21), N(22), N(23), N(24),
-             N(25), N(26), N(27), N(28), N(29));
-}
-void VerilatedCovContext::_insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9),
-                                   A(10), A(11), A(12), A(13), A(14), A(15), A(16), A(17), A(18),
-                                   A(19)) VL_MT_SAFE {
-    _insertp(C(0), C(1), C(2), C(3), C(4), C(5), C(6), C(7), C(8), C(9), C(10), C(11), C(12),
-             C(13), C(14), C(15), C(16), C(17), C(18), C(19), N(20), N(21), N(22), N(23), N(24),
-             N(25), N(26), N(27), N(28), N(29));
-}
-// Backward compatibility for Verilator
-void VerilatedCovContext::_insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4),
-                                   const std::string& val4, A(5), A(6), A(7)) VL_MT_SAFE {
-    const std::string val2str = std::to_string(val2);
-    const std::string val3str = std::to_string(val3);
-    _insertp(C(0), C(1), key2, val2str.c_str(), key3, val3str.c_str(), key4, val4.c_str(), C(5),
-             C(6), C(7), N(8), N(9), N(10), N(11), N(12), N(13), N(14), N(15), N(16), N(17), N(18),
-             N(19), N(20), N(21), N(22), N(23), N(24), N(25), N(26), N(27), N(28), N(29));
-}
-#undef A
-#undef C
-#undef N
-#undef K
-
-#endif  // DOXYGEN
-
-//=============================================================================
-// VerilatedCov
-
-#ifndef VL_NO_LEGACY
-VerilatedCovContext* VerilatedCov::threadCovp() VL_MT_SAFE {
-    return Verilated::threadContextp()->coveragep();
-}
-#endif
-
-//=============================================================================
-// VerilatedContext
-
-VerilatedCovContext* VerilatedContext::coveragep() VL_MT_SAFE {
-    static VerilatedMutex s_mutex;
-    // cppcheck-suppress identicalInnerCondition
-    if (VL_UNLIKELY(!m_coveragep)) {
-        const VerilatedLockGuard lock{s_mutex};
-        // cppcheck-suppress identicalInnerCondition
-        if (VL_LIKELY(!m_coveragep)) {  // LCOV_EXCL_LINE // Not redundant, prevents race
-            m_coveragep.reset(new VerilatedCovImp{this});
-        }
-    }
-    return reinterpret_cast(m_coveragep.get());
-}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.h
deleted file mode 100644
index a6cbd2d4ecc..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov.h
+++ /dev/null
@@ -1,249 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated coverage analysis support header
-///
-/// This must be included in user wrapper code that wants to save coverage
-/// data.
-///
-/// It declares the VerilatedCovContext::write() which writes the collected
-/// coverage information.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_COV_H_
-#define VERILATOR_VERILATED_COV_H_
-
-#include "verilatedos.h"
-
-#include "verilated.h"
-
-#include 
-#include 
-#include 
-
-class VerilatedCovImp;
-
-//=============================================================================
-/// Insert an item for coverage analysis.
-/// The first argument is a pointer to the count to be dumped.
-/// The remaining arguments occur in pairs: A string key, and a value.
-/// The value may be a string, or another type which will be auto-converted to a string.
-///
-/// Some typical keys:
-///
-///     filename        File the recording occurs in.  Defaults to __FILE__.
-///     lineno          Line number the recording occurs in.  Defaults to __LINE__
-///     column          Column number (or occurrence# for dup file/lines).  Defaults to undef.
-///     hier            Hierarchical name.  Defaults to name()
-///     type            Type of coverage.  Defaults to "user"
-///                     Other types are 'block', 'fsm', 'toggle'.
-///     comment         Description of the coverage event.  Should be set by the user.
-///                     Comments for type==block: 'if', 'else', 'elsif', 'case'
-///     thresh          Threshold to consider fully covered.
-///                     If unspecified, downstream tools will determine it.
-///     per_instance    If non-zero don't combine all hierarchies into one count
-///
-/// Example:
-///
-///     uint32_t m_cases[10];  // Storage for coverage data
-///     constructor() {
-///         // Initialize
-///         for (int i = 0; i < 10; ++i) m_cases[i] = 0;
-///         // Insert
-///         for (int i = 0; i < 10; ++i)
-///             VL_COVER_INSERT(covp, name(), &m_cases[i], "comment", "Coverage Case",
-///                             "i", cvtToNumStr(i));
-///     }
-
-#define VL_COVER_INSERT(covcontextp, name, countp, ...) \
-    do { \
-        auto const ccontextp = covcontextp; \
-        ccontextp->_inserti(countp); \
-        ccontextp->_insertf(__FILE__, __LINE__); \
-        ccontextp->_insertp("hier", name, __VA_ARGS__); \
-    } while (false)
-
-static inline void VL_COV_TOGGLE_CHG_ST_I(const int width, uint32_t* covp, const IData newData,
-                                          const IData oldData) {
-    const IData chgData = newData ^ oldData;
-    for (int i = 0; i < width; ++i) {
-        *(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1;
-    }
-}
-
-static inline void VL_COV_TOGGLE_CHG_ST_Q(const int width, uint32_t* covp, const QData newData,
-                                          const QData oldData) {
-    const QData chgData = newData ^ oldData;
-    for (int i = 0; i < width; ++i) {
-        *(covp + 2 * i + ((newData >> i) & 1)) += (chgData >> i) & 1;
-    }
-}
-
-static inline void VL_COV_TOGGLE_CHG_ST_W(const int width, uint32_t* covp, WDataInP newData,
-                                          WDataInP oldData) {
-    for (int i = 0; i < VL_WORDS_I(width); ++i) {
-        const EData chgData = newData[i] ^ oldData[i];
-        if (chgData) {
-            for (int j = 0; j < width - i * VL_EDATASIZE; ++j) {
-                *(covp + (i * VL_EDATASIZE + j) * 2 + ((newData[i] >> j) & 1))
-                    += (chgData >> j) & 1;
-            }
-        }
-    }
-}
-
-static inline void VL_COV_TOGGLE_CHG_MT_I(const int width, std::atomic* covp,
-                                          const IData newData, const IData oldData) VL_MT_SAFE {
-    const IData chgData = newData ^ oldData;
-    for (int i = 0; i < width; ++i) {
-        if (VL_BITISSET_I(chgData, i)) {
-            (covp + 2 * i + ((newData >> i) & 1))->fetch_add(1, std::memory_order_relaxed);
-        }
-    }
-}
-
-static inline void VL_COV_TOGGLE_CHG_MT_Q(const int width, std::atomic* covp,
-                                          const QData newData, const QData oldData) VL_MT_SAFE {
-    const QData chgData = newData ^ oldData;
-    for (int i = 0; i < width; ++i) {
-        if (VL_BITISSET_Q(chgData, i)) {
-            (covp + 2 * i + ((newData >> i) & 1))->fetch_add(1, std::memory_order_relaxed);
-        }
-    }
-}
-
-static inline void VL_COV_TOGGLE_CHG_MT_W(const int width, std::atomic* covp,
-                                          WDataInP newData, WDataInP oldData) VL_MT_SAFE {
-    for (int i = 0; i < VL_WORDS_I(width); ++i) {
-        const EData chgData = newData[i] ^ oldData[i];
-        if (chgData) {
-            for (int j = 0; j < width - i * VL_EDATASIZE; ++j) {
-                if (VL_BITISSET_E(chgData, j)) {
-                    (covp + (i * VL_EDATASIZE + j) * 2 + ((newData[i] >> j) & 1))
-                        ->fetch_add(1, std::memory_order_relaxed);
-                }
-            }
-        }
-    }
-}
-
-//=============================================================================
-//  VerilatedCov
-/// Per-VerilatedContext coverage data class.
-/// All public methods in this class are thread safe.
-///
-/// This structure is accessed and constructed on first access via
-/// VerilatedContext::coveragep()
-
-class VerilatedCovContext VL_NOT_FINAL : public VerilatedVirtualBase {
-    VL_UNCOPYABLE(VerilatedCovContext);
-
-public:
-    // METHODS
-    /// Return default filename, may override with +verilator+coverage+file
-    std::string defaultFilename() VL_MT_SAFE;
-    /// Make all data per_instance, overriding point's per_instance
-    void forcePerInstance(bool flag) VL_MT_SAFE;
-    /// Write all coverage data to a file
-    void write() VL_MT_SAFE { write(defaultFilename()); }
-    void write(const std::string& filename) VL_MT_SAFE;
-    /// Clear coverage points (and call delete on all items)
-    void clear() VL_MT_SAFE;
-    /// Clear items not matching the provided string
-    void clearNonMatch(const char* matchp) VL_MT_SAFE;
-    /// Zero coverage points
-    void zero() VL_MT_SAFE;
-
-    // METHODS - public but Internal use only
-
-    // Insert a coverage item
-    // We accept from 1-30 key/value pairs, all as strings.
-    // Call _insert1, followed by _insert2 and _insert3
-    // Do not call directly; use VL_COVER_INSERT or higher level macros instead
-    // _insert1: Remember item pointer with count.  (Not const, as may add zeroing function)
-    void _inserti(uint32_t* itemp) VL_MT_SAFE;
-    void _inserti(uint64_t* itemp) VL_MT_SAFE;
-    // _insert2: Set default filename and line number
-    void _insertf(const char* filename, int lineno) VL_MT_SAFE;
-    // _insert3: Set parameters
-    // We could have just the maximum argument version, but this compiles
-    // much slower (nearly 2x) than having smaller versions also.  However
-    // there's not much more gain in having a version for each number of args.
-#ifndef DOXYGEN
-#define K(n) const char* key##n
-#define A(n) const char *key##n, const char *valp##n  // Argument list
-#define D(n) const char *key##n = nullptr, const char *valp##n = nullptr  // Argument list
-    void _insertp(D(0), D(1), D(2), D(3), D(4), D(5), D(6), D(7), D(8), D(9)) VL_MT_SAFE;
-    void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), D(11), D(12),
-                  D(13), D(14), D(15), D(16), D(17), D(18), D(19)) VL_MT_SAFE;
-    void _insertp(A(0), A(1), A(2), A(3), A(4), A(5), A(6), A(7), A(8), A(9), A(10), A(11), A(12),
-                  A(13), A(14), A(15), A(16), A(17), A(18), A(19), A(20), D(21), D(22), D(23),
-                  D(24), D(25), D(26), D(27), D(28), D(29)) VL_MT_SAFE;
-    // Backward compatibility for Verilator
-    void _insertp(A(0), A(1), K(2), int val2, K(3), int val3, K(4), const std::string& val4, A(5),
-                  A(6), A(7)) VL_MT_SAFE;
-
-#undef K
-#undef A
-#undef D
-#endif  // DOXYGEN
-
-protected:
-    friend class VerilatedCovImp;
-    // CONSTRUCTORS
-    // Internal: Only made as part of VerilatedCovImp
-    VerilatedCovContext() = default;
-    ~VerilatedCovContext() override = default;
-
-    // METHODS
-    // Internal: access to implementation class
-    VerilatedCovImp* impp() VL_MT_SAFE { return reinterpret_cast(this); }
-};
-
-//=============================================================================
-//  VerilatedCov
-/// Coverage global class.
-///
-/// Global class that accesses via current thread's context's
-/// VerilatedCovContext.  This class is provided only for
-/// backward-compatibility, use VerilatedContext::coveragep() instead.
-
-#ifndef VL_NO_LEGACY
-class VerilatedCov final {
-    VL_UNCOPYABLE(VerilatedCov);
-
-public:
-    // METHODS
-    /// Return default filename for the current thread
-    static std::string defaultFilename() VL_MT_SAFE { return threadCovp()->defaultFilename(); }
-    /// Write all coverage data to a file for the current thread
-    static void write() VL_MT_SAFE { write(defaultFilename()); }
-    static void write(const std::string& filename) VL_MT_SAFE { threadCovp()->write(filename); }
-    /// Clear coverage points (and call delete on all items) for the current thread
-    static void clear() VL_MT_SAFE { threadCovp()->clear(); }
-    /// Clear items not matching the provided string for the current thread
-    static void clearNonMatch(const char* matchp) VL_MT_SAFE {
-        threadCovp()->clearNonMatch(matchp);
-    }
-    /// Zero coverage points for the current thread
-    static void zero() VL_MT_SAFE { threadCovp()->zero(); }
-
-private:
-    // Current thread's coverage structure
-    static VerilatedCovContext* threadCovp() VL_MT_SAFE;
-};
-#endif  // VL_NO_LEGACY
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov_key.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov_key.h
deleted file mode 100644
index cb0a2efa988..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_cov_key.h
+++ /dev/null
@@ -1,85 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated coverage item keys internal header
-///
-/// This file is not part of the Verilated public-facing API.
-/// It is only for internal use by the Verilated library coverage routines.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_COV_KEY_H_
-#define VERILATOR_VERILATED_COV_KEY_H_
-
-#include "verilatedos.h"
-
-#include 
-
-//=============================================================================
-// Data used to edit below file, using vlcovgen
-
-#define VLCOVGEN_ITEM(string_parsed_by_vlcovgen)
-
-// clang-format off
-VLCOVGEN_ITEM("'name':'column',      'short':'n',  'group':1, 'default':0,    'descr':'Column number for the item.  Used to disambiguate multiple coverage points on the same line number'")
-VLCOVGEN_ITEM("'name':'filename',    'short':'f',  'group':1, 'default':None, 'descr':'Filename of the item'")
-VLCOVGEN_ITEM("'name':'linescov',    'short':'S',  'group':1, 'default':'',   'descr':'List of comma-separated lines covered'")
-VLCOVGEN_ITEM("'name':'per_instance','short':'P',  'group':1, 'default':0,    'descr':'True if every hierarchy is independently counted; otherwise all hierarchies will be combined into a single count'")
-VLCOVGEN_ITEM("'name':'thresh',      'short':'s',  'group':1, 'default':None, 'descr':'Number of hits to consider covered (aka at_least)'")
-VLCOVGEN_ITEM("'name':'type',        'short':'t',  'group':1, 'default':'',   'descr':'Type of coverage (block, line, fsm, etc)'")
-// Bin attributes
-VLCOVGEN_ITEM("'name':'comment',     'short':'o',  'group':0, 'default':'',   'descr':'Textual description for the item'")
-VLCOVGEN_ITEM("'name':'hier',        'short':'h',  'group':0, 'default':'',   'descr':'Hierarchy path name for the item'")
-VLCOVGEN_ITEM("'name':'lineno',      'short':'l',  'group':0, 'default':0,    'descr':'Line number for the item'")
-VLCOVGEN_ITEM("'name':'weight',      'short':'w',  'group':0, 'default':None, 'descr':'For totaling items, weight of this item'")
-// clang-format on
-
-// VLCOVGEN_CIK_AUTO_EDIT_BEGIN
-#define VL_CIK_COLUMN "n"
-#define VL_CIK_COMMENT "o"
-#define VL_CIK_FILENAME "f"
-#define VL_CIK_HIER "h"
-#define VL_CIK_LINENO "l"
-#define VL_CIK_LINESCOV "S"
-#define VL_CIK_PER_INSTANCE "P"
-#define VL_CIK_THRESH "s"
-#define VL_CIK_TYPE "t"
-#define VL_CIK_WEIGHT "w"
-// VLCOVGEN_CIK_AUTO_EDIT_END
-
-//=============================================================================
-// VerilatedCovKey
-// Namespace-style static class for \internal use.
-
-class VerilatedCovKey final {
-public:
-    // Return the short key code for a given a long coverage key
-    static std::string shortKey(const std::string& key) VL_PURE {
-        // VLCOVGEN_SHORT_AUTO_EDIT_BEGIN
-        if (key == "column") return VL_CIK_COLUMN;
-        if (key == "comment") return VL_CIK_COMMENT;
-        if (key == "filename") return VL_CIK_FILENAME;
-        if (key == "hier") return VL_CIK_HIER;
-        if (key == "lineno") return VL_CIK_LINENO;
-        if (key == "linescov") return VL_CIK_LINESCOV;
-        if (key == "per_instance") return VL_CIK_PER_INSTANCE;
-        if (key == "thresh") return VL_CIK_THRESH;
-        if (key == "type") return VL_CIK_TYPE;
-        if (key == "weight") return VL_CIK_WEIGHT;
-        // VLCOVGEN_SHORT_AUTO_EDIT_END
-        return key;
-    }
-};
-
-#endif  // guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.cpp
deleted file mode 100644
index d8f75f57797..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.cpp
+++ /dev/null
@@ -1,814 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2009-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=========================================================================
-///
-/// \file
-/// \brief Verilated DPI implementation code
-///
-/// This file must be compiled and linked against all Verilated objects
-/// that use the DPI.
-///
-/// Declare any DPI routine inside Verilog to add this to the Makefile for
-/// the linker.
-///
-/// For documentation on the exported functions (named sv*) that are
-/// implemented here, refer to the IEEE DPI chapter.
-///
-//=========================================================================
-
-#define VERILATOR_VERILATED_DPI_CPP_
-
-#include "verilatedos.h"
-
-#include "verilated_dpi.h"
-
-#include "verilated_imp.h"
-
-// On MSVC++ we need svdpi.h to declare exports, not imports
-#define DPI_PROTOTYPES
-#undef XXTERN
-#define XXTERN DPI_EXTERN DPI_DLLESPEC
-#undef EETERN
-#define EETERN DPI_EXTERN DPI_DLLESPEC
-
-#include "vltstd/svdpi.h"
-
-//======================================================================
-// Internal macros
-
-#define VL_SVDPI_WARN_(...) VL_PRINTF_MT(__VA_ARGS__)
-
-// Function requires a "context" in the import declaration
-#define VL_SVDPI_CONTEXT_WARN_() \
-    VL_SVDPI_WARN_("%%Warning: DPI C Function called by Verilog DPI import with missing " \
-                   "'context' keyword.\n")
-
-//======================================================================
-//======================================================================
-//======================================================================
-// DPI ROUTINES
-
-const char* svDpiVersion() { return "1800-2005"; }
-
-//======================================================================
-// Bit-select utility functions.
-
-svBit svGetBitselBit(const svBitVecVal* sp, int bit) { return VL_BITRSHIFT_W(sp, bit) & 1; }
-svLogic svGetBitselLogic(const svLogicVecVal* sp, int bit) {
-    // Not VL_BITRSHIFT_W as sp is a different structure type
-    // Verilator doesn't support X/Z so only aval
-    return (((sp[VL_BITWORD_I(bit)].aval >> VL_BITBIT_I(bit)) & 1)
-            | (((sp[VL_BITWORD_I(bit)].bval >> VL_BITBIT_I(bit)) & 1) << 1));
-}
-
-void svPutBitselBit(svBitVecVal* dp, int bit, svBit s) { VL_ASSIGNBIT_WI(bit, dp, s); }
-void svPutBitselLogic(svLogicVecVal* dp, int bit, svLogic s) {
-    // Verilator doesn't support X/Z so only aval
-    dp[VL_BITWORD_I(bit)].aval = ((dp[VL_BITWORD_I(bit)].aval & ~(VL_UL(1) << VL_BITBIT_I(bit)))
-                                  | ((s & 1) << VL_BITBIT_I(bit)));
-    dp[VL_BITWORD_I(bit)].bval = ((dp[VL_BITWORD_I(bit)].bval & ~(VL_UL(1) << VL_BITBIT_I(bit)))
-                                  | ((s & 2) >> 1 << VL_BITBIT_I(bit)));
-}
-
-void svGetPartselBit(svBitVecVal* dp, const svBitVecVal* sp, int lsb, int width) {
-    // Verilator supports > 32 bit widths, which is an extension to IEEE DPI
-    // See also VL_SEL_WWI
-    const int msb = lsb + width - 1;
-    const int word_shift = VL_BITWORD_I(lsb);
-    if (VL_BITBIT_I(lsb) == 0) {
-        // Just a word extract
-        for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift];
-    } else {
-        const int loffset = lsb & VL_SIZEBITS_I;
-        const int nbitsfromlow = 32 - loffset;  // bits that end up in lword (know loffset!=0)
-        // Middle words
-        const int words = VL_WORDS_I(msb - lsb + 1);
-        for (int i = 0; i < words; ++i) {
-            dp[i] = sp[i + word_shift] >> loffset;
-            const int upperword = i + word_shift + 1;
-            if (upperword <= static_cast(VL_BITWORD_I(msb))) {
-                dp[i] |= sp[upperword] << nbitsfromlow;
-            }
-        }
-    }
-    // Clean result
-    dp[VL_WORDS_I(width) - 1] &= VL_MASK_I(width);
-}
-void svGetPartselLogic(svLogicVecVal* dp, const svLogicVecVal* sp, int lsb, int width) {
-    // Verilator supports > 32 bit widths, which is an extension to IEEE DPI
-    const int msb = lsb + width - 1;
-    const int word_shift = VL_BITWORD_I(lsb);
-    if (VL_BITBIT_I(lsb) == 0) {
-        // Just a word extract
-        for (int i = 0; i < VL_WORDS_I(width); ++i) dp[i] = sp[i + word_shift];
-    } else {
-        const int loffset = lsb & VL_SIZEBITS_I;
-        const int nbitsfromlow = 32 - loffset;  // bits that end up in lword (know loffset!=0)
-        // Middle words
-        const int words = VL_WORDS_I(msb - lsb + 1);
-        for (int i = 0; i < words; ++i) {
-            dp[i].aval = sp[i + word_shift].aval >> loffset;
-            dp[i].bval = sp[i + word_shift].bval >> loffset;
-            const int upperword = i + word_shift + 1;
-            if (upperword <= static_cast(VL_BITWORD_I(msb))) {
-                dp[i].aval |= sp[upperword].aval << nbitsfromlow;
-                dp[i].bval |= sp[upperword].bval << nbitsfromlow;
-            }
-        }
-    }
-    // Clean result
-    dp[VL_WORDS_I(width) - 1].aval &= VL_MASK_I(width);
-    dp[VL_WORDS_I(width) - 1].bval &= VL_MASK_I(width);
-}
-void svPutPartselBit(svBitVecVal* dp, const svBitVecVal s, int lbit, int width) {
-    // See also _vl_insert_WI
-    const int hbit = lbit + width - 1;
-    const int hoffset = VL_BITBIT_I(hbit);
-    const int loffset = VL_BITBIT_I(lbit);
-    if (hoffset == VL_SIZEBITS_I && loffset == 0) {
-        // Fast and common case, word based insertion
-        dp[VL_BITWORD_I(lbit)] = s;
-    } else {
-        const int hword = VL_BITWORD_I(hbit);
-        const int lword = VL_BITWORD_I(lbit);
-        if (hword == lword) {  // know < 32 bits because above checks it
-            const IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
-            dp[lword] = (dp[lword] & ~insmask) | ((s << loffset) & insmask);
-        } else {
-            const IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
-            const IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
-            const int nbitsonright = 32 - loffset;  // bits that end up in lword
-            dp[lword] = (dp[lword] & ~linsmask) | ((s << loffset) & linsmask);
-            dp[hword] = (dp[hword] & ~hinsmask) | ((s >> nbitsonright) & hinsmask);
-        }
-    }
-}
-// cppcheck-suppress passedByValue
-void svPutPartselLogic(svLogicVecVal* dp, const svLogicVecVal s, int lbit, int width) {
-    const int hbit = lbit + width - 1;
-    const int hoffset = VL_BITBIT_I(hbit);
-    const int loffset = VL_BITBIT_I(lbit);
-    if (hoffset == VL_SIZEBITS_I && loffset == 0) {
-        // Fast and common case, word based insertion
-        dp[VL_BITWORD_I(lbit)].aval = s.aval;
-        dp[VL_BITWORD_I(lbit)].bval = s.bval;
-    } else {
-        const int hword = VL_BITWORD_I(hbit);
-        const int lword = VL_BITWORD_I(lbit);
-        if (hword == lword) {  // know < 32 bits because above checks it
-            const IData insmask = (VL_MASK_I(hoffset - loffset + 1)) << loffset;
-            dp[lword].aval = (dp[lword].aval & ~insmask) | ((s.aval << loffset) & insmask);
-            dp[lword].bval = (dp[lword].bval & ~insmask) | ((s.bval << loffset) & insmask);
-        } else {
-            const IData hinsmask = (VL_MASK_I(hoffset - 0 + 1)) << 0;
-            const IData linsmask = (VL_MASK_I(31 - loffset + 1)) << loffset;
-            const int nbitsonright = 32 - loffset;  // bits that end up in lword
-            dp[lword].aval = (dp[lword].aval & ~linsmask) | ((s.aval << loffset) & linsmask);
-            dp[lword].bval = (dp[lword].bval & ~linsmask) | ((s.bval << loffset) & linsmask);
-            dp[hword].aval = (dp[hword].aval & ~hinsmask) | ((s.aval >> nbitsonright) & hinsmask);
-            dp[hword].bval = (dp[hword].bval & ~hinsmask) | ((s.bval >> nbitsonright) & hinsmask);
-        }
-    }
-}
-
-//======================================================================
-// Open array internals
-
-static const VerilatedDpiOpenVar* _vl_openhandle_varp(const svOpenArrayHandle h) VL_MT_SAFE {
-    if (VL_UNLIKELY(!h)) {
-        VL_FATAL_MT(__FILE__, __LINE__, "",
-                    "%%Error: DPI svOpenArrayHandle function called with nullptr handle");
-    }
-    const VerilatedDpiOpenVar* const varp = reinterpret_cast(h);
-    if (VL_UNLIKELY(!varp->magicOk())) {
-        VL_FATAL_MT(__FILE__, __LINE__, "",
-                    "%%Error: DPI svOpenArrayHandle function called with non-Verilator handle");
-    }
-    return varp;
-}
-
-//======================================================================
-// Open array querying functions
-
-int svLeft(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->left(d); }
-int svRight(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->right(d); }
-int svLow(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->low(d); }
-int svHigh(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->high(d); }
-int svIncrement(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->increment(d); }
-int svSize(const svOpenArrayHandle h, int d) { return _vl_openhandle_varp(h)->elements(d); }
-int svDimensions(const svOpenArrayHandle h) { return _vl_openhandle_varp(h)->udims(); }
-
-// Return pointer to open array data, or nullptr if not in IEEE standard C layout
-void* svGetArrayPtr(const svOpenArrayHandle h) {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
-    if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
-    return varp->datap();
-}
-// Return size of open array, or 0 if not in IEEE standard C layout
-int svSizeOfArray(const svOpenArrayHandle h) {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
-    if (VL_UNLIKELY(!varp->isDpiStdLayout())) return 0;
-    // Truncate 64 bits to int; DPI is limited to 4GB
-    return static_cast(varp->totalSize());
-}
-
-//======================================================================
-// Open array access internals
-
-static void* _vl_sv_adjusted_datap(const VerilatedDpiOpenVar* varp, int nargs, int indx1,
-                                   int indx2, int indx3) VL_MT_SAFE {
-    void* datap = varp->datap();
-    if (VL_UNLIKELY(nargs != varp->udims())) {
-        VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function called on"
-                       " %d dimensional array using %d dimensional function.\n",
-                       varp->udims(), nargs);
-        return nullptr;
-    }
-    if (nargs >= 1) {
-        datap = varp->datapAdjustIndex(datap, 1, indx1);
-        if (VL_UNLIKELY(!datap)) {
-            VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function index 1 "
-                           "out of bounds; %d outside [%d:%d].\n",
-                           indx1, varp->left(1), varp->right(1));
-            return nullptr;
-        }
-    }
-    if (nargs >= 2) {
-        datap = varp->datapAdjustIndex(datap, 2, indx2);
-        if (VL_UNLIKELY(!datap)) {
-            VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function index 2 "
-                           "out of bounds; %d outside [%d:%d].\n",
-                           indx2, varp->left(2), varp->right(2));
-            return nullptr;
-        }
-    }
-    if (nargs >= 3) {
-        datap = varp->datapAdjustIndex(datap, 3, indx3);
-        if (VL_UNLIKELY(!datap)) {
-            VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function index 3 "
-                           "out of bounds; %d outside [%d:%d].\n",
-                           indx1, varp->left(3), varp->right(3));
-            return nullptr;
-        }
-    }
-    return datap;
-}
-
-// Return pointer to simulator open array element, or nullptr if outside range
-static void* _vl_svGetArrElemPtr(const svOpenArrayHandle h, int nargs, int indx1, int indx2,
-                                 int indx3) VL_MT_SAFE {
-    const VerilatedDpiOpenVar* varp = _vl_openhandle_varp(h);
-    if (VL_UNLIKELY(!varp->isDpiStdLayout())) return nullptr;
-    void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
-    return datap;
-}
-
-// Copy to user bit array from simulator open array
-static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int nargs,
-                                      int indx1, int indx2, int indx3) VL_MT_SAFE {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
-    void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
-    if (VL_UNLIKELY(!datap)) return;
-    switch (varp->vltype()) {  // LCOV_EXCL_BR_LINE
-    case VLVT_UINT8: d[0] = *(reinterpret_cast(datap)); return;
-    case VLVT_UINT16: d[0] = *(reinterpret_cast(datap)); return;
-    case VLVT_UINT32: d[0] = *(reinterpret_cast(datap)); return;
-    case VLVT_UINT64: {
-        VlWide<2> lwp;
-        VL_SET_WQ(lwp, *(reinterpret_cast(datap)));
-        d[0] = lwp[0];
-        d[1] = lwp[1];
-        break;
-    }
-    case VLVT_WDATA: {
-        WDataInP wdatap = (reinterpret_cast(datap));
-        for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) d[i] = wdatap[i];
-        return;
-    }
-    default:  // LCOV_EXCL_START  // Errored earlier
-        VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
-                       varp->vltype());
-        return;  // LCOV_EXCL_STOP
-    }
-}
-// Copy to user logic array from simulator open array
-static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int nargs,
-                                        int indx1, int indx2, int indx3) VL_MT_SAFE {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
-    void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
-    if (VL_UNLIKELY(!datap)) return;
-    switch (varp->vltype()) {  // LCOV_EXCL_BR_LINE
-    case VLVT_UINT8:
-        d[0].aval = *(reinterpret_cast(datap));
-        d[0].bval = 0;
-        return;
-    case VLVT_UINT16:
-        d[0].aval = *(reinterpret_cast(datap));
-        d[0].bval = 0;
-        return;
-    case VLVT_UINT32:
-        d[0].aval = *(reinterpret_cast(datap));
-        d[0].bval = 0;
-        return;
-    case VLVT_UINT64: {
-        VlWide<2> lwp;
-        VL_SET_WQ(lwp, *(reinterpret_cast(datap)));
-        d[0].aval = lwp[0];
-        d[0].bval = 0;
-        d[1].aval = lwp[1];
-        d[1].bval = 0;
-        break;
-    }
-    case VLVT_WDATA: {
-        WDataInP wdatap = (reinterpret_cast(datap));
-        for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) {
-            d[i].aval = wdatap[i];
-            d[i].bval = 0;
-        }
-        return;
-    }
-    default:  // LCOV_EXCL_START  // Errored earlier
-        VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
-                       varp->vltype());
-        return;  // LCOV_EXCL_STOP
-    }
-}
-
-// Copy to simulator open array from from user bit array
-static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int nargs,
-                                      int indx1, int indx2, int indx3) VL_MT_SAFE {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
-    void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
-    if (VL_UNLIKELY(!datap)) return;
-    switch (varp->vltype()) {  // LCOV_EXCL_BR_LINE
-    case VLVT_UINT8: *(reinterpret_cast(datap)) = s[0]; return;
-    case VLVT_UINT16: *(reinterpret_cast(datap)) = s[0]; return;
-    case VLVT_UINT32: *(reinterpret_cast(datap)) = s[0]; return;
-    case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1], s[0]); break;
-    case VLVT_WDATA: {
-        WDataOutP wdatap = (reinterpret_cast(datap));
-        for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i];
-        return;
-    }
-    default:  // LCOV_EXCL_START  // Errored earlier
-        VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
-                       varp->vltype());
-        return;  // LCOV_EXCL_STOP
-    }
-}
-// Copy to simulator open array from from user logic array
-static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s,
-                                        int nargs, int indx1, int indx2, int indx3) VL_MT_SAFE {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
-    void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
-    if (VL_UNLIKELY(!datap)) return;
-    switch (varp->vltype()) {  // LCOV_EXCL_BR_LINE
-    case VLVT_UINT8: *(reinterpret_cast(datap)) = s[0].aval; return;
-    case VLVT_UINT16: *(reinterpret_cast(datap)) = s[0].aval; return;
-    case VLVT_UINT32: *(reinterpret_cast(datap)) = s[0].aval; return;
-    case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1].aval, s[0].aval); break;
-    case VLVT_WDATA: {
-        WDataOutP wdatap = (reinterpret_cast(datap));
-        for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i].aval;
-        return;
-    }
-    default:  // LCOV_EXCL_START  // Errored earlier
-        VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
-                       varp->vltype());
-        return;  // LCOV_EXCL_STOP
-    }
-}
-
-// Return bit from simulator open array
-static svBit _vl_svGetBitArrElem(const svOpenArrayHandle s, int nargs, int indx1, int indx2,
-                                 int indx3, int) VL_MT_SAFE {
-    // One extra index supported, as need bit number
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
-    void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
-    if (VL_UNLIKELY(!datap)) return 0;
-    switch (varp->vltype()) {  // LCOV_EXCL_BR_LINE
-    case VLVT_UINT8: return (*(reinterpret_cast(datap))) & 1;
-    default:  // LCOV_EXCL_START  // Errored earlier
-        VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
-                       varp->vltype());
-        return 0;  // LCOV_EXCL_STOP
-    }
-}
-// Update simulator open array from bit
-static void _vl_svPutBitArrElem(const svOpenArrayHandle d, svBit value, int nargs, int indx1,
-                                int indx2, int indx3, int) VL_MT_SAFE {
-    // One extra index supported, as need bit number
-    value &= 1;  // Make sure clean
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
-    void* const datap = _vl_sv_adjusted_datap(varp, nargs, indx1, indx2, indx3);
-    if (VL_UNLIKELY(!datap)) return;
-    switch (varp->vltype()) {  // LCOV_EXCL_BR_LINE
-    case VLVT_UINT8: *(reinterpret_cast(datap)) = value; return;
-    default:  // LCOV_EXCL_START  // Errored earlier
-        VL_SVDPI_WARN_("%%Warning: DPI svOpenArrayHandle function unsupported datatype (%d).\n",
-                       varp->vltype());
-        return;  // LCOV_EXCL_STOP
-    }
-}
-
-//======================================================================
-// DPI accessors that call above functions
-
-void* svGetArrElemPtr(const svOpenArrayHandle h, int indx1, ...) {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(h);
-    void* datap;
-    va_list ap;
-    va_start(ap, indx1);
-    // va_arg is a macro, so need temporaries as used below
-    switch (varp->udims()) {
-    case 1: datap = _vl_svGetArrElemPtr(h, 1, indx1, 0, 0); break;
-    case 2: {
-        const int indx2 = va_arg(ap, int);
-        datap = _vl_svGetArrElemPtr(h, 2, indx1, indx2, 0);
-        break;
-    }
-    case 3: {
-        const int indx2 = va_arg(ap, int);
-        const int indx3 = va_arg(ap, int);
-        datap = _vl_svGetArrElemPtr(h, 3, indx1, indx2, indx3);
-        break;
-    }
-    default: datap = _vl_svGetArrElemPtr(h, -1, 0, 0, 0); break;  // Will error
-    }
-    va_end(ap);
-    return datap;
-}
-void* svGetArrElemPtr1(const svOpenArrayHandle h, int indx1) {
-    return _vl_svGetArrElemPtr(h, 1, indx1, 0, 0);
-}
-void* svGetArrElemPtr2(const svOpenArrayHandle h, int indx1, int indx2) {
-    return _vl_svGetArrElemPtr(h, 2, indx1, indx2, 0);
-}
-void* svGetArrElemPtr3(const svOpenArrayHandle h, int indx1, int indx2, int indx3) {
-    return _vl_svGetArrElemPtr(h, 3, indx1, indx2, indx3);
-}
-
-void svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, ...) {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
-    va_list ap;
-    va_start(ap, indx1);
-    switch (varp->udims()) {
-    case 1: _vl_svPutBitArrElemVecVal(d, s, 1, indx1, 0, 0); break;
-    case 2: {
-        const int indx2 = va_arg(ap, int);
-        _vl_svPutBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
-        break;
-    }
-    case 3: {
-        const int indx2 = va_arg(ap, int);
-        const int indx3 = va_arg(ap, int);
-        _vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
-        break;
-    }
-    default: _vl_svPutBitArrElemVecVal(d, s, -1, 0, 0, 0); break;  // Will error
-    }
-    va_end(ap);
-}
-void svPutBitArrElem1VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1) {
-    _vl_svPutBitArrElemVecVal(d, s, 1, indx1, 0, 0);
-}
-void svPutBitArrElem2VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1,
-                            int indx2) {
-    _vl_svPutBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
-}
-void svPutBitArrElem3VecVal(const svOpenArrayHandle d, const svBitVecVal* s, int indx1, int indx2,
-                            int indx3) {
-    _vl_svPutBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
-}
-void svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1, ...) {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
-    va_list ap;
-    va_start(ap, indx1);
-    switch (varp->udims()) {
-    case 1: _vl_svPutLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break;
-    case 2: {
-        const int indx2 = va_arg(ap, int);
-        _vl_svPutLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
-        break;
-    }
-    case 3: {
-        const int indx2 = va_arg(ap, int);
-        const int indx3 = va_arg(ap, int);
-        _vl_svPutLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
-        break;
-    }
-    default: _vl_svPutLogicArrElemVecVal(d, s, -1, 0, 0, 0); break;  // Will error
-    }
-    va_end(ap);
-}
-void svPutLogicArrElem1VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1) {
-    _vl_svPutLogicArrElemVecVal(d, s, 1, indx1, 0, 0);
-}
-void svPutLogicArrElem2VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1,
-                              int indx2) {
-    _vl_svPutLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
-}
-void svPutLogicArrElem3VecVal(const svOpenArrayHandle d, const svLogicVecVal* s, int indx1,
-                              int indx2, int indx3) {
-    _vl_svPutLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
-}
-
-//======================================================================
-// From simulator storage into user space
-
-void svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
-    va_list ap;
-    va_start(ap, indx1);
-    switch (varp->udims()) {
-    case 1: _vl_svGetBitArrElemVecVal(d, s, 1, indx1, 0, 0); break;
-    case 2: {
-        const int indx2 = va_arg(ap, int);
-        _vl_svGetBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
-        break;
-    }
-    case 3: {
-        const int indx2 = va_arg(ap, int);
-        const int indx3 = va_arg(ap, int);
-        _vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
-        break;
-    }
-    default: _vl_svGetBitArrElemVecVal(d, s, -1, 0, 0, 0); break;  // Will error
-    }
-    va_end(ap);
-}
-void svGetBitArrElem1VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1) {
-    _vl_svGetBitArrElemVecVal(d, s, 1, indx1, 0, 0);
-}
-void svGetBitArrElem2VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, int indx2) {
-    _vl_svGetBitArrElemVecVal(d, s, 2, indx1, indx2, 0);
-}
-void svGetBitArrElem3VecVal(svBitVecVal* d, const svOpenArrayHandle s, int indx1, int indx2,
-                            int indx3) {
-    _vl_svGetBitArrElemVecVal(d, s, 3, indx1, indx2, indx3);
-}
-void svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, ...) {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
-    va_list ap;
-    va_start(ap, indx1);
-    switch (varp->udims()) {
-    case 1: _vl_svGetLogicArrElemVecVal(d, s, 1, indx1, 0, 0); break;
-    case 2: {
-        const int indx2 = va_arg(ap, int);
-        _vl_svGetLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
-        break;
-    }
-    case 3: {
-        const int indx2 = va_arg(ap, int);
-        const int indx3 = va_arg(ap, int);
-        _vl_svGetLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
-        break;
-    }
-    default: _vl_svGetLogicArrElemVecVal(d, s, -1, 0, 0, 0); break;  // Will error
-    }
-    va_end(ap);
-}
-void svGetLogicArrElem1VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1) {
-    _vl_svGetLogicArrElemVecVal(d, s, 1, indx1, 0, 0);
-}
-void svGetLogicArrElem2VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, int indx2) {
-    _vl_svGetLogicArrElemVecVal(d, s, 2, indx1, indx2, 0);
-}
-void svGetLogicArrElem3VecVal(svLogicVecVal* d, const svOpenArrayHandle s, int indx1, int indx2,
-                              int indx3) {
-    _vl_svGetLogicArrElemVecVal(d, s, 3, indx1, indx2, indx3);
-}
-
-svBit svGetBitArrElem(const svOpenArrayHandle s, int indx1, ...) {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
-    svBit out;
-    va_list ap;
-    va_start(ap, indx1);
-    switch (varp->udims()) {
-    case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break;
-    case 2: {
-        const int indx2 = va_arg(ap, int);
-        out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0);
-        break;
-    }
-    case 3: {
-        const int indx2 = va_arg(ap, int);
-        const int indx3 = va_arg(ap, int);
-        out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0);
-        break;
-    }
-    default: out = _vl_svGetBitArrElem(s, -1, 0, 0, 0, 0); break;  // Will error
-    }
-    va_end(ap);
-    return out;
-}
-svBit svGetBitArrElem1(const svOpenArrayHandle s, int indx1) {
-    return _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0);
-}
-svBit svGetBitArrElem2(const svOpenArrayHandle s, int indx1, int indx2) {
-    return _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0);
-}
-svBit svGetBitArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx3) {
-    return _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0);
-}
-svLogic svGetLogicArrElem(const svOpenArrayHandle s, int indx1, ...) {
-    // Verilator doesn't support X/Z so can just call Bit version
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(s);
-    svBit out;
-    va_list ap;
-    va_start(ap, indx1);
-    switch (varp->udims()) {
-    case 1: out = _vl_svGetBitArrElem(s, 1, indx1, 0, 0, 0); break;
-    case 2: {
-        const int indx2 = va_arg(ap, int);
-        out = _vl_svGetBitArrElem(s, 2, indx1, indx2, 0, 0);
-        break;
-    }
-    case 3: {
-        const int indx2 = va_arg(ap, int);
-        const int indx3 = va_arg(ap, int);
-        out = _vl_svGetBitArrElem(s, 3, indx1, indx2, indx3, 0);
-        break;
-    }
-    default: out = _vl_svGetBitArrElem(s, -1, 0, 0, 0, 0); break;  // Will error
-    }
-    va_end(ap);
-    return out;
-}
-svLogic svGetLogicArrElem1(const svOpenArrayHandle s, int indx1) {
-    // Verilator doesn't support X/Z so can just call Bit version
-    return svGetBitArrElem1(s, indx1);
-}
-svLogic svGetLogicArrElem2(const svOpenArrayHandle s, int indx1, int indx2) {
-    // Verilator doesn't support X/Z so can just call Bit version
-    return svGetBitArrElem2(s, indx1, indx2);
-}
-svLogic svGetLogicArrElem3(const svOpenArrayHandle s, int indx1, int indx2, int indx3) {
-    // Verilator doesn't support X/Z so can just call Bit version
-    return svGetBitArrElem3(s, indx1, indx2, indx3);
-}
-
-void svPutBitArrElem(const svOpenArrayHandle d, svBit value, int indx1, ...) {
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
-    va_list ap;
-    va_start(ap, indx1);
-    switch (varp->udims()) {
-    case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break;
-    case 2: {
-        const int indx2 = va_arg(ap, int);
-        _vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0);
-        break;
-    }
-    case 3: {
-        const int indx2 = va_arg(ap, int);
-        const int indx3 = va_arg(ap, int);
-        _vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0);
-        break;
-    }
-    default: _vl_svPutBitArrElem(d, value, -1, 0, 0, 0, 0); break;  // Will error
-    }
-    va_end(ap);
-}
-void svPutBitArrElem1(const svOpenArrayHandle d, svBit value, int indx1) {
-    _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0);
-}
-void svPutBitArrElem2(const svOpenArrayHandle d, svBit value, int indx1, int indx2) {
-    _vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0);
-}
-void svPutBitArrElem3(const svOpenArrayHandle d, svBit value, int indx1, int indx2, int indx3) {
-    _vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0);
-}
-void svPutLogicArrElem(const svOpenArrayHandle d, svLogic value, int indx1, ...) {
-    // Verilator doesn't support X/Z so can just call Bit version
-    const VerilatedDpiOpenVar* const varp = _vl_openhandle_varp(d);
-    va_list ap;
-    va_start(ap, indx1);
-    switch (varp->udims()) {
-    case 1: _vl_svPutBitArrElem(d, value, 1, indx1, 0, 0, 0); break;
-    case 2: {
-        const int indx2 = va_arg(ap, int);
-        _vl_svPutBitArrElem(d, value, 2, indx1, indx2, 0, 0);
-        break;
-    }
-    case 3: {
-        const int indx2 = va_arg(ap, int);
-        const int indx3 = va_arg(ap, int);
-        _vl_svPutBitArrElem(d, value, 3, indx1, indx2, indx3, 0);
-        break;
-    }
-    default: _vl_svPutBitArrElem(d, value, -1, 0, 0, 0, 0); break;  // Will error
-    }
-    va_end(ap);
-}
-void svPutLogicArrElem1(const svOpenArrayHandle d, svLogic value, int indx1) {
-    // Verilator doesn't support X/Z so can just call Bit version
-    svPutBitArrElem1(d, value, indx1);
-}
-void svPutLogicArrElem2(const svOpenArrayHandle d, svLogic value, int indx1, int indx2) {
-    // Verilator doesn't support X/Z so can just call Bit version
-    svPutBitArrElem2(d, value, indx1, indx2);
-}
-void svPutLogicArrElem3(const svOpenArrayHandle d, svLogic value, int indx1, int indx2,
-                        int indx3) {
-    // Verilator doesn't support X/Z so can just call Bit version
-    svPutBitArrElem3(d, value, indx1, indx2, indx3);
-}
-
-//======================================================================
-// Functions for working with DPI context
-
-svScope svGetScope() {
-    if (VL_UNLIKELY(!Verilated::dpiInContext())) {
-        VL_SVDPI_CONTEXT_WARN_();
-        return nullptr;
-    }
-    // NOLINTNEXTLINE(google-readability-casting)
-    return (svScope)(Verilated::dpiScope());
-}
-
-svScope svSetScope(const svScope scope) {
-    const VerilatedScope* const prevScopep = Verilated::dpiScope();
-    const VerilatedScope* const vscopep = reinterpret_cast(scope);
-    Verilated::dpiScope(vscopep);
-    // NOLINTNEXTLINE(google-readability-casting)
-    return (svScope)(prevScopep);
-}
-
-const char* svGetNameFromScope(const svScope scope) {
-    const VerilatedScope* const vscopep = reinterpret_cast(scope);
-    return vscopep->name();
-}
-
-svScope svGetScopeFromName(const char* scopeName) {
-    // NOLINTNEXTLINE(google-readability-casting)
-    return (svScope)(Verilated::threadContextp()->scopeFind(scopeName));
-}
-
-int svPutUserData(const svScope scope, void* userKey, void* userData) {
-    VerilatedImp::userInsert(scope, userKey, userData);
-    return 0;
-}
-
-void* svGetUserData(const svScope scope, void* userKey) {
-    return VerilatedImp::userFind(scope, userKey);
-}
-
-int svGetCallerInfo(const char** fileNamepp, int* lineNumberp) {
-    if (VL_UNLIKELY(!Verilated::dpiInContext())) {
-        VL_SVDPI_CONTEXT_WARN_();
-        return false;
-    }
-    if (VL_LIKELY(fileNamepp)) *fileNamepp = Verilated::dpiFilenamep();  // thread local
-    if (VL_LIKELY(lineNumberp)) *lineNumberp = Verilated::dpiLineno();  // thread local
-    return true;
-}
-
-//======================================================================
-// Time
-
-int svGetTime(const svScope scope, svTimeVal* time) {
-    if (VL_UNLIKELY(!time)) return -1;
-    const QData qtime = VL_TIME_Q();
-    VlWide<2> itime;
-    VL_SET_WQ(itime, qtime);
-    time->low = itime[0];
-    time->high = itime[1];
-    return 0;
-}
-
-int svGetTimeUnit(const svScope scope, int32_t* time_unit) {
-    if (VL_UNLIKELY(!time_unit)) return -1;
-    const VerilatedScope* const vscopep = reinterpret_cast(scope);
-    if (!vscopep) {  // Null asks for global, not unlikely
-        *time_unit = Verilated::threadContextp()->timeunit();
-    } else {
-        *time_unit = vscopep->timeunit();
-    }
-    return 0;
-}
-
-int svGetTimePrecision(const svScope scope, int32_t* time_precision) {
-    if (VL_UNLIKELY(!time_precision)) return -1;
-    *time_precision = Verilated::threadContextp()->timeprecision();
-    return 0;
-}
-
-//======================================================================
-// Disables
-
-int svIsDisabledState() {
-    return 0;  // Disables not implemented
-}
-
-void svAckDisabledState() {
-    // Disables not implemented
-}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.h
deleted file mode 100644
index cb6efff6895..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_dpi.h
+++ /dev/null
@@ -1,112 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilated DPI header
-///
-/// This file is included automatically by Verilator at the top of all C++
-/// files it generates where DPI is used.  It contains DPI interface
-/// functions required by the Verilated code.
-///
-/// This file is not part of the Verilated public-facing API.
-/// It is only for internal use.
-///
-//*************************************************************************
-
-#ifndef VERILATOR_VERILATED_DPI_H_
-#define VERILATOR_VERILATED_DPI_H_
-
-#include "verilatedos.h"
-
-#include "verilated.h"  // Also presumably included by caller
-#include "verilated_sym_props.h"
-
-#include "svdpi.h"
-
-//===================================================================
-// SETTING OPERATORS
-
-// Convert svBitVecVal to Verilator internal data
-static inline void VL_SET_W_SVBV(int obits, WDataOutP owp, const svBitVecVal* lwp) VL_MT_SAFE {
-    const int words = VL_WORDS_I(obits);
-    for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
-    owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
-}
-static inline void VL_SET_Q_SVBV(int obits, QData& out, const svBitVecVal* lwp) VL_MT_SAFE {
-    out = VL_MASK_Q(obits) & VL_SET_QII(lwp[1], lwp[0]);
-}
-static inline void VL_SET_I_SVBV(int obits, IData& out, const svBitVecVal* lwp) VL_MT_SAFE {
-    out = VL_MASK_I(obits) & lwp[0];
-}
-static inline void VL_SET_S_SVBV(int obits, SData& out, const svBitVecVal* lwp) VL_MT_SAFE {
-    out = VL_MASK_I(obits) & lwp[0];
-}
-static inline void VL_SET_C_SVBV(int obits, CData& out, const svBitVecVal* lwp) VL_MT_SAFE {
-    out = VL_MASK_I(obits) & lwp[0];
-}
-
-// Convert Verilator internal data to svBitVecVal
-static inline void VL_SET_SVBV_W(int obits, svBitVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
-    const int words = VL_WORDS_I(obits);
-    for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i];
-    owp[words - 1] = lwp[words - 1] & VL_MASK_I(obits);
-}
-static inline void VL_SET_SVBV_I(int, svBitVecVal* owp, const IData ld) VL_MT_SAFE { owp[0] = ld; }
-static inline void VL_SET_SVBV_Q(int, svBitVecVal* owp, const QData ld) VL_MT_SAFE {
-    VL_SET_WQ(owp, ld);
-}
-
-// Convert svLogicVecVal to Verilator internal data
-// Note these functions ignore X/Z in svLogicVecVal
-static inline void VL_SET_W_SVLV(int obits, WDataOutP owp, const svLogicVecVal* lwp) VL_MT_SAFE {
-    const int words = VL_WORDS_I(obits);
-    for (int i = 0; i < words - 1; ++i) owp[i] = lwp[i].aval;
-    owp[words - 1] = lwp[words - 1].aval & VL_MASK_I(obits);
-}
-static inline void VL_SET_Q_SVLV(int obits, QData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
-    out = VL_MASK_Q(obits) & VL_SET_QII(lwp[1].aval, lwp[0].aval);
-}
-static inline void VL_SET_I_SVLV(int obits, IData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
-    out = VL_MASK_I(obits) & lwp[0].aval;
-}
-static inline void VL_SET_S_SVLV(int obits, SData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
-    out = VL_MASK_I(obits) & lwp[0].aval;
-}
-static inline void VL_SET_C_SVLV(int obits, CData& out, const svLogicVecVal* lwp) VL_MT_SAFE {
-    out = VL_MASK_I(obits) & lwp[0].aval;
-}
-
-// Convert Verilator internal data to svLogicVecVal
-// Note these functions never create X/Z in svLogicVecVal
-static inline void VL_SET_SVLV_W(int obits, svLogicVecVal* owp, const WDataInP lwp) VL_MT_SAFE {
-    const int words = VL_WORDS_I(obits);
-    for (int i = 0; i < words; ++i) owp[i].bval = 0;
-    for (int i = 0; i < words - 1; ++i) owp[i].aval = lwp[i];
-    owp[words - 1].aval = lwp[words - 1] & VL_MASK_I(obits);
-}
-static inline void VL_SET_SVLV_I(int, svLogicVecVal* owp, const IData ld) VL_MT_SAFE {
-    owp[0].aval = ld;
-    owp[0].bval = 0;
-}
-static inline void VL_SET_SVLV_Q(int, svLogicVecVal* owp, const QData ld) VL_MT_SAFE {
-    VlWide<2> lwp;
-    VL_SET_WQ(lwp, ld);
-    owp[0].aval = lwp[0];
-    owp[0].bval = 0;
-    owp[1].aval = lwp[1];
-    owp[1].bval = 0;
-}
-
-//======================================================================
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.cpp
deleted file mode 100644
index f79e7316887..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.cpp
+++ /dev/null
@@ -1,405 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated C++ tracing in FST format implementation code
-///
-/// This file must be compiled and linked against all Verilated objects
-/// that use --trace-fst.
-///
-/// Use "verilator --trace-fst" to add this to the Makefile for the linker.
-///
-//=============================================================================
-
-// clang-format off
-
-#include "verilated.h"
-#include "verilated_fst_c.h"
-
-// GTKWave configuration
-#define HAVE_LIBPTHREAD
-#define FST_WRITER_PARALLEL
-#define LZ4_DISABLE_DEPRECATE_WARNINGS
-
-// Include the GTKWave implementation directly
-#define FST_CONFIG_INCLUDE "fst_config.h"
-#include "gtkwave/fastlz.c"
-#include "gtkwave/fstapi.c"
-#include "gtkwave/lz4.c"
-
-#include 
-#include 
-#include 
-#include 
-
-#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
-# include 
-#else
-# include 
-#endif
-
-// clang-format on
-
-//=============================================================================
-// Check that forward declared types matches the FST API types
-
-static_assert(std::is_same::value, "vlFstHandle mismatch");
-static_assert(std::is_same::value, "vlFstHandle mismatch");
-
-//=============================================================================
-// Specialization of the generics for this trace format
-
-#define VL_SUB_T VerilatedFst
-#define VL_BUF_T VerilatedFstBuffer
-#include "verilated_trace_imp.h"
-#undef VL_SUB_T
-#undef VL_BUF_T
-
-//=============================================================================
-// VerilatedFst
-
-VerilatedFst::VerilatedFst(void* /*fst*/) {}
-
-VerilatedFst::~VerilatedFst() {
-    if (m_fst) fstWriterClose(m_fst);
-    if (m_symbolp) VL_DO_CLEAR(delete[] m_symbolp, m_symbolp = nullptr);
-    if (m_strbufp) VL_DO_CLEAR(delete[] m_strbufp, m_strbufp = nullptr);
-}
-
-void VerilatedFst::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    m_fst = fstWriterCreate(filename, 1);
-    fstWriterSetPackType(m_fst, FST_WR_PT_LZ4);
-    fstWriterSetTimescaleFromString(m_fst, timeResStr().c_str());  // lintok-begin-on-ref
-    if (m_useFstWriterThread) fstWriterSetParallelMode(m_fst, 1);
-    constDump(true);  // First dump must contain the const signals
-    fullDump(true);  // First dump must be full for fst
-
-    Super::traceInit();
-
-    // convert m_code2symbol into an array for fast lookup
-    if (!m_symbolp) {
-        m_symbolp = new fstHandle[nextCode()]{0};
-        for (const auto& i : m_code2symbol) m_symbolp[i.first] = i.second;
-    }
-    m_code2symbol.clear();
-
-    // Allocate string buffer for arrays
-    if (!m_strbufp) m_strbufp = new char[maxBits() + 32];
-}
-
-void VerilatedFst::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    Super::closeBase();
-    emitTimeChangeMaybe();
-    fstWriterClose(m_fst);
-    m_fst = nullptr;
-}
-
-void VerilatedFst::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    Super::flushBase();
-    emitTimeChangeMaybe();
-    fstWriterFlushContext(m_fst);
-}
-
-void VerilatedFst::emitTimeChange(uint64_t timeui) {
-    if (!timeui) fstWriterEmitTimeChange(m_fst, timeui);
-    m_timeui = timeui;
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedFst::emitTimeChangeMaybe() {
-    if (VL_UNLIKELY(m_timeui)) {
-        fstWriterEmitTimeChange(m_fst, m_timeui);
-        m_timeui = 0;
-    }
-}
-
-//=============================================================================
-// Decl
-
-void VerilatedFst::declDTypeEnum(int dtypenum, const char* name, uint32_t elements,
-                                 unsigned int minValbits, const char** itemNamesp,
-                                 const char** itemValuesp) {
-    const fstEnumHandle enumNum
-        = fstWriterCreateEnumTable(m_fst, name, elements, minValbits, itemNamesp, itemValuesp);
-    const bool newEntry = m_local2fstdtype[initUserp()].emplace(dtypenum, enumNum).second;
-    assert(newEntry);
-}
-
-// TODO: should return std::optional, but I can't have C++17
-static std::pair toFstScopeType(VerilatedTracePrefixType type) {
-    switch (type) {
-    case VerilatedTracePrefixType::SCOPE_MODULE: return {true, FST_ST_VCD_MODULE};
-    case VerilatedTracePrefixType::SCOPE_INTERFACE: return {true, FST_ST_VCD_INTERFACE};
-    case VerilatedTracePrefixType::STRUCT_PACKED:
-    case VerilatedTracePrefixType::STRUCT_UNPACKED: return {true, FST_ST_VCD_STRUCT};
-    case VerilatedTracePrefixType::UNION_PACKED: return {true, FST_ST_VCD_UNION};
-    default: return {false, /* unused so whatever, just need a value */ FST_ST_VCD_SCOPE};
-    }
-}
-
-void VerilatedFst::pushPrefix(const char* namep, VerilatedTracePrefixType type) {
-    assert(!m_prefixStack.empty());  // Constructor makes an empty entry
-    const std::string name{namep};
-    // An empty name means this is the root of a model created with
-    // name()=="".  The tools get upset if we try to pass this as empty, so
-    // we put the signals under a new $rootio scope, but the signals
-    // further down will be peers, not children (as usual for name()!="").
-    const std::string prevPrefix = m_prefixStack.back().first;
-    if (name == "$rootio" && !prevPrefix.empty()) {
-        // Upper has name, we can suppress inserting $rootio, but still push so popPrefix works
-        m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
-        return;
-    } else if (name.empty()) {
-        m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
-        return;
-    }
-
-    // This code assumes a signal at a given prefix level is declared before
-    // any pushPrefix are done at that same level.
-    const std::string newPrefix = prevPrefix + name;
-    const auto pair = toFstScopeType(type);
-    const bool properScope = pair.first;
-    const fstScopeType scopeType = pair.second;
-    m_prefixStack.emplace_back(newPrefix + (properScope ? " " : ""), type);
-    if (properScope) {
-        const std::string scopeName = lastWord(newPrefix);
-        fstWriterSetScope(m_fst, scopeType, scopeName.c_str(), nullptr);
-    }
-}
-
-void VerilatedFst::popPrefix() {
-    assert(!m_prefixStack.empty());
-    const bool properScope = toFstScopeType(m_prefixStack.back().second).first;
-    if (properScope) fstWriterSetUpscope(m_fst);
-    m_prefixStack.pop_back();
-    assert(!m_prefixStack.empty());  // Always one left, the constructor's initial one
-}
-
-void VerilatedFst::declare(uint32_t code, const char* name, int dtypenum,
-                           VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
-                           VerilatedTraceSigType type, bool array, int arraynum, bool bussed,
-                           int msb, int lsb) {
-    const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
-
-    const std::string hierarchicalName = m_prefixStack.back().first + name;
-
-    const bool enabled = Super::declCode(code, hierarchicalName, bits);
-    if (!enabled) return;
-
-    assert(hierarchicalName.rfind(' ') != std::string::npos);
-    std::stringstream name_ss;
-    name_ss << lastWord(hierarchicalName);
-    if (array) name_ss << "[" << arraynum << "]";
-    if (bussed) name_ss << " [" << msb << ":" << lsb << "]";
-    const std::string name_str = name_ss.str();
-
-    if (dtypenum > 0) {
-        fstWriterEmitEnumTableRef(m_fst, m_local2fstdtype.at(initUserp()).at(dtypenum));
-    }
-
-    fstVarDir varDir = FST_VD_IMPLICIT;
-    switch (direction) {
-    case VerilatedTraceSigDirection::INOUT: varDir = FST_VD_INOUT; break;
-    case VerilatedTraceSigDirection::OUTPUT: varDir = FST_VD_OUTPUT; break;
-    case VerilatedTraceSigDirection::INPUT: varDir = FST_VD_INPUT; break;
-    case VerilatedTraceSigDirection::NONE: varDir = FST_VD_IMPLICIT; break;
-    }
-
-    fstVarType varType;
-    // Doubles have special decoding properties, so must indicate if a double
-    if (type == VerilatedTraceSigType::DOUBLE) {
-        if (kind == VerilatedTraceSigKind::PARAMETER) {
-            varType = FST_VT_VCD_REAL_PARAMETER;
-        } else {
-            varType = FST_VT_VCD_REAL;
-        }
-    }
-    // clang-format off
-    else if (kind == VerilatedTraceSigKind::PARAMETER) varType = FST_VT_VCD_PARAMETER;
-    else if (kind == VerilatedTraceSigKind::SUPPLY0) varType = FST_VT_VCD_SUPPLY0;
-    else if (kind == VerilatedTraceSigKind::SUPPLY1) varType = FST_VT_VCD_SUPPLY1;
-    else if (kind == VerilatedTraceSigKind::TRI) varType = FST_VT_VCD_TRI;
-    else if (kind == VerilatedTraceSigKind::TRI0) varType = FST_VT_VCD_TRI0;
-    else if (kind == VerilatedTraceSigKind::TRI1) varType = FST_VT_VCD_TRI1;
-    else if (kind == VerilatedTraceSigKind::TRIAND) varType = FST_VT_VCD_TRIAND;
-    else if (kind == VerilatedTraceSigKind::TRIOR) varType = FST_VT_VCD_TRIOR;
-    else if (kind == VerilatedTraceSigKind::TRIREG) varType = FST_VT_VCD_TRIREG;
-    else if (kind == VerilatedTraceSigKind::WIRE) varType = FST_VT_VCD_WIRE;
-    //
-    else if (type == VerilatedTraceSigType::INTEGER) varType = FST_VT_VCD_INTEGER;
-    else if (type == VerilatedTraceSigType::BIT) varType = FST_VT_SV_BIT;
-    else if (type == VerilatedTraceSigType::LOGIC) varType = FST_VT_SV_LOGIC;
-    else if (type == VerilatedTraceSigType::INT) varType = FST_VT_SV_INT;
-    else if (type == VerilatedTraceSigType::SHORTINT) varType = FST_VT_SV_SHORTINT;
-    else if (type == VerilatedTraceSigType::LONGINT) varType = FST_VT_SV_LONGINT;
-    else if (type == VerilatedTraceSigType::BYTE) varType = FST_VT_SV_BYTE;
-    else if (type == VerilatedTraceSigType::EVENT) varType = FST_VT_VCD_EVENT;
-    else if (type == VerilatedTraceSigType::TIME) varType = FST_VT_VCD_TIME;
-    else { assert(0); /* Unreachable */ }
-    // clang-format on
-
-    const auto it = vlstd::as_const(m_code2symbol).find(code);
-    if (it == m_code2symbol.end()) {  // New
-        m_code2symbol[code]
-            = fstWriterCreateVar(m_fst, varType, varDir, bits, name_str.c_str(), 0);
-    } else {  // Alias
-        fstWriterCreateVar(m_fst, varType, varDir, bits, name_str.c_str(), it->second);
-    }
-}
-
-void VerilatedFst::declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                             VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
-                             VerilatedTraceSigType type, bool array, int arraynum) {
-    declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 0, 0);
-}
-void VerilatedFst::declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                           VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
-                           VerilatedTraceSigType type, bool array, int arraynum) {
-    declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 0, 0);
-}
-void VerilatedFst::declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                           VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
-                           VerilatedTraceSigType type, bool array, int arraynum, int msb,
-                           int lsb) {
-    declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb);
-}
-void VerilatedFst::declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                            VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
-                            VerilatedTraceSigType type, bool array, int arraynum, int msb,
-                            int lsb) {
-    declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb);
-}
-void VerilatedFst::declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                             VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
-                             VerilatedTraceSigType type, bool array, int arraynum, int msb,
-                             int lsb) {
-    declare(code, name, dtypenum, direction, kind, type, array, arraynum, true, msb, lsb);
-}
-void VerilatedFst::declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                              VerilatedTraceSigDirection direction, VerilatedTraceSigKind kind,
-                              VerilatedTraceSigType type, bool array, int arraynum) {
-    declare(code, name, dtypenum, direction, kind, type, array, arraynum, false, 63, 0);
-}
-
-//=============================================================================
-// Get/commit trace buffer
-
-VerilatedFst::Buffer* VerilatedFst::getTraceBuffer(uint32_t fidx) {
-    if (offload()) return new OffloadBuffer{*this};
-    return new Buffer{*this};
-}
-
-void VerilatedFst::commitTraceBuffer(VerilatedFst::Buffer* bufp) {
-    if (offload()) {
-        const OffloadBuffer* const offloadBufferp = static_cast(bufp);
-        if (offloadBufferp->m_offloadBufferWritep) {
-            m_offloadBufferWritep = offloadBufferp->m_offloadBufferWritep;
-            return;  // Buffer will be deleted by the offload thread
-        }
-    }
-    delete bufp;
-}
-
-//=============================================================================
-// Configure
-
-void VerilatedFst::configure(const VerilatedTraceConfig& config) {
-    // If at least one model requests the FST writer thread, then use it
-    m_useFstWriterThread |= config.m_useFstWriterThread;
-}
-
-//=============================================================================
-// VerilatedFstBuffer implementation
-
-//=============================================================================
-// Trace rendering primitives
-
-// Note: emit* are only ever called from one place (full* in
-// verilated_trace_imp.h, which is included in this file at the top),
-// so always inline them.
-
-VL_ATTR_ALWINLINE
-void VerilatedFstBuffer::emitEvent(uint32_t code) {
-    VL_DEBUG_IFDEF(assert(m_symbolp[code]););
-    m_owner.emitTimeChangeMaybe();
-    fstWriterEmitValueChange(m_fst, m_symbolp[code], "1");
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedFstBuffer::emitBit(uint32_t code, CData newval) {
-    VL_DEBUG_IFDEF(assert(m_symbolp[code]););
-    m_owner.emitTimeChangeMaybe();
-    fstWriterEmitValueChange(m_fst, m_symbolp[code], newval ? "1" : "0");
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedFstBuffer::emitCData(uint32_t code, CData newval, int bits) {
-    char buf[VL_BYTESIZE];
-    VL_DEBUG_IFDEF(assert(m_symbolp[code]););
-    cvtCDataToStr(buf, newval << (VL_BYTESIZE - bits));
-    m_owner.emitTimeChangeMaybe();
-    fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedFstBuffer::emitSData(uint32_t code, SData newval, int bits) {
-    char buf[VL_SHORTSIZE];
-    VL_DEBUG_IFDEF(assert(m_symbolp[code]););
-    cvtSDataToStr(buf, newval << (VL_SHORTSIZE - bits));
-    m_owner.emitTimeChangeMaybe();
-    fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedFstBuffer::emitIData(uint32_t code, IData newval, int bits) {
-    char buf[VL_IDATASIZE];
-    VL_DEBUG_IFDEF(assert(m_symbolp[code]););
-    cvtIDataToStr(buf, newval << (VL_IDATASIZE - bits));
-    m_owner.emitTimeChangeMaybe();
-    fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedFstBuffer::emitQData(uint32_t code, QData newval, int bits) {
-    char buf[VL_QUADSIZE];
-    VL_DEBUG_IFDEF(assert(m_symbolp[code]););
-    cvtQDataToStr(buf, newval << (VL_QUADSIZE - bits));
-    m_owner.emitTimeChangeMaybe();
-    fstWriterEmitValueChange(m_fst, m_symbolp[code], buf);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedFstBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) {
-    int words = VL_WORDS_I(bits);
-    char* wp = m_strbufp;
-    // Convert the most significant word
-    const int bitsInMSW = VL_BITBIT_E(bits) ? VL_BITBIT_E(bits) : VL_EDATASIZE;
-    cvtEDataToStr(wp, newvalp[--words] << (VL_EDATASIZE - bitsInMSW));
-    wp += bitsInMSW;
-    // Convert the remaining words
-    while (words > 0) {
-        cvtEDataToStr(wp, newvalp[--words]);
-        wp += VL_EDATASIZE;
-    }
-    m_owner.emitTimeChangeMaybe();
-    fstWriterEmitValueChange(m_fst, m_symbolp[code], m_strbufp);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedFstBuffer::emitDouble(uint32_t code, double newval) {
-    m_owner.emitTimeChangeMaybe();
-    fstWriterEmitValueChange(m_fst, m_symbolp[code], &newval);
-}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.h
deleted file mode 100644
index b2394ab0704..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_c.h
+++ /dev/null
@@ -1,262 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated tracing in FST format header
-///
-/// User wrapper code should use this header when creating FST traces.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_FST_C_H_
-#define VERILATOR_VERILATED_FST_C_H_
-
-#include "verilated.h"
-#include "verilated_trace.h"
-
-#include 
-#include 
-#include 
-#include 
-
-typedef uint32_t vlFstHandle;
-typedef uint32_t vlFstEnumHandle;
-
-class VerilatedFstBuffer;
-
-struct fstWriterContext;
-
-//=============================================================================
-// VerilatedFst
-// Base class to create a Verilator FST dump
-// This is an internally used class - see VerilatedFstC for what to call from applications
-
-class VerilatedFst final : public VerilatedTrace {
-public:
-    using Super = VerilatedTrace;
-
-private:
-    friend VerilatedFstBuffer;  // Give the buffer access to the private bits
-
-    //=========================================================================
-    // FST-specific internals
-
-    fstWriterContext* m_fst = nullptr;
-    std::map m_code2symbol;
-    std::map> m_local2fstdtype;
-    vlFstHandle* m_symbolp = nullptr;  // same as m_code2symbol, but as an array
-    char* m_strbufp = nullptr;  // String buffer long enough to hold maxBits() chars
-    uint64_t m_timeui = 0;  // Time to emit, 0 = not needed
-
-    bool m_useFstWriterThread = false;  // Whether to use the separate FST writer thread
-
-    // Prefixes to add to signal names/scope types
-    std::vector> m_prefixStack{
-        {"", VerilatedTracePrefixType::SCOPE_MODULE}};
-
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedFst);
-    void declare(uint32_t code, const char* name, int dtypenum, VerilatedTraceSigDirection,
-                 VerilatedTraceSigKind, VerilatedTraceSigType, bool array, int arraynum,
-                 bool bussed, int msb, int lsb);
-
-protected:
-    //=========================================================================
-    // Implementation of VerilatedTrace interface
-
-    // Called when the trace moves forward to a new time point
-    void emitTimeChange(uint64_t timeui) override;
-    void emitTimeChangeMaybe();
-
-    // Hooks called from VerilatedTrace
-    bool preFullDump() override { return isOpen(); }
-    bool preChangeDump() override { return isOpen(); }
-
-    // Trace buffer management
-    Buffer* getTraceBuffer(uint32_t fidx) override;
-    void commitTraceBuffer(Buffer*) override;
-
-    // Configure sub-class
-    void configure(const VerilatedTraceConfig&) override;
-
-public:
-    //=========================================================================
-    // External interface to client code
-
-    // CONSTRUCTOR
-    explicit VerilatedFst(void* fst = nullptr);
-    ~VerilatedFst();
-
-    // METHODS - All must be thread safe
-    // Open the file; call isOpen() to see if errors
-    void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
-    // Close the file
-    void close() VL_MT_SAFE_EXCLUDES(m_mutex);
-    // Flush any remaining data to this file
-    void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
-    // Return if file is open
-    bool isOpen() const VL_MT_SAFE { return m_fst != nullptr; }
-
-    //=========================================================================
-    // Internal interface to Verilator generated code
-
-    void pushPrefix(const char*, VerilatedTracePrefixType);
-    void popPrefix();
-
-    void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                   VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                   bool array, int arraynum);
-    void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                 VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                 bool array, int arraynum);
-    void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                 VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                 bool array, int arraynum, int msb, int lsb);
-    void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                  VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                  bool array, int arraynum, int msb, int lsb);
-    void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                   VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                   bool array, int arraynum, int msb, int lsb);
-    void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                    VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                    bool array, int arraynum);
-
-    void declDTypeEnum(int dtypenum, const char* name, uint32_t elements, unsigned int minValbits,
-                       const char** itemNamesp, const char** itemValuesp);
-};
-
-#ifndef DOXYGEN
-// Declare specialization here as it's used in VerilatedFstC just below
-template <>
-void VerilatedFst::Super::dump(uint64_t time);
-template <>
-void VerilatedFst::Super::set_time_unit(const char* unitp);
-template <>
-void VerilatedFst::Super::set_time_unit(const std::string& unit);
-template <>
-void VerilatedFst::Super::set_time_resolution(const char* unitp);
-template <>
-void VerilatedFst::Super::set_time_resolution(const std::string& unit);
-template <>
-void VerilatedFst::Super::dumpvars(int level, const std::string& hier);
-#endif
-
-//=============================================================================
-// VerilatedFstBuffer
-
-class VerilatedFstBuffer VL_NOT_FINAL {
-    // Give the trace file access to the private bits
-    friend VerilatedFst;
-    friend VerilatedFst::Super;
-    friend VerilatedFst::Buffer;
-    friend VerilatedFst::OffloadBuffer;
-
-    VerilatedFst& m_owner;  // Trace file owning this buffer. Required by subclasses.
-
-    // The FST file handle
-    fstWriterContext* const m_fst = m_owner.m_fst;
-    // code to fstHande map, as an array
-    const vlFstHandle* const m_symbolp = m_owner.m_symbolp;
-    // String buffer long enough to hold maxBits() chars
-    char* const m_strbufp = m_owner.m_strbufp;
-
-    // CONSTRUCTOR
-    explicit VerilatedFstBuffer(VerilatedFst& owner)
-        : m_owner{owner} {}
-    virtual ~VerilatedFstBuffer() = default;
-
-    //=========================================================================
-    // Implementation of VerilatedTraceBuffer interface
-
-    // Implementations of duck-typed methods for VerilatedTraceBuffer. These are
-    // called from only one place (the full* methods), so always inline them.
-    VL_ATTR_ALWINLINE void emitEvent(uint32_t code);
-    VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
-    VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
-    VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
-    VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
-    VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
-    VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
-    VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
-};
-
-//=============================================================================
-// VerilatedFstC
-/// Create a FST dump file in C standalone (no SystemC) simulations.
-/// Also derived for use in SystemC simulations.
-
-class VerilatedFstC VL_NOT_FINAL : public VerilatedTraceBaseC {
-    VerilatedFst m_sptrace;  // Trace file being created
-
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedFstC);
-
-public:
-    /// Construct the dump. Optional argument is ignored.
-    explicit VerilatedFstC(void* filep = nullptr)
-        : m_sptrace{filep} {}
-    /// Destruct, flush, and close the dump
-    virtual ~VerilatedFstC() { close(); }
-
-    // METHODS - User called
-
-    /// Return if file is open
-    bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); }
-    /// Open a new FST file
-    virtual void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); }
-    /// Close dump
-    void close() VL_MT_SAFE {
-        m_sptrace.close();
-        modelConnected(false);
-    }
-    /// Flush dump
-    void flush() VL_MT_SAFE { m_sptrace.flush(); }
-    /// Write one cycle of dump data
-    /// Call with the current context's time just after eval'ed,
-    /// e.g. ->dump(contextp->time())
-    void dump(uint64_t timeui) { m_sptrace.dump(timeui); }
-    /// Write one cycle of dump data - backward compatible and to reduce
-    /// conversion warnings.  It's better to use a uint64_t time instead.
-    void dump(double timestamp) { dump(static_cast(timestamp)); }
-    void dump(uint32_t timestamp) { dump(static_cast(timestamp)); }
-    void dump(int timestamp) { dump(static_cast(timestamp)); }
-
-    // METHODS - Internal/backward compatible
-    // \protectedsection
-
-    // Set time units (s/ms, defaults to ns)
-    // Users should not need to call this, as for Verilated models, these
-    // propagate from the Verilated default timeunit
-    void set_time_unit(const char* unitp) VL_MT_SAFE { m_sptrace.set_time_unit(unitp); }
-    void set_time_unit(const std::string& unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); }
-    // Set time resolution (s/ms, defaults to ns)
-    // Users should not need to call this, as for Verilated models, these
-    // propagate from the Verilated default timeprecision
-    void set_time_resolution(const char* unitp) VL_MT_SAFE {
-        m_sptrace.set_time_resolution(unitp);
-    }
-    void set_time_resolution(const std::string& unit) VL_MT_SAFE {
-        m_sptrace.set_time_resolution(unit);
-    }
-    // Set variables to dump, using $dumpvars format
-    // If level = 0, dump everything and hier is then ignored
-    void dumpvars(int level, const std::string& hier) VL_MT_SAFE {
-        m_sptrace.dumpvars(level, hier);
-    }
-
-    // Internal class access
-    VerilatedFst* spTrace() { return &m_sptrace; }
-};
-
-#endif  // guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.cpp
deleted file mode 100644
index e19a491f189..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// THIS MODULE IS PUBLICLY LICENSED
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated tracing in FST for SystemC implementation code
-///
-/// This file is deprecated, only verilated_fst_sc.h is needed.
-/// It is provided only for backward compatibility with user's linker scripts.
-///
-//=============================================================================
-
-#ifdef VL_NO_LEGACY
-#error "verilated_fst_sc.cpp is deprecated; verilated_fst_sc.h is self-sufficient"
-#endif
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.h
deleted file mode 100644
index fe554add466..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_fst_sc.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated tracing in FST format for SystemC header
-///
-/// User wrapper code should use this header when creating FST SystemC traces.
-///
-/// This class is not threadsafe, as the SystemC kernel is not threadsafe.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_FST_SC_H_
-#define VERILATOR_VERILATED_FST_SC_H_
-
-#include "verilatedos.h"
-
-#include "verilated_fst_c.h"
-#include "verilated_sc_trace.h"
-
-//=============================================================================
-// VerilatedFstSc
-/// Trace file used to create FST dump for SystemC version of Verilated models. It's very similar
-/// to its C version (see the class VerilatedFstC)
-
-class VerilatedFstSc final : VerilatedScTraceBase, public VerilatedFstC {
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedFstSc);
-
-public:
-    VerilatedFstSc() {
-        spTrace()->set_time_unit(VerilatedScTraceBase::getScTimeUnit());
-        spTrace()->set_time_resolution(VerilatedScTraceBase::getScTimeResolution());
-    }
-
-    // METHODS
-    // Override VerilatedFstC. Must be called after starting simulation.
-    void open(const char* filename) override VL_MT_SAFE {
-        VerilatedScTraceBase::checkScElaborationDone();
-        VerilatedFstC::open(filename);
-    }
-
-    // METHODS - for SC kernel
-    // Called from SystemC kernel
-    void cycle() override { VerilatedFstC::dump(sc_core::sc_time_stamp().to_double()); }
-};
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_funcs.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_funcs.h
deleted file mode 100644
index e3e4534ffc2..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_funcs.h
+++ /dev/null
@@ -1,3059 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilated common functions
-///
-/// verilated.h should be included instead of this file.
-///
-/// Those macro/function/variable starting or ending in _ are internal,
-/// however many of the other function/macros here are also internal.
-///
-//*************************************************************************
-
-#ifndef VERILATOR_VERILATED_FUNCS_H_
-#define VERILATOR_VERILATED_FUNCS_H_
-
-#ifndef VERILATOR_VERILATED_H_INTERNAL_
-#error "verilated_funcs.h should only be included by verilated.h"
-#endif
-
-#include 
-
-//=========================================================================
-// Extern functions -- User may override -- See verilated.cpp
-
-/// Routine to call for $finish
-/// User code may wish to replace this function, to do so, define VL_USER_FINISH.
-/// This code does not have to be thread safe.
-/// Verilator internal code must call VL_FINISH_MT instead, which eventually calls this.
-extern void vl_finish(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE;
-
-/// Routine to call for $stop and non-fatal error
-/// User code may wish to replace this function, to do so, define VL_USER_STOP.
-/// This code does not have to be thread safe.
-/// Verilator internal code must call VL_STOP_MT instead, which eventually calls this.
-extern void vl_stop(const char* filename, int linenum, const char* hier) VL_MT_UNSAFE;
-
-/// Routine to call for fatal messages
-/// User code may wish to replace this function, to do so, define VL_USER_FATAL.
-/// This code does not have to be thread safe.
-/// Verilator internal code must call VL_FATAL_MT instead, which eventually calls this.
-extern void vl_fatal(const char* filename, int linenum, const char* hier,
-                     const char* msg) VL_MT_UNSAFE;
-
-/// Routine to call for warning messages
-/// User code may wish to replace this function, to do so, define VL_USER_WARN.
-/// This code does not have to be thread safe.
-/// Verilator internal code must call VL_WARN_MT instead, which eventually calls this.
-extern void vl_warn(const char* filename, int linenum, const char* hier,
-                    const char* msg) VL_MT_UNSAFE;
-
-//=========================================================================
-// Extern functions -- Slow path
-
-/// Multithread safe wrapper for calls to $finish
-extern void VL_FINISH_MT(const char* filename, int linenum, const char* hier) VL_MT_SAFE;
-/// Multithread safe wrapper for calls to $stop
-extern void VL_STOP_MT(const char* filename, int linenum, const char* hier,
-                       bool maybe = true) VL_MT_SAFE;
-/// Multithread safe wrapper to call for fatal messages
-extern void VL_FATAL_MT(const char* filename, int linenum, const char* hier,
-                        const char* msg) VL_MT_SAFE;
-/// Multithread safe wrapper to call for warning messages
-extern void VL_WARN_MT(const char* filename, int linenum, const char* hier,
-                       const char* msg) VL_MT_SAFE;
-
-// clang-format off
-/// Print a string, multithread safe. Eventually VL_PRINTF will get called.
-extern void VL_PRINTF_MT(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
-// clang-format on
-
-/// Print a debug message from internals with standard prefix, with printf style format
-extern void VL_DBG_MSGF(const char* formatp, ...) VL_ATTR_PRINTF(1) VL_MT_SAFE;
-
-/// Print a debug message from string via VL_DBG_MSGF
-inline void VL_DBG_MSGS(const std::string& str) VL_MT_SAFE { VL_DBG_MSGF("%s", str.c_str()); }
-
-// EMIT_RULE: VL_RANDOM:  oclean=dirty
-inline IData VL_RANDOM_I() VL_MT_SAFE { return vl_rand64(); }
-inline QData VL_RANDOM_Q() VL_MT_SAFE { return vl_rand64(); }
-extern WDataOutP VL_RANDOM_W(int obits, WDataOutP outwp) VL_MT_SAFE;
-extern IData VL_RANDOM_SEEDED_II(IData& seedr) VL_MT_SAFE;
-extern IData VL_URANDOM_SEEDED_II(IData seed) VL_MT_SAFE;
-inline IData VL_URANDOM_RANGE_I(IData hi, IData lo) {
-    const uint64_t rnd = vl_rand64();
-    if (VL_LIKELY(hi > lo)) {
-        // (hi - lo + 1) can be zero when hi is UINT_MAX and lo is zero
-        if (VL_UNLIKELY(hi - lo + 1 == 0)) return rnd;
-        // Modulus isn't very fast but it's common that hi-low is power-of-two
-        return (rnd % (hi - lo + 1)) + lo;
-    } else {
-        if (VL_UNLIKELY(lo - hi + 1 == 0)) return rnd;
-        return (rnd % (lo - hi + 1)) + hi;
-    }
-}
-
-/// Random reset a signal of given width (init time only, var-specific PRNG)
-extern IData VL_SCOPED_RAND_RESET_I(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE;
-/// Random reset a signal of given width (init time only, var-specific PRNG)
-extern QData VL_SCOPED_RAND_RESET_Q(int obits, uint64_t scopeHash, uint64_t salt) VL_MT_UNSAFE;
-/// Random reset a signal of given width (init time only, var-specific PRNG)
-extern WDataOutP VL_SCOPED_RAND_RESET_W(int obits, WDataOutP outwp, uint64_t scopeHash,
-                                        uint64_t salt) VL_MT_UNSAFE;
-
-/// Random reset a signal of given width (assign time only)
-extern IData VL_SCOPED_RAND_RESET_ASSIGN_I(int obits, uint64_t scopeHash,
-                                           uint64_t salt) VL_MT_UNSAFE;
-/// Random reset a signal of given width (assign time only)
-extern QData VL_SCOPED_RAND_RESET_ASSIGN_Q(int obits, uint64_t scopeHash,
-                                           uint64_t salt) VL_MT_UNSAFE;
-/// Random reset a signal of given width (assign time only)
-extern WDataOutP VL_SCOPED_RAND_RESET_ASSIGN_W(int obits, WDataOutP outwp, uint64_t scopeHash,
-                                               uint64_t salt) VL_MT_UNSAFE;
-
-/// Random reset a signal of given width (init time only)
-extern IData VL_RAND_RESET_I(int obits) VL_MT_SAFE;
-/// Random reset a signal of given width (init time only)
-extern QData VL_RAND_RESET_Q(int obits) VL_MT_SAFE;
-/// Random reset a signal of given width (init time only)
-extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE;
-
-/// Zero reset a signal (slow - else use VL_ZERO_W)
-extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE;
-
-extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
-                              const VerilatedContext* contextp) VL_MT_SAFE;
-
-extern WDataOutP _vl_moddiv_w(int lbits, WDataOutP owp, WDataInP const lwp, WDataInP const rwp,
-                              bool is_modulus) VL_MT_SAFE;
-
-extern void _vl_vsss_based(WDataOutP owp, int obits, int baseLog2, const char* strp,
-                           size_t posstart, size_t posend) VL_MT_SAFE;
-
-extern IData VL_FGETS_IXI(int obits, void* destp, IData fpi) VL_MT_SAFE;
-
-extern void VL_FFLUSH_I(IData fdi) VL_MT_SAFE;
-extern IData VL_FSEEK_I(IData fdi, IData offset, IData origin) VL_MT_SAFE;
-extern IData VL_FTELL_I(IData fdi) VL_MT_SAFE;
-extern void VL_FCLOSE_I(IData fdi) VL_MT_SAFE;
-
-extern IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, IData fpi,
-                        IData start, IData count) VL_MT_SAFE;
-
-extern void VL_WRITEF_NX(const std::string& format, int argc, ...) VL_MT_SAFE;
-extern void VL_FWRITEF_NX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE;
-
-extern IData VL_FSCANF_INX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE;
-extern IData VL_SSCANF_IINX(int lbits, IData ld, const std::string& format, int argc,
-                            ...) VL_MT_SAFE;
-extern IData VL_SSCANF_IQNX(int lbits, QData ld, const std::string& format, int argc,
-                            ...) VL_MT_SAFE;
-extern IData VL_SSCANF_IWNX(int lbits, WDataInP const lwp, const std::string& format, int argc,
-                            ...) VL_MT_SAFE;
-
-extern void VL_SFORMAT_NX(int obits, CData& destr, const std::string& format, int argc,
-                          ...) VL_MT_SAFE;
-extern void VL_SFORMAT_NX(int obits, SData& destr, const std::string& format, int argc,
-                          ...) VL_MT_SAFE;
-extern void VL_SFORMAT_NX(int obits, IData& destr, const std::string& format, int argc,
-                          ...) VL_MT_SAFE;
-extern void VL_SFORMAT_NX(int obits, QData& destr, const std::string& format, int argc,
-                          ...) VL_MT_SAFE;
-extern void VL_SFORMAT_NX(int obits, void* destp, const std::string& format, int argc,
-                          ...) VL_MT_SAFE;
-
-extern void VL_STACKTRACE() VL_MT_SAFE;
-extern std::string VL_STACKTRACE_N() VL_MT_SAFE;
-extern IData VL_SYSTEM_IW(int lhswords, WDataInP const lhsp) VL_MT_SAFE;
-extern IData VL_SYSTEM_IQ(QData lhs) VL_MT_SAFE;
-inline IData VL_SYSTEM_II(IData lhs) VL_MT_SAFE { return VL_SYSTEM_IQ(lhs); }
-extern IData VL_SYSTEM_IN(const std::string& lhs) VL_MT_SAFE;
-
-extern IData VL_TESTPLUSARGS_I(const std::string& format) VL_MT_SAFE;
-extern const char* vl_mc_scan_plusargs(const char* prefixp) VL_MT_SAFE;  // PLIish
-
-//=========================================================================
-// Base macros
-
-// Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
-// Arguments must not have side effects
-#define VL_BITISSETLIMIT_W(data, width, bit) (((bit) < (width)) && VL_BITISSET_W(data, bit))
-
-// Shift appropriate word by bit. Does not account for wrapping between two words
-// Argument 'bit' must not have side effects
-#define VL_BITRSHIFT_W(data, bit) ((data)[VL_BITWORD_E(bit)] >> VL_BITBIT_E(bit))
-
-// Create two 32-bit words from quadword
-// WData is always at least 2 words; does not clean upper bits
-#define VL_SET_WQ(owp, data) \
-    do { \
-        (owp)[0] = static_cast(data); \
-        (owp)[1] = static_cast((data) >> VL_EDATASIZE); \
-    } while (false)
-#define VL_SET_WI(owp, data) \
-    do { \
-        (owp)[0] = static_cast(data); \
-        (owp)[1] = 0; \
-    } while (false)
-#define VL_SET_QW(lwp) \
-    ((static_cast((lwp)[0])) \
-     | (static_cast((lwp)[1]) << (static_cast(VL_EDATASIZE))))
-#define VL_SET_QII(ld, rd) ((static_cast(ld) << 32ULL) | static_cast(rd))
-
-// Return FILE* from IData
-extern FILE* VL_CVT_I_FP(IData lhs) VL_MT_SAFE;
-
-// clang-format off
-// Use a union to avoid cast-to-different-size warnings
-// Return void* from QData
-static inline void* VL_CVT_Q_VP(QData lhs) VL_PURE {
-    union { void* fp; QData q; } u;
-    u.q = lhs;
-    return u.fp;
-}
-// Return QData from const void*
-static inline QData VL_CVT_VP_Q(const void* fp) VL_PURE {
-    union { const void* fp; QData q; } u;
-    u.q = 0;
-    u.fp = fp;
-    return u.q;
-}
-// Return double from QData (bits, not numerically)
-static inline double VL_CVT_D_Q(QData lhs) VL_PURE {
-    union { double d; QData q; } u;
-    u.q = lhs;
-    return u.d;
-}
-// Return QData from double (bits, not numerically)
-static inline QData VL_CVT_Q_D(double lhs) VL_PURE {
-    union { double d; QData q; } u;
-    u.d = lhs;
-    return u.q;
-}
-// clang-format on
-// Return string from DPI char*
-static inline std::string VL_CVT_N_CSTR(const char* lhsp) VL_PURE {
-    return lhsp ? std::string{lhsp} : ""s;
-}
-
-// Return queue from an unpacked array
-template 
-static inline VlQueue VL_CVT_UNPACK_TO_Q(const VlUnpacked& q) VL_PURE {
-    VlQueue ret;
-    for (size_t i = 0; i < N_Depth; ++i) ret.push_back(q[i]);
-    return ret;
-}
-
-// Return double from lhs (numeric) unsigned
-double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
-static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE {
-    return static_cast(static_cast(lhs));
-}
-static inline double VL_ITOR_D_Q(int, QData lhs) VL_PURE {
-    return static_cast(static_cast(lhs));
-}
-// Return double from lhs (numeric) signed
-double VL_ISTOR_D_W(int lbits, WDataInP const lwp) VL_MT_SAFE;
-static inline double VL_ISTOR_D_I(int lbits, IData lhs) VL_MT_SAFE {
-    if (lbits == 32) return static_cast(static_cast(lhs));
-    VlWide lwp;
-    VL_SET_WI(lwp, lhs);
-    return VL_ISTOR_D_W(lbits, lwp);
-}
-static inline double VL_ISTOR_D_Q(int lbits, QData lhs) VL_MT_SAFE {
-    if (lbits == 64) return static_cast(static_cast(lhs));
-    VlWide lwp;
-    VL_SET_WQ(lwp, lhs);
-    return VL_ISTOR_D_W(lbits, lwp);
-}
-// Return IData truncated from double (numeric)
-static inline IData VL_RTOI_I_D(double lhs) VL_PURE { return static_cast(VL_TRUNC(lhs)); }
-
-// Sign extend such that if MSB set, we get ffff_ffff, else 0s
-// (Requires clean input)
-#define VL_SIGN_I(nbits, lhs) ((lhs) >> VL_BITBIT_I((nbits) - VL_UL(1)))
-#define VL_SIGN_Q(nbits, lhs) ((lhs) >> VL_BITBIT_Q((nbits) - 1ULL))
-#define VL_SIGN_E(nbits, lhs) ((lhs) >> VL_BITBIT_E((nbits) - VL_EUL(1)))
-#define VL_SIGN_W(nbits, rwp) \
-    ((rwp)[VL_BITWORD_E((nbits) - VL_EUL(1))] >> VL_BITBIT_E((nbits) - VL_EUL(1)))
-#define VL_SIGNONES_E(nbits, lhs) (-(VL_SIGN_E(nbits, lhs)))
-
-// Sign bit extended up to MSB, doesn't include unsigned portion
-// Optimization bug in GCC 3.3 returns different bitmasks to later states for
-static inline IData VL_EXTENDSIGN_I(int lbits, IData lhs) VL_PURE {
-    return (-((lhs) & (VL_UL(1) << (lbits - 1))));
-}
-static inline QData VL_EXTENDSIGN_Q(int lbits, QData lhs) VL_PURE {
-    return (-((lhs) & (1ULL << (lbits - 1))));
-}
-
-// Debugging prints
-extern void _vl_debug_print_w(int lbits, WDataInP const iwp) VL_MT_SAFE;
-
-//=========================================================================
-// Time handling
-
-// clang-format off
-
-#if defined(SYSTEMC_VERSION)
-/// Return current simulation time
-// Already defined: extern sc_time sc_time_stamp();
-inline uint64_t vl_time_stamp64() VL_MT_SAFE { return sc_core::sc_time_stamp().value(); }
-#else  // Non-SystemC
-# if !defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY)
-#  ifdef VL_TIME_STAMP64
-// vl_time_stamp64() may be optionally defined by the user to return time.
-// On MSVC++ weak symbols are not supported so must be declared, or define
-// VL_TIME_CONTEXT.
-extern uint64_t vl_time_stamp64() VL_ATTR_WEAK VL_MT_SAFE;
-#  else
-// sc_time_stamp() may be optionally defined by the user to return time.
-// On MSVC++ weak symbols are not supported so must be declared, or define
-// VL_TIME_CONTEXT.
-extern double sc_time_stamp() VL_ATTR_WEAK VL_MT_SAFE;  // Verilator 4.032 and newer
-inline uint64_t vl_time_stamp64() VL_MT_SAFE {
-    // clang9.0.1 requires & although we really do want the weak symbol value
-    // cppcheck-suppress duplicateValueTernary
-    return VL_LIKELY(&sc_time_stamp) ? static_cast(sc_time_stamp()) : 0;
-}
-#  endif
-# endif
-#endif
-
-// clang-format on
-
-uint64_t VerilatedContext::time() const VL_MT_SAFE {
-    // When using non-default context, fastest path is return time
-    if (VL_LIKELY(m_s.m_time)) return m_s.m_time;
-#if defined(SYSTEMC_VERSION) || (!defined(VL_TIME_CONTEXT) && !defined(VL_NO_LEGACY))
-    // Zero time could mean really at zero, or using callback
-    // clang9.0.1 requires & although we really do want the weak symbol value
-    if (VL_LIKELY(&vl_time_stamp64)) {  // else is weak symbol that is not defined
-        return vl_time_stamp64();
-    }
-#endif
-    return 0;
-}
-
-#define VL_TIME_Q() (Verilated::threadContextp()->time())
-#define VL_TIME_D() (static_cast(VL_TIME_Q()))
-
-// Time scaled from 1-per-precision into a module's time units ("Unit"-ed, not "United")
-// Optimized assuming scale is always constant.
-// Can't use multiply in Q flavor, as might lose precision
-#define VL_TIME_ROUND(t, p) (((t) + ((p) / 2)) / (p))
-#define VL_TIME_UNITED_Q(scale) VL_TIME_ROUND(VL_TIME_Q(), static_cast(scale))
-#define VL_TIME_UNITED_D(scale) (VL_TIME_D() / static_cast(scale))
-
-// Return time precision as multiplier of time units
-double vl_time_multiplier(int scale) VL_PURE;
-// Return power of 10. e.g. returns 100 if n==2
-uint64_t vl_time_pow10(int n) VL_PURE;
-// Return time as string with timescale suffix
-std::string vl_timescaled_double(double value, const char* format = "%0.0f%s") VL_PURE;
-
-//=========================================================================
-// Functional macros/routines
-// These all take the form
-//      VL_func_IW(bits, bits, op, op)
-//      VL_func_WW(bits, bits, out, op, op)
-// The I/W indicates if it's a integer or wide for the output and each operand.
-// The bits indicate the bit width of the output and each operand.
-// If wide output, a temporary storage location is specified.
-
-//===================================================================
-// SETTING OPERATORS
-
-VL_ATTR_ALWINLINE
-static WDataOutP VL_MEMSET_ZERO_W(WDataOutP owp, int words) VL_MT_SAFE {
-    return static_cast(std::memset(owp, 0, words * sizeof(EData)));
-}
-VL_ATTR_ALWINLINE
-static WDataOutP VL_MEMSET_ONES_W(WDataOutP owp, int words) VL_MT_SAFE {
-    return static_cast(std::memset(owp, 0xff, words * sizeof(EData)));
-}
-VL_ATTR_ALWINLINE
-static WDataOutP VL_MEMCPY_W(WDataOutP owp, WDataInP const iwp, int words) VL_MT_SAFE {
-    return static_cast(std::memcpy(owp, iwp, words * sizeof(EData)));
-}
-
-// Output clean
-// EMIT_RULE: VL_CLEAN:  oclean=clean; obits=lbits;
-#define VL_CLEAN_II(obits, lbits, lhs) ((lhs) & (VL_MASK_I(obits)))
-#define VL_CLEAN_QQ(obits, lbits, lhs) ((lhs) & (VL_MASK_Q(obits)))
-
-// EMIT_RULE: VL_ASSIGNCLEAN:  oclean=clean; obits==lbits;
-#define VL_ASSIGNCLEAN_W(obits, owp, lwp) VL_CLEAN_WW((obits), (owp), (lwp))
-static inline WDataOutP _vl_clean_inplace_w(int obits, WDataOutP owp) VL_MT_SAFE {
-    const int words = VL_WORDS_I(obits);
-    owp[words - 1] &= VL_MASK_E(obits);
-    return owp;
-}
-static inline WDataOutP VL_CLEAN_WW(int obits, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE {
-    const int words = VL_WORDS_I(obits);
-    VL_MEMCPY_W(owp, lwp, words - 1);
-    owp[words - 1] = lwp[words - 1] & VL_MASK_E(obits);
-    return owp;
-}
-static inline WDataOutP VL_ZERO_W(int obits, WDataOutP owp) VL_MT_SAFE {
-    return VL_MEMSET_ZERO_W(owp, VL_WORDS_I(obits));
-}
-static inline WDataOutP VL_ALLONES_W(int obits, WDataOutP owp) VL_MT_SAFE {
-    const int words = VL_WORDS_I(obits);
-    VL_MEMSET_ONES_W(owp, words - 1);
-    owp[words - 1] = VL_MASK_E(obits);
-    return owp;
-}
-
-// EMIT_RULE: VL_ASSIGN:  oclean=rclean; obits==lbits;
-// For now, we always have a clean rhs.
-// Note: If a ASSIGN isn't clean, use VL_ASSIGNCLEAN instead to do the same thing.
-static inline WDataOutP VL_ASSIGN_W(int obits, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE {
-    return VL_MEMCPY_W(owp, lwp, VL_WORDS_I(obits));
-}
-
-// EMIT_RULE: VL_ASSIGNBIT:  rclean=clean;
-static inline void VL_ASSIGNBIT_II(int bit, CData& lhsr, IData rhs) VL_PURE {
-    lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit)));
-}
-static inline void VL_ASSIGNBIT_II(int bit, SData& lhsr, IData rhs) VL_PURE {
-    lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit)));
-}
-static inline void VL_ASSIGNBIT_II(int bit, IData& lhsr, IData rhs) VL_PURE {
-    lhsr = ((lhsr & ~(VL_UL(1) << VL_BITBIT_I(bit))) | (rhs << VL_BITBIT_I(bit)));
-}
-static inline void VL_ASSIGNBIT_QI(int bit, QData& lhsr, QData rhs) VL_PURE {
-    lhsr = ((lhsr & ~(1ULL << VL_BITBIT_Q(bit))) | (static_cast(rhs) << VL_BITBIT_Q(bit)));
-}
-static inline void VL_ASSIGNBIT_WI(int bit, WDataOutP owp, IData rhs) VL_MT_SAFE {
-    const EData orig = owp[VL_BITWORD_E(bit)];
-    owp[VL_BITWORD_E(bit)] = ((orig & ~(VL_EUL(1) << VL_BITBIT_E(bit)))
-                              | (static_cast(rhs) << VL_BITBIT_E(bit)));
-}
-// Alternative form that is an instruction faster when rhs is constant one.
-static inline void VL_ASSIGNBIT_IO(int bit, CData& lhsr) VL_PURE {
-    lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit)));
-}
-static inline void VL_ASSIGNBIT_IO(int bit, SData& lhsr) VL_PURE {
-    lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit)));
-}
-static inline void VL_ASSIGNBIT_IO(int bit, IData& lhsr) VL_PURE {
-    lhsr = (lhsr | (VL_UL(1) << VL_BITBIT_I(bit)));
-}
-static inline void VL_ASSIGNBIT_QO(int bit, QData& lhsr) VL_PURE {
-    lhsr = (lhsr | (1ULL << VL_BITBIT_Q(bit)));
-}
-static inline void VL_ASSIGNBIT_WO(int bit, WDataOutP owp) VL_MT_SAFE {
-    const EData orig = owp[VL_BITWORD_E(bit)];
-    owp[VL_BITWORD_E(bit)] = (orig | (VL_EUL(1) << VL_BITBIT_E(bit)));
-}
-
-//===================================================================
-// SYSTEMC OPERATORS
-// Copying verilog format to systemc integers, doubles, and bit vectors.
-// Get a SystemC variable
-
-#define VL_ASSIGN_DSD(obits, vvar, svar) \
-    { (vvar) = (svar).read(); }
-#define VL_ASSIGN_ISI(obits, vvar, svar) \
-    { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read()); }
-#define VL_ASSIGN_QSQ(obits, vvar, svar) \
-    { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read()); }
-
-#define VL_ASSIGN_ISW(obits, od, svar) \
-    { (od) = ((svar).read().get_word(0)) & VL_MASK_I(obits); }
-#define VL_ASSIGN_QSW(obits, od, svar) \
-    { \
-        (od) = ((static_cast((svar).read().get_word(1))) << VL_IDATASIZE \
-                | (svar).read().get_word(0)) \
-               & VL_MASK_Q(obits); \
-    }
-#define VL_ASSIGN_WSW(obits, owp, svar) \
-    { \
-        const int words = VL_WORDS_I(obits); \
-        for (int i = 0; i < words; ++i) (owp)[i] = (svar).read().get_word(i); \
-        (owp)[words - 1] &= VL_MASK_E(obits); \
-    }
-
-#define VL_ASSIGN_ISU(obits, vvar, svar) \
-    { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read().to_uint()); }
-#define VL_ASSIGN_QSU(obits, vvar, svar) \
-    { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read().to_uint64()); }
-#define VL_ASSIGN_ISB(obits, vvar, svar) \
-    { (vvar) = VL_CLEAN_II((obits), (obits), (svar).read().to_uint()); }
-#define VL_ASSIGN_QSB(obits, vvar, svar) \
-    { (vvar) = VL_CLEAN_QQ((obits), (obits), (svar).read().to_uint64()); }
-#define VL_ASSIGN_WSB(obits, owp, svar) \
-    { \
-        const int words = VL_WORDS_I(obits); \
-        sc_dt::sc_biguint<(obits)> _butemp = (svar).read(); \
-        uint32_t* chunkp = _butemp.get_raw(); \
-        int32_t lsb = 0; \
-        while (lsb < obits - BITS_PER_DIGIT) { \
-            const uint32_t data = *chunkp; \
-            ++chunkp; \
-            _vl_insert_WI(owp.data(), data, lsb + BITS_PER_DIGIT - 1, lsb); \
-            lsb += BITS_PER_DIGIT; \
-        } \
-        if (lsb < obits) { \
-            const uint32_t msb_data = *chunkp; \
-            _vl_insert_WI(owp.data(), msb_data, obits - 1, lsb); \
-        } \
-        (owp)[words - 1] &= VL_MASK_E(obits); \
-    }
-
-// Copying verilog format from systemc integers, doubles, and bit vectors.
-// Set a SystemC variable
-
-#define VL_ASSIGN_SDD(obits, svar, vvar) \
-    { (svar).write(vvar); }
-#define VL_ASSIGN_SII(obits, svar, vvar) \
-    { (svar).write(vvar); }
-#define VL_ASSIGN_SQQ(obits, svar, vvar) \
-    { (svar).write(vvar); }
-
-#define VL_ASSIGN_SWI(obits, svar, rd) \
-    { \
-        sc_dt::sc_bv<(obits)> _bvtemp; \
-        _bvtemp.set_word(0, (rd)); \
-        (svar).write(_bvtemp); \
-    }
-#define VL_ASSIGN_SWQ(obits, svar, rd) \
-    { \
-        sc_dt::sc_bv<(obits)> _bvtemp; \
-        _bvtemp.set_word(0, static_cast(rd)); \
-        _bvtemp.set_word(1, static_cast((rd) >> VL_IDATASIZE)); \
-        (svar).write(_bvtemp); \
-    }
-#define VL_ASSIGN_SWW(obits, svar, rwp) \
-    { \
-        sc_dt::sc_bv<(obits)> _bvtemp; \
-        for (int i = 0; i < VL_WORDS_I(obits); ++i) _bvtemp.set_word(i, (rwp)[i]); \
-        (svar).write(_bvtemp); \
-    }
-
-#define VL_ASSIGN_SUI(obits, svar, rd) \
-    { (svar).write(rd); }
-#define VL_ASSIGN_SUQ(obits, svar, rd) \
-    { (svar).write(rd); }
-#define VL_ASSIGN_SBI(obits, svar, rd) \
-    { (svar).write(rd); }
-#define VL_ASSIGN_SBQ(obits, svar, rd) \
-    { (svar).write(rd); }
-#define VL_ASSIGN_SBW(obits, svar, rwp) \
-    { \
-        sc_dt::sc_biguint<(obits)> _butemp; \
-        int32_t lsb = 0; \
-        uint32_t* chunkp = _butemp.get_raw(); \
-        while (lsb + BITS_PER_DIGIT < (obits)) { \
-            static_assert(std::is_same::value, "IData and EData mismatch"); \
-            const uint32_t data \
-                = VL_SEL_IWII(lsb + BITS_PER_DIGIT + 1, (rwp).data(), lsb, BITS_PER_DIGIT); \
-            *chunkp = data & VL_MASK_E(BITS_PER_DIGIT); \
-            ++chunkp; \
-            lsb += BITS_PER_DIGIT; \
-        } \
-        if (lsb < (obits)) { \
-            const uint32_t msb_data = VL_SEL_IWII((obits) + 1, (rwp).data(), lsb, (obits) - lsb); \
-            *chunkp = msb_data & VL_MASK_E((obits) - lsb); \
-        } \
-        _butemp.set(0, *(rwp).data() & 1); /* force update the sign */ \
-        (svar).write(_butemp); \
-    }
-
-//===================================================================
-// Extending sizes
-
-// CAREFUL, we're width changing, so obits!=lbits
-
-// Right must be clean because otherwise size increase would pick up bad bits
-// EMIT_RULE: VL_EXTEND:  oclean=clean; rclean==clean;
-#define VL_EXTEND_II(obits, lbits, lhs) ((lhs))
-#define VL_EXTEND_QI(obits, lbits, lhs) (static_cast(lhs))
-#define VL_EXTEND_QQ(obits, lbits, lhs) ((lhs))
-
-static inline WDataOutP VL_EXTEND_WI(int obits, int, WDataOutP owp, IData ld) VL_MT_SAFE {
-    // Note for extracts that obits != lbits
-    owp[0] = ld;
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    return owp;
-}
-static inline WDataOutP VL_EXTEND_WQ(int obits, int, WDataOutP owp, QData ld) VL_MT_SAFE {
-    VL_SET_WQ(owp, ld);
-    VL_MEMSET_ZERO_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E);
-    return owp;
-}
-static inline WDataOutP VL_EXTEND_WW(int obits, int lbits, WDataOutP owp,
-                                     WDataInP const lwp) VL_MT_SAFE {
-    const int lwords = VL_WORDS_I(lbits);
-    VL_PREFETCH_RD(lwp);
-    VL_MEMSET_ZERO_W(owp + lwords, VL_WORDS_I(obits) - lwords);
-    return VL_MEMCPY_W(owp, lwp, lwords);
-}
-
-// EMIT_RULE: VL_EXTENDS:  oclean=*dirty*; obits=lbits;
-// Sign extension; output dirty
-static inline IData VL_EXTENDS_II(int, int lbits, IData lhs) VL_PURE {
-    return VL_EXTENDSIGN_I(lbits, lhs) | lhs;
-}
-static inline QData VL_EXTENDS_QI(int, int lbits, QData lhs /*Q_as_need_extended*/) VL_PURE {
-    return VL_EXTENDSIGN_Q(lbits, lhs) | lhs;
-}
-static inline QData VL_EXTENDS_QQ(int, int lbits, QData lhs) VL_PURE {
-    return VL_EXTENDSIGN_Q(lbits, lhs) | lhs;
-}
-
-static inline WDataOutP VL_EXTENDS_WI(int obits, int lbits, WDataOutP owp, IData ld) VL_MT_SAFE {
-    owp[0] = ld;
-    if (VL_SIGN_E(lbits, owp[0])) {
-        owp[0] |= ~VL_MASK_E(lbits);
-        VL_MEMSET_ONES_W(owp + 1, VL_WORDS_I(obits) - 1);
-    } else {
-        VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    }
-    return owp;
-}
-static inline WDataOutP VL_EXTENDS_WQ(int obits, int lbits, WDataOutP owp, QData ld) VL_MT_SAFE {
-    VL_SET_WQ(owp, ld);
-    if (VL_SIGN_E(lbits, owp[1])) {
-        owp[1] |= ~VL_MASK_E(lbits);
-        VL_MEMSET_ONES_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E);
-    } else {
-        VL_MEMSET_ZERO_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E);
-    }
-    return owp;
-}
-static inline WDataOutP VL_EXTENDS_WW(int obits, int lbits, WDataOutP owp,
-                                      WDataInP const lwp) VL_MT_SAFE {
-    const int lwords = VL_WORDS_I(lbits);
-    VL_PREFETCH_RD(lwp);
-    owp[lwords - 1] = lwp[lwords - 1];
-    if (VL_SIGN_E(lbits, lwp[lwords - 1])) {
-        owp[lwords - 1] |= ~VL_MASK_E(lbits);
-        VL_MEMSET_ONES_W(owp + lwords, VL_WORDS_I(obits) - lwords);
-    } else {
-        VL_MEMSET_ZERO_W(owp + lwords, VL_WORDS_I(obits) - lwords);
-    }
-    return VL_MEMCPY_W(owp, lwp, lwords - 1);
-}
-
-//===================================================================
-// REDUCTION OPERATORS
-
-// EMIT_RULE: VL_REDAND:  oclean=clean; lclean==clean; obits=1;
-#define VL_REDAND_II(lbits, lhs) ((lhs) == VL_MASK_I(lbits))
-#define VL_REDAND_IQ(lbits, lhs) ((lhs) == VL_MASK_Q(lbits))
-static inline IData VL_REDAND_IW(int lbits, WDataInP const lwp) VL_PURE {
-    const int words = VL_WORDS_I(lbits);
-    EData combine = lwp[0];
-    for (int i = 1; i < words - 1; ++i) combine &= lwp[i];
-    combine &= ~VL_MASK_E(lbits) | lwp[words - 1];
-    // cppcheck-suppress knownConditionTrueFalse
-    return ((~combine) == 0);
-}
-
-// EMIT_RULE: VL_REDOR:  oclean=clean; lclean==clean; obits=1;
-#define VL_REDOR_I(lhs) ((lhs) != 0)
-#define VL_REDOR_Q(lhs) ((lhs) != 0)
-static inline IData VL_REDOR_W(int words, WDataInP const lwp) VL_PURE {
-    EData equal = 0;
-    for (int i = 0; i < words; ++i) equal |= lwp[i];
-    return (equal != 0);
-}
-
-// EMIT_RULE: VL_REDXOR:  oclean=dirty; obits=1;
-static inline IData VL_REDXOR_2(IData r) VL_PURE {
-    // Experiments show VL_REDXOR_2 is faster than __builtin_parityl
-    r = (r ^ (r >> 1));
-    return r;
-}
-static inline IData VL_REDXOR_4(IData r) VL_PURE {
-#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS)
-    return __builtin_parityl(r);
-#else
-    r = (r ^ (r >> 1));
-    r = (r ^ (r >> 2));
-    return r;
-#endif
-}
-static inline IData VL_REDXOR_8(IData r) VL_PURE {
-#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS)
-    return __builtin_parityl(r);
-#else
-    r = (r ^ (r >> 1));
-    r = (r ^ (r >> 2));
-    r = (r ^ (r >> 4));
-    return r;
-#endif
-}
-static inline IData VL_REDXOR_16(IData r) VL_PURE {
-#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS)
-    return __builtin_parityl(r);
-#else
-    r = (r ^ (r >> 1));
-    r = (r ^ (r >> 2));
-    r = (r ^ (r >> 4));
-    r = (r ^ (r >> 8));
-    return r;
-#endif
-}
-static inline IData VL_REDXOR_32(IData r) VL_PURE {
-#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS)
-    return __builtin_parityl(r);
-#else
-    r = (r ^ (r >> 1));
-    r = (r ^ (r >> 2));
-    r = (r ^ (r >> 4));
-    r = (r ^ (r >> 8));
-    r = (r ^ (r >> 16));
-    return r;
-#endif
-}
-static inline IData VL_REDXOR_64(QData r) VL_PURE {
-#if defined(__GNUC__) && (__GNUC__ >= 4) && !defined(VL_NO_BUILTINS)
-    return __builtin_parityll(r);
-#else
-    r = (r ^ (r >> 1));
-    r = (r ^ (r >> 2));
-    r = (r ^ (r >> 4));
-    r = (r ^ (r >> 8));
-    r = (r ^ (r >> 16));
-    r = (r ^ (r >> 32));
-    return static_cast(r);
-#endif
-}
-static inline IData VL_REDXOR_W(int words, WDataInP const lwp) VL_PURE {
-    EData r = lwp[0];
-    for (int i = 1; i < words; ++i) r ^= lwp[i];
-    return VL_REDXOR_32(r);
-}
-
-// EMIT_RULE: VL_COUNTONES_II:  oclean = false; lhs clean
-static inline IData VL_COUNTONES_I(IData lhs) VL_PURE {
-    // This is faster than __builtin_popcountl
-    IData r = lhs - ((lhs >> 1) & 033333333333) - ((lhs >> 2) & 011111111111);
-    r = (r + (r >> 3)) & 030707070707;
-    r = (r + (r >> 6));
-    r = (r + (r >> 12) + (r >> 24)) & 077;
-    return r;
-}
-static inline IData VL_COUNTONES_Q(QData lhs) VL_PURE {
-    return VL_COUNTONES_I(static_cast(lhs)) + VL_COUNTONES_I(static_cast(lhs >> 32));
-}
-#define VL_COUNTONES_E VL_COUNTONES_I
-static inline IData VL_COUNTONES_W(int words, WDataInP const lwp) VL_PURE {
-    EData r = 0;
-    for (int i = 0; i < words; ++i) r += VL_COUNTONES_E(lwp[i]);
-    return r;
-}
-
-// EMIT_RULE: VL_COUNTBITS_II:  oclean = false; lhs clean
-static inline IData VL_COUNTBITS_I(int lbits, IData lhs, IData ctrl0, IData ctrl1,
-                                   IData ctrl2) VL_PURE {
-    const int ctrlSum = (ctrl0 & 0x1) + (ctrl1 & 0x1) + (ctrl2 & 0x1);
-    if (ctrlSum == 3) {
-        return VL_COUNTONES_I(lhs);
-    } else if (ctrlSum == 0) {
-        const IData mask = (lbits == 32) ? -1 : ((1 << lbits) - 1);
-        return VL_COUNTONES_I(~lhs & mask);
-    } else {
-        return (lbits == 32) ? 32 : lbits;
-    }
-}
-static inline IData VL_COUNTBITS_Q(int lbits, QData lhs, IData ctrl0, IData ctrl1,
-                                   IData ctrl2) VL_PURE {
-    return VL_COUNTBITS_I(32, static_cast(lhs), ctrl0, ctrl1, ctrl2)
-           + VL_COUNTBITS_I(lbits - 32, static_cast(lhs >> 32), ctrl0, ctrl1, ctrl2);
-}
-#define VL_COUNTBITS_E VL_COUNTBITS_I
-static inline IData VL_COUNTBITS_W(int lbits, int words, WDataInP const lwp, IData ctrl0,
-                                   IData ctrl1, IData ctrl2) VL_MT_SAFE {
-    EData r = 0;
-    IData wordLbits = 32;
-    for (int i = 0; i < words; ++i) {
-        if (i == words - 1) wordLbits = lbits % 32;
-        r += VL_COUNTBITS_E(wordLbits, lwp[i], ctrl0, ctrl1, ctrl2);
-    }
-    return r;
-}
-
-static inline IData VL_ONEHOT_I(IData lhs) VL_PURE {
-    return (((lhs & (lhs - 1)) == 0) & (lhs != 0));
-}
-static inline IData VL_ONEHOT_Q(QData lhs) VL_PURE {
-    return (((lhs & (lhs - 1)) == 0) & (lhs != 0));
-}
-static inline IData VL_ONEHOT_W(int words, WDataInP const lwp) VL_PURE {
-    EData one = 0;
-    for (int i = 0; (i < words); ++i) {
-        if (lwp[i]) {
-            if (one) return 0;
-            one = 1;
-            if (lwp[i] & (lwp[i] - 1)) return 0;
-        }
-    }
-    return one;
-}
-
-static inline IData VL_ONEHOT0_I(IData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); }
-static inline IData VL_ONEHOT0_Q(QData lhs) VL_PURE { return ((lhs & (lhs - 1)) == 0); }
-static inline IData VL_ONEHOT0_W(int words, WDataInP const lwp) VL_PURE {
-    bool one = false;
-    for (int i = 0; (i < words); ++i) {
-        if (lwp[i]) {
-            if (one) return 0;
-            one = true;
-            if (lwp[i] & (lwp[i] - 1)) return 0;
-        }
-    }
-    return 1;
-}
-
-static inline IData VL_CLOG2_I(IData lhs) VL_PURE {
-    // There are faster algorithms, or fls GCC4 builtins, but rarely used
-    // In C++20 there will be std::bit_width(lhs) - 1
-    if (VL_UNLIKELY(!lhs)) return 0;
-    --lhs;
-    int shifts = 0;
-    for (; lhs != 0; ++shifts) lhs = lhs >> 1;
-    return shifts;
-}
-static inline IData VL_CLOG2_Q(QData lhs) VL_PURE {
-    if (VL_UNLIKELY(!lhs)) return 0;
-    --lhs;
-    int shifts = 0;
-    for (; lhs != 0; ++shifts) lhs = lhs >> 1ULL;
-    return shifts;
-}
-static inline IData VL_CLOG2_W(int words, WDataInP const lwp) VL_PURE {
-    const EData adjust = (VL_COUNTONES_W(words, lwp) == 1) ? 0 : 1;
-    for (int i = words - 1; i >= 0; --i) {
-        if (VL_UNLIKELY(lwp[i])) {  // Shorter worst case if predict not taken
-            for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) {
-                if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) {
-                    return i * VL_EDATASIZE + bit + adjust;
-                }
-            }
-            // Can't get here - one bit must be set
-        }
-    }
-    return 0;
-}
-
-static inline IData VL_MOSTSETBITP1_W(int words, WDataInP const lwp) VL_PURE {
-    // MSB set bit plus one; similar to FLS.  0=value is zero
-    for (int i = words - 1; i >= 0; --i) {
-        if (VL_UNLIKELY(lwp[i])) {  // Shorter worst case if predict not taken
-            for (int bit = VL_EDATASIZE - 1; bit >= 0; --bit) {
-                if (VL_UNLIKELY(VL_BITISSET_E(lwp[i], bit))) return i * VL_EDATASIZE + bit + 1;
-            }
-            // Can't get here - one bit must be set
-        }
-    }
-    return 0;
-}
-
-//===================================================================
-// SIMPLE LOGICAL OPERATORS
-
-// EMIT_RULE: VL_AND:  oclean=lclean||rclean; obits=lbits; lbits==rbits;
-static inline WDataOutP VL_AND_W(int words, WDataOutP owp, WDataInP const lwp,
-                                 WDataInP const rwp) VL_MT_SAFE {
-    for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] & rwp[i]);
-    return owp;
-}
-// EMIT_RULE: VL_OR:   oclean=lclean&&rclean; obits=lbits; lbits==rbits;
-static inline WDataOutP VL_OR_W(int words, WDataOutP owp, WDataInP const lwp,
-                                WDataInP const rwp) VL_MT_SAFE {
-    for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] | rwp[i]);
-    return owp;
-}
-// EMIT_RULE: VL_CHANGEXOR:  oclean=1; obits=32; lbits==rbits;
-static inline IData VL_CHANGEXOR_W(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE {
-    IData od = 0;
-    for (int i = 0; (i < words); ++i) od |= (lwp[i] ^ rwp[i]);
-    return od;
-}
-// EMIT_RULE: VL_XOR:  oclean=lclean&&rclean; obits=lbits; lbits==rbits;
-static inline WDataOutP VL_XOR_W(int words, WDataOutP owp, WDataInP const lwp,
-                                 WDataInP const rwp) VL_MT_SAFE {
-    for (int i = 0; (i < words); ++i) owp[i] = (lwp[i] ^ rwp[i]);
-    return owp;
-}
-// EMIT_RULE: VL_NOT:  oclean=dirty; obits=lbits;
-static inline WDataOutP VL_NOT_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE {
-    for (int i = 0; i < words; ++i) owp[i] = ~(lwp[i]);
-    return owp;
-}
-
-//=========================================================================
-// Logical comparisons
-
-// EMIT_RULE: VL_EQ:  oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits;
-// EMIT_RULE: VL_NEQ: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits;
-// EMIT_RULE: VL_LT:  oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits;
-// EMIT_RULE: VL_GT:  oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits;
-// EMIT_RULE: VL_GTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits;
-// EMIT_RULE: VL_LTE: oclean=clean; lclean==clean; rclean==clean; obits=1; lbits==rbits;
-#define VL_NEQ_W(words, lwp, rwp) (!VL_EQ_W(words, lwp, rwp))
-#define VL_LT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) < 0)
-#define VL_LTE_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) <= 0)
-#define VL_GT_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) > 0)
-#define VL_GTE_W(words, lwp, rwp) (_vl_cmp_w(words, lwp, rwp) >= 0)
-
-// Output clean,  AND  MUST BE CLEAN
-static inline IData VL_EQ_W(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE {
-    EData nequal = 0;
-    for (int i = 0; (i < words); ++i) nequal |= (lwp[i] ^ rwp[i]);
-    return (nequal == 0);
-}
-
-// Internal usage
-static inline int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE {
-    for (int i = words - 1; i >= 0; --i) {
-        if (lwp[i] > rwp[i]) return 1;
-        if (lwp[i] < rwp[i]) return -1;
-    }
-    return 0;  // ==
-}
-
-#define VL_LTS_IWW(lbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) < 0)
-#define VL_LTES_IWW(lbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) <= 0)
-#define VL_GTS_IWW(lbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) > 0)
-#define VL_GTES_IWW(lbits, lwp, rwp) (_vl_cmps_w(lbits, lwp, rwp) >= 0)
-
-static inline IData VL_GTS_III(int lbits, IData lhs, IData rhs) VL_PURE {
-    // For lbits==32, this becomes just a single instruction, otherwise ~5.
-    // GCC 3.3.4 sign extension bugs on AMD64 architecture force us to use quad logic
-    const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);  // Q for gcc
-    const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);  // Q for gcc
-    return lhs_signed > rhs_signed;
-}
-static inline IData VL_GTS_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
-    const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
-    const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
-    return lhs_signed > rhs_signed;
-}
-
-static inline IData VL_GTES_III(int lbits, IData lhs, IData rhs) VL_PURE {
-    const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);  // Q for gcc
-    const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);  // Q for gcc
-    return lhs_signed >= rhs_signed;
-}
-static inline IData VL_GTES_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
-    const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
-    const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
-    return lhs_signed >= rhs_signed;
-}
-
-static inline IData VL_LTS_III(int lbits, IData lhs, IData rhs) VL_PURE {
-    const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);  // Q for gcc
-    const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);  // Q for gcc
-    return lhs_signed < rhs_signed;
-}
-static inline IData VL_LTS_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
-    const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
-    const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
-    return lhs_signed < rhs_signed;
-}
-
-static inline IData VL_LTES_III(int lbits, IData lhs, IData rhs) VL_PURE {
-    const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);  // Q for gcc
-    const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);  // Q for gcc
-    return lhs_signed <= rhs_signed;
-}
-static inline IData VL_LTES_IQQ(int lbits, QData lhs, QData rhs) VL_PURE {
-    const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
-    const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
-    return lhs_signed <= rhs_signed;
-}
-
-static inline int _vl_cmps_w(int lbits, WDataInP const lwp, WDataInP const rwp) VL_PURE {
-    const int words = VL_WORDS_I(lbits);
-    int i = words - 1;
-    // We need to flip sense if negative comparison
-    const EData lsign = VL_SIGN_E(lbits, lwp[i]);
-    const EData rsign = VL_SIGN_E(lbits, rwp[i]);
-    if (!lsign && rsign) return 1;  // + > -
-    if (lsign && !rsign) return -1;  // - < +
-    for (; i >= 0; --i) {
-        if (lwp[i] > rwp[i]) return 1;
-        if (lwp[i] < rwp[i]) return -1;
-    }
-    return 0;  // ==
-}
-
-//=========================================================================
-// Expressions
-
-// Output NOT clean
-static inline WDataOutP VL_NEGATE_W(int words, WDataOutP owp, WDataInP const lwp) VL_MT_SAFE {
-    EData carry = 1;
-    for (int i = 0; i < words; ++i) {
-        owp[i] = ~lwp[i] + carry;
-        carry = (owp[i] < ~lwp[i]);
-    }
-    return owp;
-}
-static inline void VL_NEGATE_INPLACE_W(int words, WDataOutP owp_lwp) VL_MT_SAFE {
-    EData carry = 1;
-    for (int i = 0; i < words; ++i) {
-        const EData word = ~owp_lwp[i] + carry;
-        carry = (word < ~owp_lwp[i]);
-        owp_lwp[i] = word;
-    }
-}
-
-// EMIT_RULE: VL_MUL:    oclean=dirty; lclean==clean; rclean==clean;
-// EMIT_RULE: VL_DIV:    oclean=dirty; lclean==clean; rclean==clean;
-// EMIT_RULE: VL_MODDIV: oclean=dirty; lclean==clean; rclean==clean;
-static inline IData VL_DIV_III(int lbits, IData lhs, IData rhs) {
-    return (rhs == 0) ? 0 : lhs / rhs;
-}
-static inline QData VL_DIV_QQQ(int lbits, QData lhs, QData rhs) {
-    return (rhs == 0) ? 0 : lhs / rhs;
-}
-#define VL_DIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 0))
-static inline IData VL_MODDIV_III(int lbits, IData lhs, IData rhs) {
-    return (rhs == 0) ? 0 : lhs % rhs;
-}
-static inline QData VL_MODDIV_QQQ(int lbits, QData lhs, QData rhs) {
-    return (rhs == 0) ? 0 : lhs % rhs;
-}
-#define VL_MODDIV_WWW(lbits, owp, lwp, rwp) (_vl_moddiv_w(lbits, owp, lwp, rwp, 1))
-
-static inline WDataOutP VL_ADD_W(int words, WDataOutP owp, WDataInP const lwp,
-                                 WDataInP const rwp) VL_MT_SAFE {
-    QData carry = 0;
-    for (int i = 0; i < words; ++i) {
-        carry = carry + static_cast(lwp[i]) + static_cast(rwp[i]);
-        owp[i] = (carry & 0xffffffffULL);
-        carry = (carry >> 32ULL) & 0xffffffffULL;
-    }
-    // Last output word is dirty
-    return owp;
-}
-
-static inline WDataOutP VL_SUB_W(int words, WDataOutP owp, WDataInP const lwp,
-                                 WDataInP const rwp) VL_MT_SAFE {
-    QData carry = 0;
-    for (int i = 0; i < words; ++i) {
-        carry = (carry + static_cast(lwp[i])
-                 + static_cast(static_cast(~rwp[i])));
-        if (i == 0) ++carry;  // Negation of rwp
-        owp[i] = (carry & 0xffffffffULL);
-        carry = (carry >> 32ULL) & 0xffffffffULL;
-    }
-    // Last output word is dirty
-    return owp;
-}
-
-static inline WDataOutP VL_MUL_W(int words, WDataOutP owp, WDataInP const lwp,
-                                 WDataInP const rwp) VL_MT_SAFE {
-    for (int i = 0; i < words; ++i) owp[i] = 0;
-    for (int lword = 0; lword < words; ++lword) {
-        for (int rword = 0; rword < words; ++rword) {
-            QData mul = static_cast(lwp[lword]) * static_cast(rwp[rword]);
-            for (int qword = lword + rword; qword < words; ++qword) {
-                mul += static_cast(owp[qword]);
-                owp[qword] = (mul & 0xffffffffULL);
-                mul = (mul >> 32ULL) & 0xffffffffULL;
-            }
-        }
-    }
-    // Last output word is dirty
-    return owp;
-}
-
-static inline IData VL_MULS_III(int lbits, IData lhs, IData rhs) VL_PURE {
-    const int32_t lhs_signed = VL_EXTENDS_II(32, lbits, lhs);
-    const int32_t rhs_signed = VL_EXTENDS_II(32, lbits, rhs);
-    return lhs_signed * rhs_signed;
-}
-static inline QData VL_MULS_QQQ(int lbits, QData lhs, QData rhs) VL_PURE {
-    const int64_t lhs_signed = VL_EXTENDS_QQ(64, lbits, lhs);
-    const int64_t rhs_signed = VL_EXTENDS_QQ(64, lbits, rhs);
-    return lhs_signed * rhs_signed;
-}
-
-static inline WDataOutP VL_MULS_WWW(int lbits, WDataOutP owp, WDataInP const lwp,
-                                    WDataInP const rwp) VL_MT_SAFE {
-    const int words = VL_WORDS_I(lbits);
-    VL_DEBUG_IFDEF(assert(words <= VL_MULS_MAX_WORDS););
-    // cppcheck-suppress variableScope
-    WData lwstore[VL_MULS_MAX_WORDS];  // Fixed size, as MSVC++ doesn't allow [words] here
-    // cppcheck-suppress variableScope
-    WData rwstore[VL_MULS_MAX_WORDS];
-    WDataInP lwusp = lwp;
-    WDataInP rwusp = rwp;
-    const EData lneg = VL_SIGN_E(lbits, lwp[words - 1]);
-    if (lneg) {  // Negate lhs
-        lwusp = lwstore;
-        VL_NEGATE_W(words, lwstore, lwp);
-        lwstore[words - 1] &= VL_MASK_E(lbits);  // Clean it
-    }
-    const EData rneg = VL_SIGN_E(lbits, rwp[words - 1]);
-    if (rneg) {  // Negate rhs
-        rwusp = rwstore;
-        VL_NEGATE_W(words, rwstore, rwp);
-        rwstore[words - 1] &= VL_MASK_E(lbits);  // Clean it
-    }
-    VL_MUL_W(words, owp, lwusp, rwusp);
-    owp[words - 1] &= VL_MASK_E(
-        lbits);  // Clean.  Note it's ok for the multiply to overflow into the sign bit
-    if ((lneg ^ rneg) & 1) {  // Negate output (not using NEGATE, as owp==lwp)
-        QData carry = 0;
-        for (int i = 0; i < words; ++i) {
-            carry = carry + static_cast(static_cast(~owp[i]));
-            if (i == 0) ++carry;  // Negation of temp2
-            owp[i] = (carry & 0xffffffffULL);
-            carry = (carry >> 32ULL) & 0xffffffffULL;
-        }
-        // Not needed: owp[words-1] |= 1< 0) power = power * power;
-        if (rhs & (1ULL << i)) out *= power;
-    }
-    return out;
-}
-static inline QData VL_POW_QQQ(int, int, int rbits, QData lhs, QData rhs) VL_PURE {
-    if (VL_UNLIKELY(rhs == 0)) return 1;
-    if (VL_UNLIKELY(lhs == 0)) return 0;
-    QData power = lhs;
-    QData out = 1ULL;
-    for (int i = 0; i < rbits; ++i) {
-        if (i > 0) power = power * power;
-        if (rhs & (1ULL << i)) out *= power;
-    }
-    return out;
-}
-WDataOutP VL_POW_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp,
-                     WDataInP const rwp) VL_MT_SAFE;
-WDataOutP VL_POW_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp,
-                     QData rhs) VL_MT_SAFE;
-QData VL_POW_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp) VL_MT_SAFE;
-
-#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \
-    VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign)
-#define VL_POWSS_IIQ(obits, lbits, rbits, lhs, rhs, lsign, rsign) \
-    VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign)
-#define VL_POWSS_IIW(obits, lbits, rbits, lhs, rwp, lsign, rsign) \
-    VL_POWSS_QQW(obits, lbits, rbits, lhs, rwp, lsign, rsign)
-#define VL_POWSS_QQI(obits, lbits, rbits, lhs, rhs, lsign, rsign) \
-    VL_POWSS_QQQ(obits, lbits, rbits, lhs, rhs, lsign, rsign)
-#define VL_POWSS_WWI(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign) \
-    VL_POWSS_WWQ(obits, lbits, rbits, owp, lwp, rhs, lsign, rsign)
-
-static inline IData VL_POWSS_III(int obits, int, int rbits, IData lhs, IData rhs, bool lsign,
-                                 bool rsign) VL_MT_SAFE {
-    if (VL_UNLIKELY(rhs == 0)) return 1;
-    if (rsign && VL_SIGN_I(rbits, rhs)) {
-        if (lhs == 0) {
-            return 0;  // "X"
-        } else if (lhs == 1) {
-            return 1;
-        } else if (lsign && lhs == VL_MASK_I(obits)) {  // -1
-            if (rhs & 1) {
-                return VL_MASK_I(obits);  // -1^odd=-1
-            } else {
-                return 1;  // -1^even=1
-            }
-        }
-        return 0;
-    }
-    return VL_POW_III(obits, rbits, rbits, lhs, rhs);
-}
-static inline QData VL_POWSS_QQQ(int obits, int, int rbits, QData lhs, QData rhs, bool lsign,
-                                 bool rsign) VL_MT_SAFE {
-    if (VL_UNLIKELY(rhs == 0)) return 1;
-    if (rsign && VL_SIGN_Q(rbits, rhs)) {
-        if (lhs == 0) {
-            return 0;  // "X"
-        } else if (lhs == 1) {
-            return 1;
-        } else if (lsign && lhs == VL_MASK_Q(obits)) {  // -1
-            if (rhs & 1) {
-                return VL_MASK_Q(obits);  // -1^odd=-1
-            } else {
-                return 1;  // -1^even=1
-            }
-        }
-        return 0;
-    }
-    return VL_POW_QQQ(obits, rbits, rbits, lhs, rhs);
-}
-WDataOutP VL_POWSS_WWW(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp,
-                       WDataInP const rwp, bool lsign, bool rsign) VL_MT_SAFE;
-WDataOutP VL_POWSS_WWQ(int obits, int, int rbits, WDataOutP owp, WDataInP const lwp, QData rhs,
-                       bool lsign, bool rsign) VL_MT_SAFE;
-QData VL_POWSS_QQW(int obits, int, int rbits, QData lhs, WDataInP const rwp, bool lsign,
-                   bool rsign) VL_MT_SAFE;
-
-//===================================================================
-// Concat/replication
-
-// INTERNAL: Stuff LHS bit 0++ into OUTPUT at specified offset
-// ld may be "dirty", output is clean
-static inline void _vl_insert_II(CData& lhsr, IData ld, int hbit, int lbit, int rbits) VL_PURE {
-    const IData cleanmask = VL_MASK_I(rbits);
-    const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit;
-    lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask));
-}
-static inline void _vl_insert_II(SData& lhsr, IData ld, int hbit, int lbit, int rbits) VL_PURE {
-    const IData cleanmask = VL_MASK_I(rbits);
-    const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit;
-    lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask));
-}
-static inline void _vl_insert_II(IData& lhsr, IData ld, int hbit, int lbit, int rbits) VL_PURE {
-    const IData cleanmask = VL_MASK_I(rbits);
-    const IData insmask = (VL_MASK_I(hbit - lbit + 1)) << lbit;
-    lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask));
-}
-static inline void _vl_insert_QQ(QData& lhsr, QData ld, int hbit, int lbit, int rbits) VL_PURE {
-    const QData cleanmask = VL_MASK_Q(rbits);
-    const QData insmask = (VL_MASK_Q(hbit - lbit + 1)) << lbit;
-    lhsr = (lhsr & ~insmask) | ((ld << lbit) & (insmask & cleanmask));
-}
-static inline void _vl_insert_WI(WDataOutP iowp, IData ld, int hbit, int lbit,
-                                 int rbits = 0) VL_MT_SAFE {
-    // Insert value ld into iowp at bit slice [hbit:lbit]. iowp is rbits wide.
-    const int hoffset = VL_BITBIT_E(hbit);
-    const int loffset = VL_BITBIT_E(lbit);
-    const int roffset = VL_BITBIT_E(rbits);
-    const int hword = VL_BITWORD_E(hbit);
-    const int lword = VL_BITWORD_E(lbit);
-    const int rword = VL_BITWORD_E(rbits);
-    const EData cleanmask = hword == rword ? VL_MASK_E(roffset) : VL_MASK_E(0);
-
-    if (hoffset == VL_SIZEBITS_E && loffset == 0) {
-        // Fast and common case, word based insertion
-        iowp[lword] = ld & cleanmask;
-    } else {
-        const EData lde = static_cast(ld);
-        if (hword == lword) {  // know < EData bits because above checks it
-            // Assignment is contained within one word of destination
-            const EData insmask = (VL_MASK_E(hoffset - loffset + 1)) << loffset;
-            iowp[lword] = (iowp[lword] & ~insmask) | ((lde << loffset) & (insmask & cleanmask));
-        } else {
-            // Assignment crosses a word boundary in destination
-            const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0;
-            const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset;
-            const int nbitsonright = VL_EDATASIZE - loffset;  // bits that end up in lword
-            iowp[lword] = (iowp[lword] & ~linsmask) | ((lde << loffset) & linsmask);
-            // Prevent unsafe write where lword was final writable location and hword is
-            // out-of-bounds.
-            if (VL_LIKELY(!(hword == rword && roffset == 0))) {
-                iowp[hword]
-                    = (iowp[hword] & ~hinsmask) | ((lde >> nbitsonright) & (hinsmask & cleanmask));
-            }
-        }
-    }
-}
-
-// Copy bits from lwp[hbit:lbit] to low bits of lhsr. rbits is real width of lshr
-static inline void _vl_insert_IW(IData& lhsr, WDataInP const lwp, int hbit, int lbit,
-                                 int rbits = 0) VL_MT_SAFE {
-    const int hoffset = VL_BITBIT_E(hbit);
-    const int loffset = VL_BITBIT_E(lbit);
-    const int hword = VL_BITWORD_E(hbit);
-    const int lword = VL_BITWORD_E(lbit);
-    const IData cleanmask = VL_MASK_I(rbits);
-    if (hword == lword) {
-        const IData insmask = (VL_MASK_I(hoffset - loffset + 1));
-        lhsr = (lhsr & ~insmask) | ((lwp[lword] >> loffset) & (insmask & cleanmask));
-    } else {
-        const int nbitsonright = VL_IDATASIZE - loffset;  // bits that filled by lword
-        const IData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << nbitsonright;
-        const IData linsmask = VL_MASK_E(VL_EDATASIZE - loffset);
-        lhsr = (lhsr & ~linsmask) | ((lwp[lword] >> loffset) & (linsmask & cleanmask));
-        lhsr = (lhsr & ~hinsmask) | ((lwp[hword] << nbitsonright) & (hinsmask & cleanmask));
-    }
-}
-
-// INTERNAL: Stuff large LHS bit 0++ into OUTPUT at specified offset
-// lwp may be "dirty"
-static inline void _vl_insert_WW(WDataOutP iowp, WDataInP const lwp, int hbit, int lbit,
-                                 int rbits = 0) VL_MT_SAFE {
-    const int hoffset = VL_BITBIT_E(hbit);
-    const int loffset = VL_BITBIT_E(lbit);
-    const int roffset = VL_BITBIT_E(rbits);
-    const int lword = VL_BITWORD_E(lbit);
-    const int hword = VL_BITWORD_E(hbit);
-    const int rword = VL_BITWORD_E(rbits);
-    const int words = VL_WORDS_I(hbit - lbit + 1);
-    // Cleaning mask, only applied to top word of the assignment.  Is a no-op
-    // if we don't assign to the top word of the destination.
-    const EData cleanmask = hword == rword ? VL_MASK_E(roffset) : VL_MASK_E(0);
-
-    if (hoffset == VL_SIZEBITS_E && loffset == 0) {
-        // Fast and common case, word based insertion
-        for (int i = 0; i < (words - 1); ++i) iowp[lword + i] = lwp[i];
-        iowp[hword] = lwp[words - 1] & cleanmask;
-    } else if (loffset == 0) {
-        // Non-32bit, but nicely aligned, so stuff all but the last word
-        for (int i = 0; i < (words - 1); ++i) iowp[lword + i] = lwp[i];
-        // Know it's not a full word as above fast case handled it
-        const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1));
-        iowp[hword] = (iowp[hword] & ~hinsmask) | (lwp[words - 1] & (hinsmask & cleanmask));
-    } else {
-        const EData hinsmask = (VL_MASK_E(hoffset - 0 + 1)) << 0;
-        const EData linsmask = (VL_MASK_E((VL_EDATASIZE - 1) - loffset + 1)) << loffset;
-        const int nbitsonright
-            = VL_EDATASIZE - loffset;  // bits that end up in lword (know loffset!=0)
-        // Middle words
-        for (int i = 0; i < words; ++i) {
-            {  // Lower word
-                const int oword = lword + i;
-                const EData d = lwp[i] << loffset;
-                const EData od = (iowp[oword] & ~linsmask) | (d & linsmask);
-                if (oword == hword) {
-                    iowp[oword] = (iowp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask));
-                } else {
-                    iowp[oword] = od;
-                }
-            }
-            {  // Upper word
-                const int oword = lword + i + 1;
-                if (oword <= hword) {
-                    const EData d = lwp[i] >> nbitsonright;
-                    const EData od = (d & ~linsmask) | (iowp[oword] & linsmask);
-                    if (oword == hword) {
-                        iowp[oword] = (iowp[oword] & ~hinsmask) | (od & (hinsmask & cleanmask));
-                    } else {
-                        iowp[oword] = od;
-                    }
-                }
-            }
-        }
-    }
-}
-
-static inline void _vl_insert_WQ(WDataOutP iowp, QData ld, int hbit, int lbit,
-                                 int rbits = 0) VL_MT_SAFE {
-    VlWide lwp;
-    VL_SET_WQ(lwp, ld);
-    _vl_insert_WW(iowp, lwp, hbit, lbit, rbits);
-}
-
-// EMIT_RULE: VL_REPLICATE:  oclean=clean>width32, dirty<=width32; lclean=clean; rclean==clean;
-// RHS MUST BE CLEAN CONSTANT.
-#define VL_REPLICATE_IOI(lbits, ld, rep) (-(ld))  // Iff lbits==1
-#define VL_REPLICATE_QOI(lbits, ld, rep) (-(static_cast(ld)))  // Iff lbits==1
-
-static inline IData VL_REPLICATE_III(int lbits, IData ld, IData rep) VL_PURE {
-    IData returndata = ld;
-    for (unsigned i = 1; i < rep; ++i) {
-        returndata = returndata << lbits;
-        returndata |= ld;
-    }
-    return returndata;
-}
-static inline QData VL_REPLICATE_QII(int lbits, IData ld, IData rep) VL_PURE {
-    QData returndata = ld;
-    for (unsigned i = 1; i < rep; ++i) {
-        returndata = returndata << lbits;
-        returndata |= static_cast(ld);
-    }
-    return returndata;
-}
-static inline WDataOutP VL_REPLICATE_WII(int lbits, WDataOutP owp, IData ld,
-                                         IData rep) VL_MT_SAFE {
-    owp[0] = ld;
-    // Zeroing all words isn't strictly needed but allows compiler to know
-    // it does not need to preserve data in word(s) not being written
-    for (unsigned i = 1; i < VL_WORDS_I(static_cast(lbits) * rep); ++i) owp[i] = 0;
-    for (unsigned i = 1; i < rep; ++i) {
-        _vl_insert_WI(owp, ld, i * lbits + lbits - 1, i * lbits);
-    }
-    return owp;
-}
-static inline WDataOutP VL_REPLICATE_WQI(int lbits, WDataOutP owp, QData ld,
-                                         IData rep) VL_MT_SAFE {
-    VL_SET_WQ(owp, ld);
-    // Zeroing all words isn't strictly needed but allows compiler to know
-    // it does not need to preserve data in word(s) not being written
-    for (unsigned i = 2; i < VL_WORDS_I(static_cast(lbits) * rep); ++i) owp[i] = 0;
-    for (unsigned i = 1; i < rep; ++i) {
-        _vl_insert_WQ(owp, ld, i * lbits + lbits - 1, i * lbits);
-    }
-    return owp;
-}
-static inline WDataOutP VL_REPLICATE_WWI(int lbits, WDataOutP owp, WDataInP const lwp,
-                                         IData rep) VL_MT_SAFE {
-    for (unsigned i = 0; i < VL_WORDS_I(static_cast(lbits)); ++i) owp[i] = lwp[i];
-    // Zeroing all words isn't strictly needed but allows compiler to know
-    // it does not need to preserve data in word(s) not being written
-    for (unsigned i = VL_WORDS_I(static_cast(lbits));
-         i < VL_WORDS_I(static_cast(lbits * rep)); ++i)
-        owp[i] = 0;
-    for (unsigned i = 1; i < rep; ++i) {
-        _vl_insert_WW(owp, lwp, i * lbits + lbits - 1, i * lbits);
-    }
-    return owp;
-}
-
-// Left stream operator. Output will always be clean. LHS and RHS must be clean.
-// Special "fast" versions for slice sizes that are a power of 2. These use
-// shifts and masks to execute faster than the slower for-loop approach where a
-// subset of bits is copied in during each iteration.
-static inline IData VL_STREAML_FAST_III(int lbits, IData ld, IData rd_log2) VL_PURE {
-    // Pre-shift bits in most-significant slice:
-    //
-    // If lbits is not a multiple of the slice size (i.e., lbits % rd != 0),
-    // then we end up with a "gap" in our reversed result. For example, if we
-    // have a 5-bit Verilog signal (lbits=5) in an 8-bit C data type:
-    //
-    //   ld = ---43210
-    //
-    // (where numbers are the Verilog signal bit numbers and '-' is an unused bit).
-    // Executing the switch statement below with a slice size of two (rd=2,
-    // rd_log2=1) produces:
-    //
-    //   ret = 1032-400
-    //
-    // Pre-shifting the bits in the most-significant slice allows us to avoid
-    // this gap in the shuffled data:
-    //
-    //   ld_adjusted = --4-3210
-    //   ret = 10324---
-    IData ret = ld;
-    if (rd_log2) {
-        const uint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2);  // max multiple of rd <= lbits
-        const uint32_t lbitsRem = lbits - lbitsFloor;  // number of bits in most-sig slice (MSS)
-        const IData msbMask = lbitsFloor == 32 ? 0UL : VL_MASK_I(lbitsRem) << lbitsFloor;
-        ret = (ret & ~msbMask) | ((ret & msbMask) << ((VL_UL(1) << rd_log2) - lbitsRem));
-    }
-    switch (rd_log2) {
-    case 0: ret = ((ret >> 1) & VL_UL(0x55555555)) | ((ret & VL_UL(0x55555555)) << 1);  // FALLTHRU
-    case 1: ret = ((ret >> 2) & VL_UL(0x33333333)) | ((ret & VL_UL(0x33333333)) << 2);  // FALLTHRU
-    case 2: ret = ((ret >> 4) & VL_UL(0x0f0f0f0f)) | ((ret & VL_UL(0x0f0f0f0f)) << 4);  // FALLTHRU
-    case 3: ret = ((ret >> 8) & VL_UL(0x00ff00ff)) | ((ret & VL_UL(0x00ff00ff)) << 8);  // FALLTHRU
-    case 4: ret = ((ret >> 16) | (ret << 16));  // FALLTHRU
-    default:;
-    }
-    return ret >> (VL_IDATASIZE - lbits);
-}
-
-static inline QData VL_STREAML_FAST_QQI(int lbits, QData ld, IData rd_log2) VL_PURE {
-    // Pre-shift bits in most-significant slice (see comment in VL_STREAML_FAST_III)
-    QData ret = ld;
-    if (rd_log2) {
-        const uint32_t lbitsFloor = lbits & ~VL_MASK_I(rd_log2);
-        const uint32_t lbitsRem = lbits - lbitsFloor;
-        const QData msbMask = lbitsFloor == 64 ? 0ULL : VL_MASK_Q(lbitsRem) << lbitsFloor;
-        ret = (ret & ~msbMask) | ((ret & msbMask) << ((1ULL << rd_log2) - lbitsRem));
-    }
-    switch (rd_log2) {
-    case 0:
-        ret = (((ret >> 1) & 0x5555555555555555ULL)
-               | ((ret & 0x5555555555555555ULL) << 1));  // FALLTHRU
-    case 1:
-        ret = (((ret >> 2) & 0x3333333333333333ULL)
-               | ((ret & 0x3333333333333333ULL) << 2));  // FALLTHRU
-    case 2:
-        ret = (((ret >> 4) & 0x0f0f0f0f0f0f0f0fULL)
-               | ((ret & 0x0f0f0f0f0f0f0f0fULL) << 4));  // FALLTHRU
-    case 3:
-        ret = (((ret >> 8) & 0x00ff00ff00ff00ffULL)
-               | ((ret & 0x00ff00ff00ff00ffULL) << 8));  // FALLTHRU
-    case 4:
-        ret = (((ret >> 16) & 0x0000ffff0000ffffULL)
-               | ((ret & 0x0000ffff0000ffffULL) << 16));  // FALLTHRU
-    case 5: ret = ((ret >> 32) | (ret << 32));  // FALLTHRU
-    default:;
-    }
-    return ret >> (VL_QUADSIZE - lbits);
-}
-
-// Regular "slow" streaming operators
-static inline IData VL_STREAML_III(int lbits, IData ld, IData rd) VL_PURE {
-    IData ret = 0;
-    // Slice size should never exceed the lhs width
-    const IData mask = VL_MASK_I(rd);
-    for (int istart = 0; istart < lbits; istart += rd) {
-        int ostart = lbits - rd - istart;
-        ostart = ostart > 0 ? ostart : 0;
-        ret |= ((ld >> istart) & mask) << ostart;
-    }
-    return ret;
-}
-
-static inline QData VL_STREAML_QQI(int lbits, QData ld, IData rd) VL_PURE {
-    QData ret = 0;
-    // Slice size should never exceed the lhs width
-    const QData mask = VL_MASK_Q(rd);
-    for (int istart = 0; istart < lbits; istart += rd) {
-        int ostart = lbits - rd - istart;
-        ostart = ostart > 0 ? ostart : 0;
-        ret |= ((ld >> istart) & mask) << ostart;
-    }
-    return ret;
-}
-
-static inline WDataOutP VL_STREAML_WWI(int lbits, WDataOutP owp, WDataInP const lwp,
-                                       IData rd) VL_MT_SAFE {
-    VL_ZERO_W(lbits, owp);
-    // Slice size should never exceed the lhs width
-    const int ssize = (rd < static_cast(lbits)) ? rd : (static_cast(lbits));
-    for (int istart = 0; istart < lbits; istart += rd) {
-        int ostart = lbits - rd - istart;
-        ostart = ostart > 0 ? ostart : 0;
-        for (int sbit = 0; sbit < ssize && sbit < lbits - istart; ++sbit) {
-            // Extract a single bit from lwp and shift it to the correct
-            // location for owp.
-            const EData bit = (VL_BITRSHIFT_W(lwp, (istart + sbit)) & 1)
-                              << VL_BITBIT_E(ostart + sbit);
-            owp[VL_BITWORD_E(ostart + sbit)] |= bit;
-        }
-    }
-    return owp;
-}
-
-static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue& q) {
-    IData ret = 0;
-    for (size_t i = 0; i < q.size(); ++i)
-        ret |= static_cast(q.at(q.size() - 1 - i)) << (i * lbits);
-    return ret;
-}
-
-static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue& q) {
-    IData ret = 0;
-    for (size_t i = 0; i < q.size(); ++i)
-        ret |= static_cast(q.at(q.size() - 1 - i)) << (i * lbits);
-    return ret;
-}
-
-static inline IData VL_PACK_I_RI(int obits, int lbits, const VlQueue& q) {
-    IData ret = 0;
-    for (size_t i = 0; i < q.size(); ++i) ret |= q.at(q.size() - 1 - i) << (i * lbits);
-    return ret;
-}
-
-template 
-static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked& q) {
-    IData ret = 0;
-    for (size_t i = 0; i < N_Depth; ++i)
-        ret |= static_cast(q[N_Depth - 1 - i]) << (i * lbits);
-    return ret;
-}
-
-template 
-static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked& q) {
-    IData ret = 0;
-    for (size_t i = 0; i < N_Depth; ++i)
-        ret |= static_cast(q[N_Depth - 1 - i]) << (i * lbits);
-    return ret;
-}
-
-template 
-static inline IData VL_PACK_I_UI(int obits, int lbits, const VlUnpacked& q) {
-    IData ret = 0;
-    for (size_t i = 0; i < N_Depth; ++i) ret |= q[N_Depth - 1 - i] << (i * lbits);
-    return ret;
-}
-
-static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue& q) {
-    QData ret = 0;
-    for (size_t i = 0; i < q.size(); ++i)
-        ret |= static_cast(q.at(q.size() - 1 - i)) << (i * lbits);
-    return ret;
-}
-
-static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue& q) {
-    QData ret = 0;
-    for (size_t i = 0; i < q.size(); ++i)
-        ret |= static_cast(q.at(q.size() - 1 - i)) << (i * lbits);
-    return ret;
-}
-
-static inline QData VL_PACK_Q_RI(int obits, int lbits, const VlQueue& q) {
-    QData ret = 0;
-    for (size_t i = 0; i < q.size(); ++i)
-        ret |= static_cast(q.at(q.size() - 1 - i)) << (i * lbits);
-    return ret;
-}
-
-template 
-static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked& q) {
-    QData ret = 0;
-    for (size_t i = 0; i < N_Depth; ++i)
-        ret |= static_cast(q[N_Depth - 1 - i]) << (i * lbits);
-    return ret;
-}
-
-template 
-static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked& q) {
-    QData ret = 0;
-    for (size_t i = 0; i < N_Depth; ++i)
-        ret |= static_cast(q[N_Depth - 1 - i]) << (i * lbits);
-    return ret;
-}
-
-template 
-static inline QData VL_PACK_Q_UI(int obits, int lbits, const VlUnpacked& q) {
-    QData ret = 0;
-    for (size_t i = 0; i < N_Depth; ++i)
-        ret |= static_cast(q[N_Depth - 1 - i]) << (i * lbits);
-    return ret;
-}
-
-static inline QData VL_PACK_Q_RQ(int obits, int lbits, const VlQueue& q) {
-    QData ret = 0;
-    for (size_t i = 0; i < q.size(); ++i) ret |= q.at(q.size() - 1 - i) << (i * lbits);
-    return ret;
-}
-
-template 
-static inline QData VL_PACK_Q_UQ(int obits, int lbits, const VlUnpacked& q) {
-    QData ret = 0;
-    for (size_t i = 0; i < N_Depth; ++i) ret |= q[N_Depth - 1 - i] << (i * lbits);
-    return ret;
-}
-
-static inline WDataOutP VL_PACK_W_RI(int obits, int lbits, WDataOutP owp,
-                                     const VlQueue& q) {
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    if (VL_UNLIKELY(obits < q.size() * lbits)) return owp;  // Though is illegal for q to be larger
-    const int offset = obits - q.size() * lbits;
-    for (size_t i = 0; i < q.size(); ++i)
-        _vl_insert_WI(owp, q.at(q.size() - i - 1), i * lbits + lbits - 1 + offset,
-                      i * lbits + offset);
-    return owp;
-}
-
-static inline WDataOutP VL_PACK_W_RI(int obits, int lbits, WDataOutP owp,
-                                     const VlQueue& q) {
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    if (VL_UNLIKELY(obits < q.size() * lbits)) return owp;  // Though is illegal for q to be larger
-    const int offset = obits - q.size() * lbits;
-    for (size_t i = 0; i < q.size(); ++i)
-        _vl_insert_WI(owp, q.at(q.size() - i - 1), i * lbits + lbits - 1 + offset,
-                      i * lbits + offset);
-    return owp;
-}
-
-static inline WDataOutP VL_PACK_W_RI(int obits, int lbits, WDataOutP owp,
-                                     const VlQueue& q) {
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    if (VL_UNLIKELY(obits < q.size() * lbits)) return owp;  // Though is illegal for q to be larger
-    const int offset = obits - q.size() * lbits;
-    for (size_t i = 0; i < q.size(); ++i)
-        _vl_insert_WI(owp, q.at(q.size() - 1 - i), i * lbits + lbits - 1 + offset,
-                      i * lbits + offset);
-    return owp;
-}
-
-template 
-static inline WDataOutP VL_PACK_W_UI(int obits, int lbits, WDataOutP owp,
-                                     const VlUnpacked& q) {
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    for (size_t i = 0; i < N_Depth; ++i)
-        _vl_insert_WI(owp, q[N_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
-    return owp;
-}
-
-template 
-static inline WDataOutP VL_PACK_W_UI(int obits, int lbits, WDataOutP owp,
-                                     const VlUnpacked& q) {
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    for (size_t i = 0; i < N_Depth; ++i)
-        _vl_insert_WI(owp, q[N_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
-    return owp;
-}
-
-template 
-static inline WDataOutP VL_PACK_W_UI(int obits, int lbits, WDataOutP owp,
-                                     const VlUnpacked& q) {
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    for (size_t i = 0; i < N_Depth; ++i)
-        _vl_insert_WI(owp, q[N_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
-    return owp;
-}
-
-static inline WDataOutP VL_PACK_W_RQ(int obits, int lbits, WDataOutP owp,
-                                     const VlQueue& q) {
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    if (VL_UNLIKELY(obits < q.size() * lbits)) return owp;  // Though is illegal for q to be larger
-    const int offset = obits - q.size() * lbits;
-    for (size_t i = 0; i < q.size(); ++i)
-        _vl_insert_WQ(owp, q.at(q.size() - 1 - i), i * lbits + lbits - 1 + offset,
-                      i * lbits + offset);
-    return owp;
-}
-
-template 
-static inline WDataOutP VL_PACK_W_UQ(int obits, int lbits, WDataOutP owp,
-                                     const VlUnpacked& q) {
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    for (size_t i = 0; i < N_Depth; ++i)
-        _vl_insert_WQ(owp, q[N_Depth - 1 - i], i * lbits + lbits - 1, i * lbits);
-    return owp;
-}
-
-template 
-static inline WDataOutP VL_PACK_W_RW(int obits, int lbits, WDataOutP owp,
-                                     const VlQueue>& q) {
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    if (VL_UNLIKELY(obits < q.size() * lbits)) return owp;  // Though is illegal for q to be larger
-    const int offset = obits - q.size() * lbits;
-    for (size_t i = 0; i < q.size(); ++i)
-        _vl_insert_WW(owp, q.at(q.size() - 1 - i), i * lbits + lbits - 1 + offset,
-                      i * lbits + offset);
-    return owp;
-}
-
-template 
-static inline WDataOutP VL_PACK_W_UW(int obits, int lbits, WDataOutP owp,
-                                     const VlUnpacked, N_Depth>& q) {
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    if (VL_UNLIKELY(obits < q.size() * lbits)) return owp;  // Though is illegal for q to be larger
-    const int offset = obits - q.size() * lbits;
-    for (size_t i = 0; i < N_Depth; ++i)
-        _vl_insert_WW(owp, q[N_Depth - 1 - i], i * lbits + lbits - 1 + offset, i * lbits + offset);
-    return owp;
-}
-
-// Because concats are common and wide, it's valuable to always have a clean output.
-// Thus we specify inputs must be clean, so we don't need to clean the output.
-// Note the bit shifts are always constants, so the adds in these constify out.
-// Casts required, as args may be 8 bit entities, and need to shift to appropriate output size
-#define VL_CONCAT_III(obits, lbits, rbits, ld, rd) \
-    (static_cast(ld) << (rbits) | static_cast(rd))
-#define VL_CONCAT_QII(obits, lbits, rbits, ld, rd) \
-    (static_cast(ld) << (rbits) | static_cast(rd))
-#define VL_CONCAT_QIQ(obits, lbits, rbits, ld, rd) \
-    (static_cast(ld) << (rbits) | static_cast(rd))
-#define VL_CONCAT_QQI(obits, lbits, rbits, ld, rd) \
-    (static_cast(ld) << (rbits) | static_cast(rd))
-#define VL_CONCAT_QQQ(obits, lbits, rbits, ld, rd) \
-    (static_cast(ld) << (rbits) | static_cast(rd))
-
-static inline WDataOutP VL_CONCAT_WII(int obits, int lbits, int rbits, WDataOutP owp, IData ld,
-                                      IData rd) VL_MT_SAFE {
-    owp[0] = rd;
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    _vl_insert_WI(owp, ld, rbits + lbits - 1, rbits);
-    return owp;
-}
-static inline WDataOutP VL_CONCAT_WWI(int obits, int lbits, int rbits, WDataOutP owp,
-                                      WDataInP const lwp, IData rd) VL_MT_SAFE {
-    owp[0] = rd;
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    _vl_insert_WW(owp, lwp, rbits + lbits - 1, rbits);
-    return owp;
-}
-static inline WDataOutP VL_CONCAT_WIW(int obits, int lbits, int rbits, WDataOutP owp, IData ld,
-                                      WDataInP const rwp) VL_MT_SAFE {
-    const int rwords = VL_WORDS_I(rbits);
-    VL_MEMCPY_W(owp, rwp, rwords);
-    VL_MEMSET_ZERO_W(owp + rwords, VL_WORDS_I(obits) - rwords);
-    _vl_insert_WI(owp, ld, rbits + lbits - 1, rbits);
-    return owp;
-}
-static inline WDataOutP VL_CONCAT_WIQ(int obits, int lbits, int rbits, WDataOutP owp, IData ld,
-                                      QData rd) VL_MT_SAFE {
-    VL_SET_WQ(owp, rd);
-    VL_MEMSET_ZERO_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E);
-    _vl_insert_WI(owp, ld, rbits + lbits - 1, rbits);
-    return owp;
-}
-static inline WDataOutP VL_CONCAT_WQI(int obits, int lbits, int rbits, WDataOutP owp, QData ld,
-                                      IData rd) VL_MT_SAFE {
-    owp[0] = rd;
-    VL_MEMSET_ZERO_W(owp + 1, VL_WORDS_I(obits) - 1);
-    _vl_insert_WQ(owp, ld, rbits + lbits - 1, rbits);
-    return owp;
-}
-static inline WDataOutP VL_CONCAT_WQQ(int obits, int lbits, int rbits, WDataOutP owp, QData ld,
-                                      QData rd) VL_MT_SAFE {
-    VL_SET_WQ(owp, rd);
-    VL_MEMSET_ZERO_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E);
-    _vl_insert_WQ(owp, ld, rbits + lbits - 1, rbits);
-    return owp;
-}
-static inline WDataOutP VL_CONCAT_WWQ(int obits, int lbits, int rbits, WDataOutP owp,
-                                      WDataInP const lwp, QData rd) VL_MT_SAFE {
-    VL_SET_WQ(owp, rd);
-    VL_MEMSET_ZERO_W(owp + VL_WQ_WORDS_E, VL_WORDS_I(obits) - VL_WQ_WORDS_E);
-    _vl_insert_WW(owp, lwp, rbits + lbits - 1, rbits);
-    return owp;
-}
-static inline WDataOutP VL_CONCAT_WQW(int obits, int lbits, int rbits, WDataOutP owp, QData ld,
-                                      WDataInP const rwp) VL_MT_SAFE {
-    const int rwords = VL_WORDS_I(rbits);
-    VL_MEMCPY_W(owp, rwp, rwords);
-    VL_MEMSET_ZERO_W(owp + rwords, VL_WORDS_I(obits) - rwords);
-    _vl_insert_WQ(owp, ld, rbits + lbits - 1, rbits);
-    return owp;
-}
-static inline WDataOutP VL_CONCAT_WWW(int obits, int lbits, int rbits, WDataOutP owp,
-                                      WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE {
-    const int rwords = VL_WORDS_I(rbits);
-    VL_MEMCPY_W(owp, rwp, rwords);
-    VL_MEMSET_ZERO_W(owp + rwords, VL_WORDS_I(obits) - rwords);
-    _vl_insert_WW(owp, lwp, rbits + lbits - 1, rbits);
-    return owp;
-}
-
-//===================================================================
-// Shifts
-
-// Static shift, used by internal functions
-// The output is the same as the input - it overlaps!
-static inline void _vl_shiftl_inplace_w(int obits, WDataOutP iowp,
-                                        IData rd /*1 or 4*/) VL_MT_SAFE {
-    const int words = VL_WORDS_I(obits);
-    const EData linsmask = VL_MASK_E(rd);
-    for (int i = words - 1; i >= 1; --i) {
-        iowp[i]
-            = ((iowp[i] << rd) & ~linsmask) | ((iowp[i - 1] >> (VL_EDATASIZE - rd)) & linsmask);
-    }
-    iowp[0] = ((iowp[0] << rd) & ~linsmask);
-    iowp[VL_WORDS_I(obits) - 1] &= VL_MASK_E(obits);
-}
-
-// EMIT_RULE: VL_SHIFTL:  oclean=lclean; rclean==clean;
-// Important: Unlike most other funcs, the shift might well be a computed
-// expression.  Thus consider this when optimizing.  (And perhaps have 2 funcs?)
-// If RHS (rd/rwp) is larger than the output, zeros (or all ones for >>>) must be returned
-// (This corresponds to AstShift*Ovr Ast nodes)
-static inline IData VL_SHIFTL_III(int obits, int, int, IData lhs, IData rhs) VL_MT_SAFE {
-    if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
-    return lhs << rhs;  // Small is common so not clean return
-}
-static inline IData VL_SHIFTL_IIQ(int obits, int, int, IData lhs, QData rhs) VL_MT_SAFE {
-    if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
-    return VL_CLEAN_II(obits, obits, lhs << rhs);
-}
-static inline QData VL_SHIFTL_QQI(int obits, int, int, QData lhs, IData rhs) VL_MT_SAFE {
-    if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
-    return lhs << rhs;  // Small is common so not clean return
-}
-static inline QData VL_SHIFTL_QQQ(int obits, int, int, QData lhs, QData rhs) VL_MT_SAFE {
-    if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
-    return VL_CLEAN_QQ(obits, obits, lhs << rhs);
-}
-static inline WDataOutP VL_SHIFTL_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp,
-                                      IData rd) VL_MT_SAFE {
-    const int word_shift = VL_BITWORD_E(rd);
-    const int bit_shift = VL_BITBIT_E(rd);
-    if (rd >= static_cast(obits)) {  // rd may be huge with MSB set
-        for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0;
-    } else if (bit_shift == 0) {  // Aligned word shift (<<0,<<32,<<64 etc)
-        for (int i = 0; i < word_shift; ++i) owp[i] = 0;
-        for (int i = word_shift; i < VL_WORDS_I(obits); ++i) owp[i] = lwp[i - word_shift];
-    } else {
-        for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0;
-        _vl_insert_WW(owp, lwp, obits - 1, rd);
-    }
-    return owp;
-}
-static inline WDataOutP VL_SHIFTL_WWW(int obits, int lbits, int rbits, WDataOutP owp,
-                                      WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE {
-    for (int i = 1; i < VL_WORDS_I(rbits); ++i) {
-        if (VL_UNLIKELY(rwp[i])) {  // Huge shift 1>>32 or more
-            return VL_ZERO_W(obits, owp);
-        }
-    }
-    return VL_SHIFTL_WWI(obits, lbits, 32, owp, lwp, rwp[0]);
-}
-static inline WDataOutP VL_SHIFTL_WWQ(int obits, int lbits, int rbits, WDataOutP owp,
-                                      WDataInP const lwp, QData rd) VL_MT_SAFE {
-    VlWide rwp;
-    VL_SET_WQ(rwp, rd);
-    return VL_SHIFTL_WWW(obits, lbits, rbits, owp, lwp, rwp);
-}
-static inline IData VL_SHIFTL_IIW(int obits, int, int rbits, IData lhs,
-                                  WDataInP const rwp) VL_MT_SAFE {
-    for (int i = 1; i < VL_WORDS_I(rbits); ++i) {
-        if (VL_UNLIKELY(rwp[i])) {  // Huge shift 1>>32 or more
-            return 0;
-        }
-    }
-    return VL_SHIFTL_III(obits, obits, 32, lhs, rwp[0]);
-}
-static inline QData VL_SHIFTL_QQW(int obits, int, int rbits, QData lhs,
-                                  WDataInP const rwp) VL_MT_SAFE {
-    for (int i = 1; i < VL_WORDS_I(rbits); ++i) {
-        if (VL_UNLIKELY(rwp[i])) {  // Huge shift 1>>32 or more
-            return 0;
-        }
-    }
-    // Above checks rwp[1]==0 so not needed in below shift
-    return VL_SHIFTL_QQI(obits, obits, 32, lhs, rwp[0]);
-}
-
-// EMIT_RULE: VL_SHIFTR:  oclean=lclean; rclean==clean;
-// Important: Unlike most other funcs, the shift might well be a computed
-// expression.  Thus consider this when optimizing.  (And perhaps have 2 funcs?)
-static inline IData VL_SHIFTR_III(int obits, int, int, IData lhs, IData rhs) VL_PURE {
-    if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
-    return lhs >> rhs;
-}
-static inline IData VL_SHIFTR_IIQ(int obits, int, int, IData lhs, QData rhs) VL_PURE {
-    if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return 0;
-    return lhs >> rhs;
-}
-static inline QData VL_SHIFTR_QQI(int obits, int, int, QData lhs, IData rhs) VL_PURE {
-    if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
-    return lhs >> rhs;
-}
-static inline QData VL_SHIFTR_QQQ(int obits, int, int, QData lhs, QData rhs) VL_PURE {
-    if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return 0;
-    return lhs >> rhs;
-}
-static inline WDataOutP VL_SHIFTR_WWI(int obits, int, int, WDataOutP owp, WDataInP const lwp,
-                                      IData rd) VL_MT_SAFE {
-    const int word_shift = VL_BITWORD_E(rd);  // Maybe 0
-    const int bit_shift = VL_BITBIT_E(rd);
-    if (rd >= static_cast(obits)) {  // rd may be huge with MSB set
-        for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = 0;
-    } else if (bit_shift == 0) {  // Aligned word shift (>>0,>>32,>>64 etc)
-        const int copy_words = (VL_WORDS_I(obits) - word_shift);
-        for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift];
-        for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = 0;
-    } else {
-        const int loffset = rd & VL_SIZEBITS_E;
-        const int nbitsonright = VL_EDATASIZE - loffset;  // bits that end up in lword (know
-                                                          // loffset!=0) Middle words
-        const int words = VL_WORDS_I(obits - rd);
-        for (int i = 0; i < words; ++i) {
-            owp[i] = lwp[i + word_shift] >> loffset;
-            const int upperword = i + word_shift + 1;
-            if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright;
-        }
-        for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0;
-    }
-    return owp;
-}
-static inline WDataOutP VL_SHIFTR_WWW(int obits, int lbits, int rbits, WDataOutP owp,
-                                      WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE {
-    for (int i = 1; i < VL_WORDS_I(rbits); ++i) {
-        if (VL_UNLIKELY(rwp[i])) {  // Huge shift 1>>32 or more
-            return VL_ZERO_W(obits, owp);
-        }
-    }
-    return VL_SHIFTR_WWI(obits, lbits, 32, owp, lwp, rwp[0]);
-}
-static inline WDataOutP VL_SHIFTR_WWQ(int obits, int lbits, int rbits, WDataOutP owp,
-                                      WDataInP const lwp, QData rd) VL_MT_SAFE {
-    VlWide rwp;
-    VL_SET_WQ(rwp, rd);
-    return VL_SHIFTR_WWW(obits, lbits, rbits, owp, lwp, rwp);
-}
-
-static inline IData VL_SHIFTR_IIW(int obits, int, int rbits, IData lhs,
-                                  WDataInP const rwp) VL_PURE {
-    for (int i = 1; i < VL_WORDS_I(rbits); ++i) {
-        if (VL_UNLIKELY(rwp[i])) return 0;  // Huge shift 1>>32 or more
-    }
-    return VL_SHIFTR_III(obits, obits, 32, lhs, rwp[0]);
-}
-static inline QData VL_SHIFTR_QQW(int obits, int, int rbits, QData lhs,
-                                  WDataInP const rwp) VL_PURE {
-    for (int i = 1; i < VL_WORDS_I(rbits); ++i) {
-        if (VL_UNLIKELY(rwp[i])) return 0;  // Huge shift 1>>32 or more
-    }
-    return VL_SHIFTR_QQI(obits, obits, 32, lhs, rwp[0]);
-}
-
-// EMIT_RULE: VL_SHIFTRS:  oclean=false; lclean=clean, rclean==clean;
-static inline IData VL_SHIFTRS_III(int obits, int lbits, int, IData lhs, IData rhs) VL_PURE {
-    // Note the C standard does not specify the >> operator as a arithmetic shift!
-    // IEEE says signed if output signed, but bit position from lbits;
-    // must use lbits for sign; lbits might != obits,
-    // an EXTEND(SHIFTRS(...)) can became a SHIFTRS(...) within same 32/64 bit word length
-    const IData sign = -(lhs >> (lbits - 1));  // ffff_ffff if negative
-    if (VL_UNLIKELY(rhs >= VL_IDATASIZE)) return sign & VL_MASK_I(obits);
-    const IData signext = ~(VL_MASK_I(lbits) >> rhs);  // One with bits where we've shifted "past"
-    return (lhs >> rhs) | (sign & VL_CLEAN_II(obits, obits, signext));
-}
-static inline QData VL_SHIFTRS_QQI(int obits, int lbits, int, QData lhs, IData rhs) VL_PURE {
-    const QData sign = -(lhs >> (lbits - 1));
-    if (VL_UNLIKELY(rhs >= VL_QUADSIZE)) return sign & VL_MASK_Q(obits);
-    const QData signext = ~(VL_MASK_Q(lbits) >> rhs);
-    return (lhs >> rhs) | (sign & VL_CLEAN_QQ(obits, obits, signext));
-}
-static inline IData VL_SHIFTRS_IQI(int obits, int lbits, int rbits, QData lhs, IData rhs) VL_PURE {
-    return static_cast(VL_SHIFTRS_QQI(obits, lbits, rbits, lhs, rhs));
-}
-static inline WDataOutP VL_SHIFTRS_WWI(int obits, int lbits, int, WDataOutP owp,
-                                       WDataInP const lwp, IData rd) VL_MT_SAFE {
-    const int word_shift = VL_BITWORD_E(rd);
-    const int bit_shift = VL_BITBIT_E(rd);
-    const int lmsw = VL_WORDS_I(obits) - 1;
-    const EData sign = VL_SIGNONES_E(lbits, lwp[lmsw]);
-    if (rd >= static_cast(obits)) {  // Shifting past end, sign in all of lbits
-        for (int i = 0; i <= lmsw; ++i) owp[i] = sign;
-        owp[lmsw] &= VL_MASK_E(lbits);
-    } else if (bit_shift == 0) {  // Aligned word shift (>>0,>>32,>>64 etc)
-        const int copy_words = (VL_WORDS_I(obits) - word_shift);
-        for (int i = 0; i < copy_words; ++i) owp[i] = lwp[i + word_shift];
-        if (copy_words >= 0) owp[copy_words - 1] |= ~VL_MASK_E(obits) & sign;
-        for (int i = copy_words; i < VL_WORDS_I(obits); ++i) owp[i] = sign;
-        owp[lmsw] &= VL_MASK_E(lbits);
-    } else {
-        const int loffset = rd & VL_SIZEBITS_E;
-        const int nbitsonright
-            = VL_EDATASIZE - loffset;  // bits that end up in lword (know loffset!=0)
-        // Middle words
-        const int words = VL_WORDS_I(obits - rd);
-        for (int i = 0; i < words; ++i) {
-            owp[i] = lwp[i + word_shift] >> loffset;
-            const int upperword = i + word_shift + 1;
-            if (upperword < VL_WORDS_I(obits)) owp[i] |= lwp[upperword] << nbitsonright;
-        }
-        if (words) owp[words - 1] |= sign & ~VL_MASK_E(obits - loffset);
-        for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = sign;
-        owp[lmsw] &= VL_MASK_E(lbits);
-    }
-    return owp;
-}
-static inline WDataOutP VL_SHIFTRS_WWW(int obits, int lbits, int rbits, WDataOutP owp,
-                                       WDataInP const lwp, WDataInP const rwp) VL_MT_SAFE {
-    EData overshift = 0;  // Huge shift 1>>32 or more
-    for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i];
-    if (VL_UNLIKELY(overshift || rwp[0] >= static_cast(obits))) {
-        const int owords = VL_WORDS_I(obits);
-        if (VL_SIGN_E(lbits, lwp[owords - 1])) {
-            VL_MEMSET_ONES_W(owp, owords);
-            owp[owords - 1] &= VL_MASK_E(lbits);
-        } else {
-            VL_MEMSET_ZERO_W(owp, owords);
-        }
-        return owp;
-    }
-    return VL_SHIFTRS_WWI(obits, lbits, 32, owp, lwp, rwp[0]);
-}
-static inline WDataOutP VL_SHIFTRS_WWQ(int obits, int lbits, int rbits, WDataOutP owp,
-                                       WDataInP const lwp, QData rd) VL_MT_SAFE {
-    VlWide rwp;
-    VL_SET_WQ(rwp, rd);
-    return VL_SHIFTRS_WWW(obits, lbits, rbits, owp, lwp, rwp);
-}
-static inline IData VL_SHIFTRS_IIW(int obits, int lbits, int rbits, IData lhs,
-                                   WDataInP const rwp) VL_PURE {
-    EData overshift = 0;  // Huge shift 1>>32 or more
-    for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i];
-    if (VL_UNLIKELY(overshift || rwp[0] >= static_cast(obits))) {
-        const IData sign = -(lhs >> (lbits - 1));  // ffff_ffff if negative
-        return VL_CLEAN_II(obits, obits, sign);
-    }
-    return VL_SHIFTRS_III(obits, lbits, 32, lhs, rwp[0]);
-}
-static inline QData VL_SHIFTRS_QQW(int obits, int lbits, int rbits, QData lhs,
-                                   WDataInP const rwp) VL_PURE {
-    EData overshift = 0;  // Huge shift 1>>32 or more
-    for (int i = 1; i < VL_WORDS_I(rbits); ++i) overshift |= rwp[i];
-    if (VL_UNLIKELY(overshift || rwp[0] >= static_cast(obits))) {
-        const QData sign = -(lhs >> (lbits - 1));  // ffff_ffff if negative
-        return VL_CLEAN_QQ(obits, obits, sign);
-    }
-    return VL_SHIFTRS_QQI(obits, lbits, 32, lhs, rwp[0]);
-}
-static inline IData VL_SHIFTRS_IIQ(int obits, int lbits, int rbits, IData lhs, QData rhs) VL_PURE {
-    VlWide rwp;
-    VL_SET_WQ(rwp, rhs);
-    return VL_SHIFTRS_IIW(obits, lbits, rbits, lhs, rwp);
-}
-static inline QData VL_SHIFTRS_QQQ(int obits, int lbits, int rbits, QData lhs, QData rhs) VL_PURE {
-    VlWide rwp;
-    VL_SET_WQ(rwp, rhs);
-    return VL_SHIFTRS_QQW(obits, lbits, rbits, lhs, rwp);
-}
-
-//===================================================================
-// Bit selection
-
-// EMIT_RULE: VL_BITSEL:  oclean=dirty; rclean==clean;
-#define VL_BITSEL_IIII(lbits, lhs, rhs) ((lhs) >> (rhs))
-#define VL_BITSEL_QIII(lbits, lhs, rhs) ((lhs) >> (rhs))
-#define VL_BITSEL_QQII(lbits, lhs, rhs) ((lhs) >> (rhs))
-#define VL_BITSEL_IQII(lbits, lhs, rhs) (static_cast((lhs) >> (rhs)))
-
-static inline IData VL_BITSEL_IWII(int lbits, WDataInP const lwp, IData rd) VL_MT_SAFE {
-    const int word = VL_BITWORD_E(rd);
-    if (VL_UNLIKELY(rd > static_cast(lbits))) {
-        return ~0;  // Spec says you can go outside the range of a array.  Don't coredump if so.
-        // We return all 1's as that's more likely to find bugs (?) than 0's.
-    } else {
-        return (lwp[word] >> VL_BITBIT_E(rd));
-    }
-}
-
-// EMIT_RULE: VL_RANGE:  oclean=lclean;  out=dirty
-//  &  MUST BE CLEAN (currently constant)
-#define VL_SEL_IIII(lbits, lhs, lsb, width) ((lhs) >> (lsb))
-#define VL_SEL_QQII(lbits, lhs, lsb, width) ((lhs) >> (lsb))
-#define VL_SEL_IQII(lbits, lhs, lsb, width) (static_cast((lhs) >> (lsb)))
-
-static inline IData VL_SEL_IWII(int lbits, WDataInP const lwp, IData lsb, IData width) VL_MT_SAFE {
-    const int msb = lsb + width - 1;
-    if (VL_UNLIKELY(msb >= lbits)) {
-        return ~0;  // Spec says you can go outside the range of a array.  Don't coredump if so.
-    } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) {
-        return VL_BITRSHIFT_W(lwp, lsb);
-    } else {
-        // 32 bit extraction may span two words
-        const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb);  // bits that come from low word
-        return ((lwp[VL_BITWORD_E(msb)] << nbitsfromlow) | VL_BITRSHIFT_W(lwp, lsb));
-    }
-}
-
-static inline QData VL_SEL_QWII(int lbits, WDataInP const lwp, IData lsb, IData width) VL_MT_SAFE {
-    const int msb = lsb + width - 1;
-    if (VL_UNLIKELY(msb > lbits)) {
-        return ~0;  // Spec says you can go outside the range of a array.  Don't coredump if so.
-    } else if (VL_BITWORD_E(msb) == VL_BITWORD_E(static_cast(lsb))) {
-        return VL_BITRSHIFT_W(lwp, lsb);
-    } else if (VL_BITWORD_E(msb) == 1 + VL_BITWORD_E(static_cast(lsb))) {
-        const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb);
-        const QData hi = (lwp[VL_BITWORD_E(msb)]);
-        const QData lo = VL_BITRSHIFT_W(lwp, lsb);
-        return (hi << nbitsfromlow) | lo;
-    } else {
-        // 64 bit extraction may span three words
-        const int nbitsfromlow = VL_EDATASIZE - VL_BITBIT_E(lsb);
-        const QData hi = (lwp[VL_BITWORD_E(msb)]);
-        const QData mid = (lwp[VL_BITWORD_E(lsb) + 1]);
-        const QData lo = VL_BITRSHIFT_W(lwp, lsb);
-        return (hi << (nbitsfromlow + VL_EDATASIZE)) | (mid << nbitsfromlow) | lo;
-    }
-}
-
-static inline WDataOutP VL_SEL_WWII(int obits, int lbits, WDataOutP owp, WDataInP const lwp,
-                                    IData lsb, IData width) VL_MT_SAFE {
-    const int msb = lsb + width - 1;
-    const int word_shift = VL_BITWORD_E(lsb);
-    if (VL_UNLIKELY(msb > lbits)) {  // Outside bounds,
-        for (int i = 0; i < VL_WORDS_I(obits) - 1; ++i) owp[i] = ~0;
-        owp[VL_WORDS_I(obits) - 1] = VL_MASK_E(obits);
-    } else if (VL_BITBIT_E(lsb) == 0) {
-        // Just a word extract
-        for (int i = 0; i < VL_WORDS_I(obits); ++i) owp[i] = lwp[i + word_shift];
-    } else {
-        // Not a _vl_insert because the bits come from any bit number and goto bit 0
-        const int loffset = lsb & VL_SIZEBITS_E;
-        const int nbitsfromlow = VL_EDATASIZE - loffset;  // bits that end up in lword (know
-                                                          // loffset!=0) Middle words
-        const int words = VL_WORDS_I(msb - lsb + 1);
-        for (int i = 0; i < words; ++i) {
-            owp[i] = lwp[i + word_shift] >> loffset;
-            const int upperword = i + word_shift + 1;
-            if (upperword <= static_cast(VL_BITWORD_E(msb))) {
-                owp[i] |= lwp[upperword] << nbitsfromlow;
-            }
-        }
-        for (int i = words; i < VL_WORDS_I(obits); ++i) owp[i] = 0;
-    }
-    return owp;
-}
-
-template 
-static inline VlQueue VL_CLONE_Q(const VlQueue& from, int lbits, int srcElementBits,
-                                    int dstElementBits) {
-    VlQueue ret;
-    VL_COPY_Q(ret, from, lbits, srcElementBits, dstElementBits);
-    return ret;
-}
-
-template 
-static inline VlQueue VL_REVCLONE_Q(const VlQueue& from, int lbits, int srcElementBits,
-                                       int dstElementBits) {
-    VlQueue ret;
-    VL_REVCOPY_Q(ret, from, lbits, srcElementBits, dstElementBits);
-    return ret;
-}
-
-// Helper function to get a bit from a queue at a specific bit index
-template 
-static inline bool VL_GET_QUEUE_BIT(const VlQueue& queue, int srcElementBits, size_t bitIndex) {
-    const size_t elemIdx = bitIndex / srcElementBits;
-    if (VL_UNLIKELY(elemIdx >= queue.size())) return false;
-
-    const T element = queue.at(elemIdx);
-    if (srcElementBits == 1) {
-        return element & 1;
-    } else {
-        const size_t bitInElem = bitIndex % srcElementBits;
-        const size_t actualBitPos = srcElementBits - 1 - bitInElem;
-        return (element >> actualBitPos) & 1;
-    }
-}
-
-// Helper function to set a bit in the destination queue
-template 
-static inline void VL_SET_QUEUE_BIT(VlQueue& queue, int dstElementBits, size_t bitIndex,
-                                    bool value) {
-    if (dstElementBits == 1) {
-        if (VL_UNLIKELY(bitIndex >= queue.size())) return;
-        queue.atWrite(bitIndex) = value ? 1 : 0;
-    } else {
-        const size_t elemIdx = bitIndex / dstElementBits;
-        if (VL_UNLIKELY(elemIdx >= queue.size())) return;
-        const size_t bitInElem = bitIndex % dstElementBits;
-        const size_t actualBitPos = dstElementBits - 1 - bitInElem;
-        if (value) {
-            queue.atWrite(elemIdx) |= (static_cast(1) << actualBitPos);
-        } else {
-            queue.atWrite(elemIdx) &= ~(static_cast(1) << actualBitPos);
-        }
-    }
-}
-
-// Helper function to get a bit from a VlWide queue at a specific bit index
-template 
-static inline bool VL_GET_QUEUE_BIT(const VlQueue>& queue, int srcElementBits,
-                                    size_t bitIndex) {
-    const size_t elemIdx = bitIndex / srcElementBits;
-    if (VL_UNLIKELY(elemIdx >= queue.size())) return false;
-
-    const VlWide& element = queue.at(elemIdx);
-    const size_t bitInElem = bitIndex % srcElementBits;
-    const size_t actualBitPos = srcElementBits - 1 - bitInElem;
-
-    return VL_BITISSET_W(element.data(), actualBitPos);
-}
-
-// Helper function to set a bit in a VlWide queue at a specific bit index
-template 
-static inline void VL_SET_QUEUE_BIT(VlQueue>& queue, int dstElementBits,
-                                    size_t bitIndex, bool value) {
-    const size_t elemIdx = bitIndex / dstElementBits;
-    if (VL_UNLIKELY(elemIdx >= queue.size())) return;
-
-    const size_t bitInElem = bitIndex % dstElementBits;
-    const size_t actualBitPos = dstElementBits - 1 - bitInElem;
-
-    VlWide& element = queue.atWrite(elemIdx);
-    if (value) {
-        VL_ASSIGNBIT_WO(actualBitPos, element.data());
-    } else {
-        VL_ASSIGNBIT_WI(actualBitPos, element.data(), 0);
-    }
-}
-
-template 
-static inline void VL_ZERO_INIT_QUEUE_ELEM(T& elem) {
-    elem = 0;
-}
-
-template 
-static inline void VL_ZERO_INIT_QUEUE_ELEM(VlWide& elem) {
-    for (size_t j = 0; j < N_Words; ++j) { elem.at(j) = 0; }
-}
-
-// This specialization works for both VlQueue (and similar) as well
-// as VlQueue>.
-template 
-static inline void VL_COPY_Q(VlQueue& q, const VlQueue& from, int lbits, int srcElementBits,
-                             int dstElementBits) {
-    if (srcElementBits == dstElementBits) {
-        // Simple case: same element bit width, direct copy of each element
-        if (VL_UNLIKELY(&q == &from)) return;  // Skip self-assignment when it's truly a no-op
-        q = from;
-    } else {
-        // Different element bit widths: use streaming conversion
-        VlQueue srcCopy = from;
-        const size_t srcTotalBits = from.size() * srcElementBits;
-        const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits;
-        q.renew(dstSize);
-        for (size_t i = 0; i < dstSize; ++i) { VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i)); }
-        for (size_t bitIndex = 0; bitIndex < srcTotalBits; ++bitIndex) {
-            VL_SET_QUEUE_BIT(q, dstElementBits, bitIndex,
-                             VL_GET_QUEUE_BIT(srcCopy, srcElementBits, bitIndex));
-        }
-    }
-}
-
-// This specialization works for both VlQueue (and similar) as well
-// as VlQueue>.
-template 
-static inline void VL_REVCOPY_Q(VlQueue& q, const VlQueue& from, int lbits,
-                                int srcElementBits, int dstElementBits) {
-    const size_t srcTotalBits = from.size() * srcElementBits;
-    const size_t dstSize = (srcTotalBits + dstElementBits - 1) / dstElementBits;
-
-    // Always make a copy to handle the case where q and from are the same queue
-    VlQueue srcCopy = from;
-
-    // Initialize all elements to zero using appropriate method
-    q.renew(dstSize);
-    for (size_t i = 0; i < dstSize; ++i) VL_ZERO_INIT_QUEUE_ELEM(q.atWrite(i));
-
-    if (lbits == 1) {
-        // Simple bit reversal: write directly to destination
-        for (int i = srcTotalBits - 1; i >= 0; --i) {
-            VL_SET_QUEUE_BIT(q, dstElementBits, srcTotalBits - 1 - i,
-                             VL_GET_QUEUE_BIT(srcCopy, srcElementBits, i));
-        }
-    } else {
-        // Generalized block-reversal for lbits > 1:
-        // 1. Reverse all bits using 1-bit blocks
-        // 2. Split into lbits-sized blocks and pad incomplete blocks on the left
-        // 3. Reverse each lbits-sized block using 1-bit blocks
-        const size_t numCompleteBlocks = srcTotalBits / lbits;
-        const size_t remainderBits = srcTotalBits % lbits;
-        const size_t srcBlocks = numCompleteBlocks + (remainderBits > 0 ? 1 : 0);
-
-        size_t dstBitIndex = 0;
-
-        for (size_t block = 0; block < srcBlocks; ++block) {
-            const size_t blockStart = block * lbits;
-            const int bitsToProcess = VL_LIKELY(block < numCompleteBlocks) ? lbits : remainderBits;
-            for (int bit = bitsToProcess - 1; bit >= 0; --bit) {
-                const size_t reversedBitIndex = blockStart + bit;
-                const size_t originalBitIndex = srcTotalBits - 1 - reversedBitIndex;
-                VL_SET_QUEUE_BIT(q, dstElementBits, dstBitIndex++,
-                                 VL_GET_QUEUE_BIT(srcCopy, srcElementBits, originalBitIndex));
-            }
-            dstBitIndex += lbits - bitsToProcess;
-        }
-    }
-}
-
-//======================================================================
-// Expressions needing insert/select
-
-static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) {
-    const size_t size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
-}
-
-static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) {
-    const size_t size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
-}
-
-static inline void VL_UNPACK_RI_I(int lbits, int rbits, VlQueue& q, IData from) {
-    const size_t size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
-}
-
-static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) {
-    const size_t size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
-}
-
-static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) {
-    const size_t size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
-}
-
-static inline void VL_UNPACK_RI_Q(int lbits, int rbits, VlQueue& q, QData from) {
-    const size_t size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
-}
-
-static inline void VL_UNPACK_RQ_Q(int lbits, int rbits, VlQueue& q, QData from) {
-    const size_t size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    const QData mask = VL_MASK_Q(lbits);
-    for (size_t i = 0; i < size; ++i) q.atWrite(size - 1 - i) = (from >> (i * lbits)) & mask;
-}
-
-static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WDataInP rwp) {
-    const int size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < size; ++i) {
-        // Extract from MSB to LSB: MSB goes to index 0
-        const int bitPos = rbits - (i + 1) * lbits;
-        const int actualBitPos = (bitPos < 0) ? 0 : bitPos;
-        const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits;
-        q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask;
-    }
-}
-
-static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WDataInP rwp) {
-    const int size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < size; ++i) {
-        // Extract from MSB to LSB: MSB goes to index 0
-        const int bitPos = rbits - (i + 1) * lbits;
-        const int actualBitPos = (bitPos < 0) ? 0 : bitPos;
-        const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits;
-        q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask;
-    }
-}
-
-static inline void VL_UNPACK_RI_W(int lbits, int rbits, VlQueue& q, WDataInP rwp) {
-    const int size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < size; ++i) {
-        // Extract from MSB to LSB: MSB goes to index 0
-        const int bitPos = rbits - (i + 1) * lbits;
-        const int actualBitPos = (bitPos < 0) ? 0 : bitPos;
-        const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits;
-        q.atWrite(i) = VL_SEL_IWII(rbits, rwp, actualBitPos, actualWidth) & mask;
-    }
-}
-
-static inline void VL_UNPACK_RQ_W(int lbits, int rbits, VlQueue& q, WDataInP rwp) {
-    const int size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    const QData mask = VL_MASK_Q(lbits);
-    for (size_t i = 0; i < size; ++i) {
-        // Extract from MSB to LSB: MSB goes to index 0
-        const int bitPos = rbits - (i + 1) * lbits;
-        const int actualBitPos = (bitPos < 0) ? 0 : bitPos;
-        const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits;
-        q.atWrite(i) = VL_SEL_QWII(rbits, rwp, actualBitPos, actualWidth) & mask;
-    }
-}
-
-template 
-static inline void VL_UNPACK_RW_W(int lbits, int rbits, VlQueue>& q,
-                                  WDataInP rwp) {
-    const int size = (rbits + lbits - 1) / lbits;
-    q.renew(size);
-    for (size_t i = 0; i < size; ++i) {
-        // Extract from MSB to LSB: MSB goes to index 0
-        const int bitPos = rbits - (i + 1) * lbits;
-        const int actualBitPos = (bitPos < 0) ? 0 : bitPos;
-        const int actualWidth = (bitPos < 0) ? (lbits + bitPos) : lbits;
-        VL_SEL_WWII(actualWidth, rbits, q.atWrite(i), rwp, actualBitPos, actualWidth);
-    }
-}
-
-template 
-static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked& q,
-                                  IData from) {
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
-}
-
-template 
-static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked& q,
-                                  IData from) {
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
-}
-
-template 
-static inline void VL_UNPACK_UI_I(int lbits, int rbits, VlUnpacked& q,
-                                  IData from) {
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
-}
-
-template 
-static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked& q,
-                                  QData from) {
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
-}
-
-template 
-static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked& q,
-                                  QData from) {
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
-}
-
-template 
-static inline void VL_UNPACK_UI_Q(int lbits, int rbits, VlUnpacked& q,
-                                  QData from) {
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
-}
-
-template 
-static inline void VL_UNPACK_UQ_Q(int lbits, int rbits, VlUnpacked& q,
-                                  QData from) {
-    const QData mask = VL_MASK_Q(lbits);
-    for (size_t i = 0; i < N_Depth; ++i) q[i] = (from >> ((N_Depth - 1 - i) * lbits)) & mask;
-}
-
-template 
-static inline void VL_UNPACK_UI_W(int lbits, int rbits, VlUnpacked& q,
-                                  WDataInP rwp) {
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < N_Depth; ++i)
-        q[i] = VL_SEL_IWII(rbits, rwp, (N_Depth - 1 - i) * lbits, lbits) & mask;
-}
-
-template 
-static inline void VL_UNPACK_UI_W(int lbits, int rbits, VlUnpacked& q,
-                                  WDataInP rwp) {
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < N_Depth; ++i)
-        q[i] = VL_SEL_IWII(rbits, rwp, (N_Depth - 1 - i) * lbits, lbits) & mask;
-}
-
-template 
-static inline void VL_UNPACK_UI_W(int lbits, int rbits, VlUnpacked& q,
-                                  WDataInP rwp) {
-    const IData mask = VL_MASK_I(lbits);
-    for (size_t i = 0; i < N_Depth; ++i)
-        q[i] = VL_SEL_IWII(rbits, rwp, (N_Depth - 1 - i) * lbits, lbits) & mask;
-}
-
-template 
-static inline void VL_UNPACK_UQ_W(int lbits, int rbits, VlUnpacked& q,
-                                  WDataInP rwp) {
-    const QData mask = VL_MASK_Q(lbits);
-    for (size_t i = 0; i < N_Depth; ++i)
-        q[i] = VL_SEL_QWII(rbits, rwp, (N_Depth - 1 - i) * lbits, lbits) & mask;
-}
-
-template 
-static inline void VL_UNPACK_UW_W(int lbits, int rbits, VlUnpacked, N_Depth>& q,
-                                  WDataInP rwp) {
-    for (size_t i = 0; i < N_Depth; ++i)
-        VL_SEL_WWII(lbits, rbits, q[i], rwp, (N_Depth - 1 - i) * lbits, lbits);
-}
-
-// Return QData from double (numeric)
-// EMIT_RULE: VL_RTOIROUND_Q_D:  oclean=dirty; lclean==clean/real
-static inline QData VL_RTOIROUND_Q_D(double lhs) VL_PURE {
-    // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa
-    // This does not need to support subnormals as they are sub-integral
-    lhs = VL_ROUND(lhs);
-    if (lhs == 0.0) return 0;
-    const QData q = VL_CVT_Q_D(lhs);
-    const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52;
-    const uint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
-    uint64_t out = 0;
-    if (lsb < 0) {
-        out = mantissa >> -lsb;
-    } else if (lsb < 64) {
-        out = mantissa << lsb;
-    }
-    if (lhs < 0) out = -out;
-    return out;
-}
-static inline IData VL_RTOIROUND_I_D(double lhs) VL_PURE {
-    return static_cast(VL_RTOIROUND_Q_D(lhs));
-}
-static inline WDataOutP VL_RTOIROUND_W_D(int obits, WDataOutP owp, double lhs) VL_MT_SAFE {
-    // IEEE format: [63]=sign [62:52]=exp+1023 [51:0]=mantissa
-    // This does not need to support subnormals as they are sub-integral
-    lhs = VL_ROUND(lhs);
-    VL_ZERO_W(obits, owp);
-    if (lhs == 0.0) return owp;
-    const QData q = VL_CVT_Q_D(lhs);
-    const int lsb = static_cast((q >> 52ULL) & VL_MASK_Q(11)) - 1023 - 52;
-    const uint64_t mantissa = (q & VL_MASK_Q(52)) | (1ULL << 52);
-    if (lsb < 0) {
-        VL_SET_WQ(owp, mantissa >> -lsb);
-    } else if (lsb < obits) {
-        _vl_insert_WQ(owp, mantissa, lsb + 52, lsb);
-    }
-    if (lhs < 0) VL_NEGATE_INPLACE_W(VL_WORDS_I(obits), owp);
-    return owp;
-}
-
-//======================================================================
-// Range assignments
-
-// EMIT_RULE: VL_ASSIGNRANGE:  rclean=dirty;
-static inline void VL_ASSIGNSEL_II(int rbits, int obits, int lsb, CData& lhsr, IData rhs) VL_PURE {
-    _vl_insert_II(lhsr, rhs, lsb + obits - 1, lsb, rbits);
-}
-static inline void VL_ASSIGNSEL_II(int rbits, int obits, int lsb, SData& lhsr, IData rhs) VL_PURE {
-    _vl_insert_II(lhsr, rhs, lsb + obits - 1, lsb, rbits);
-}
-static inline void VL_ASSIGNSEL_II(int rbits, int obits, int lsb, IData& lhsr, IData rhs) VL_PURE {
-    _vl_insert_II(lhsr, rhs, lsb + obits - 1, lsb, rbits);
-}
-static inline void VL_ASSIGNSEL_QI(int rbits, int obits, int lsb, QData& lhsr, IData rhs) VL_PURE {
-    _vl_insert_QQ(lhsr, rhs, lsb + obits - 1, lsb, rbits);
-}
-static inline void VL_ASSIGNSEL_QQ(int rbits, int obits, int lsb, QData& lhsr, QData rhs) VL_PURE {
-    _vl_insert_QQ(lhsr, rhs, lsb + obits - 1, lsb, rbits);
-}
-// static inline void VL_ASSIGNSEL_IIIW(int obits, int lsb, IData& lhsr, WDataInP const rwp)
-// VL_MT_SAFE { Illegal, as lhs width >= rhs width
-static inline void VL_ASSIGNSEL_WI(int rbits, int obits, int lsb, WDataOutP iowp,
-                                   IData rhs) VL_MT_SAFE {
-    _vl_insert_WI(iowp, rhs, lsb + obits - 1, lsb, rbits);
-}
-static inline void VL_ASSIGNSEL_WQ(int rbits, int obits, int lsb, WDataOutP iowp,
-                                   QData rhs) VL_MT_SAFE {
-    _vl_insert_WQ(iowp, rhs, lsb + obits - 1, lsb, rbits);
-}
-static inline void VL_ASSIGNSEL_WW(int rbits, int obits, int lsb, WDataOutP iowp,
-                                   WDataInP const rwp) VL_MT_SAFE {
-    _vl_insert_WW(iowp, rwp, lsb + obits - 1, lsb, rbits);
-}
-
-//====================================================
-// Range assignments
-
-// These additional functions copy bits range [obis+roffset-1:roffset] from rhs to lower bits
-// of lhs(select before assigning). Rhs should always be wider than lhs.
-static inline void VL_SELASSIGN_II(int rbits, int obits, CData& lhsr, IData rhs,
-                                   int roffset) VL_PURE {
-    _vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
-}
-static inline void VL_SELASSIGN_II(int rbits, int obits, SData& lhsr, IData rhs,
-                                   int roffset) VL_PURE {
-    _vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
-}
-static inline void VL_SELASSIGN_II(int rbits, int obits, IData& lhsr, IData rhs,
-                                   int roffset) VL_PURE {
-    _vl_insert_II(lhsr, rhs >> roffset, obits - 1, 0, rbits);
-}
-static inline void VL_SELASSIGN_IQ(int rbits, int obits, CData& lhsr, QData rhs,
-                                   int roffset) VL_PURE {
-    // it will be truncated to right CData mask
-    const CData cleanmask = VL_MASK_I(rbits);
-    const CData insmask = VL_MASK_I(obits);
-    lhsr = (lhsr & ~insmask) | (static_cast(rhs >> roffset) & (insmask & cleanmask));
-}
-static inline void VL_SELASSIGN_IQ(int rbits, int obits, SData& lhsr, QData rhs,
-                                   int roffset) VL_PURE {
-    // it will be truncated to right CData mask
-    const SData cleanmask = VL_MASK_I(rbits);
-    const SData insmask = VL_MASK_I(obits);
-    lhsr = (lhsr & ~insmask) | (static_cast(rhs >> roffset) & (insmask & cleanmask));
-}
-static inline void VL_SELASSIGN_IQ(int rbits, int obits, IData& lhsr, QData rhs,
-                                   int roffset) VL_PURE {
-    const IData cleanmask = VL_MASK_I(rbits);
-    const IData insmask = VL_MASK_I(obits);
-    lhsr = (lhsr & ~insmask) | (static_cast(rhs >> roffset) & (insmask & cleanmask));
-}
-
-static inline void VL_SELASSIGN_QQ(int rbits, int obits, QData& lhsr, QData rhs,
-                                   int roffset) VL_PURE {
-    _vl_insert_QQ(lhsr, rhs >> roffset, obits - 1, 0, rbits);
-}
-
-static inline void VL_SELASSIGN_IW(int rbits, int obits, CData& lhsr, WDataInP const rhs,
-                                   int roffset) VL_MT_SAFE {
-    IData l = static_cast(lhsr);
-    _vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits);
-    lhsr = static_cast(l);
-}
-static inline void VL_SELASSIGN_IW(int rbits, int obits, SData& lhsr, WDataInP const rhs,
-                                   int roffset) VL_MT_SAFE {
-    IData l = static_cast(lhsr);
-    _vl_insert_IW(l, rhs, roffset + obits - 1, roffset, rbits);
-    lhsr = static_cast(l);
-}
-static inline void VL_SELASSIGN_IW(int rbits, int obits, IData& lhsr, WDataInP const rhs,
-                                   int roffset) VL_MT_SAFE {
-    _vl_insert_IW(lhsr, rhs, roffset + obits - 1, roffset, rbits);
-}
-static inline void VL_SELASSIGN_QW(int rbits, int obits, QData& lhsr, WDataInP const rhs,
-                                   int roffset) VL_MT_SAFE {
-    // assert VL_QDATASIZE >= rbits > VL_IDATASIZE;
-    IData low = static_cast(lhsr);
-    IData high = static_cast(lhsr >> VL_IDATASIZE);
-    if (obits <= VL_IDATASIZE) {
-        _vl_insert_IW(low, rhs, obits + roffset - 1, roffset, VL_IDATASIZE);
-    } else {
-        _vl_insert_IW(low, rhs, roffset + VL_IDATASIZE - 1, roffset, VL_IDATASIZE);
-        _vl_insert_IW(high, rhs, roffset + obits - 1, roffset + VL_IDATASIZE,
-                      rbits - VL_IDATASIZE);
-    }
-    lhsr = (static_cast(high) << VL_IDATASIZE) | low;
-}
-
-static inline void VL_SELASSIGN_WW(int rbits, int obits, WDataOutP iowp, WDataInP const rwp,
-                                   int roffset) VL_MT_SAFE {
-    // assert rbits > VL_QDATASIZE
-    const int wordoff = roffset / VL_EDATASIZE;
-    const int lsb = roffset & VL_SIZEBITS_E;
-    const int upperbits = lsb == 0 ? 0 : VL_EDATASIZE - lsb;
-    // If roffset is not aligned, we copy some bits to align it.
-    if (lsb != 0) {
-        const int w = obits < upperbits ? obits : upperbits;
-        const int insmask = VL_MASK_E(w);
-        iowp[0] = (iowp[0] & ~insmask) | ((rwp[wordoff] >> lsb) & insmask);
-        // cppcheck-suppress knownConditionTrueFalse
-        if (w == obits) return;
-        obits -= w;
-    }
-    _vl_insert_WW(iowp, rwp + wordoff + (lsb != 0), upperbits + obits - 1, upperbits, rbits);
-}
-
-//======================================================================
-// Triops
-
-static inline WDataOutP VL_COND_WIWW(int obits, WDataOutP owp, int cond, WDataInP const w1p,
-                                     WDataInP const w2p) VL_MT_SAFE {
-    return VL_MEMCPY_W(owp, cond ? w1p : w2p, VL_WORDS_I(obits));
-}
-
-//======================================================================
-// Constification
-
-// VL_CONST_W_#X(int obits, WDataOutP owp, IData data0, .... IData data(#-1))
-// Sets wide vector words to specified constant words.
-// These macros are used when o might represent more words then are given as constants,
-// hence all upper words must be zeroed.
-// If changing the number of functions here, also change EMITCINLINES_NUM_CONSTW
-
-#define VL_C_END_(obits, wordsSet) \
-    VL_MEMSET_ZERO_W(o + (wordsSet), VL_WORDS_I(obits) - (wordsSet)); \
-    return o
-
-// clang-format off
-static inline WDataOutP VL_CONST_W_1X(int obits, WDataOutP o, EData d0) VL_MT_SAFE {
-    o[0] = d0;
-    VL_C_END_(obits, 1);
-}
-static inline WDataOutP VL_CONST_W_2X(int obits, WDataOutP o, EData d1, EData d0) VL_MT_SAFE {
-    o[0] = d0;  o[1] = d1;
-    VL_C_END_(obits, 2);
-}
-static inline WDataOutP VL_CONST_W_3X(int obits, WDataOutP o, EData d2, EData d1,
-                                      EData d0) VL_MT_SAFE {
-    o[0] = d0;  o[1] = d1;  o[2] = d2;
-    VL_C_END_(obits, 3);
-}
-static inline WDataOutP VL_CONST_W_4X(int obits, WDataOutP o,
-                                      EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
-    o[0] = d0;  o[1] = d1;  o[2] = d2;  o[3] = d3;
-    VL_C_END_(obits, 4);
-}
-static inline WDataOutP VL_CONST_W_5X(int obits, WDataOutP o,
-                                      EData d4,
-                                      EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
-    o[0] = d0;  o[1] = d1;  o[2] = d2;  o[3] = d3;
-    o[4] = d4;
-    VL_C_END_(obits, 5);
-}
-static inline WDataOutP VL_CONST_W_6X(int obits, WDataOutP o,
-                                      EData d5, EData d4,
-                                      EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
-    o[0] = d0;  o[1] = d1;  o[2] = d2;  o[3] = d3;
-    o[4] = d4;  o[5] = d5;
-    VL_C_END_(obits, 6);
-}
-static inline WDataOutP VL_CONST_W_7X(int obits, WDataOutP o,
-                                      EData d6, EData d5, EData d4,
-                                      EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
-    o[0] = d0;  o[1] = d1;  o[2] = d2;  o[3] = d3;
-    o[4] = d4;  o[5] = d5;  o[6] = d6;
-    VL_C_END_(obits, 7);
-}
-static inline WDataOutP VL_CONST_W_8X(int obits, WDataOutP o,
-                                      EData d7, EData d6, EData d5, EData d4,
-                                      EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
-    o[0] = d0;  o[1] = d1;  o[2] = d2;  o[3] = d3;
-    o[4] = d4;  o[5] = d5;  o[6] = d6;  o[7] = d7;
-    VL_C_END_(obits, 8);
-}
-//
-static inline WDataOutP VL_CONSTHI_W_1X(int obits, int lsb, WDataOutP o,
-                                        EData d0) VL_MT_SAFE {
-    WDataOutP ohi = o + VL_WORDS_I(lsb);
-    ohi[0] = d0;
-    VL_C_END_(obits, VL_WORDS_I(lsb) + 1);
-}
-static inline WDataOutP VL_CONSTHI_W_2X(int obits, int lsb, WDataOutP o,
-                                        EData d1, EData d0) VL_MT_SAFE {
-    WDataOutP ohi = o + VL_WORDS_I(lsb);
-    ohi[0] = d0;  ohi[1] = d1;
-    VL_C_END_(obits, VL_WORDS_I(lsb) + 2);
-}
-static inline WDataOutP VL_CONSTHI_W_3X(int obits, int lsb, WDataOutP o,
-                                        EData d2, EData d1, EData d0) VL_MT_SAFE {
-    WDataOutP ohi = o + VL_WORDS_I(lsb);
-    ohi[0] = d0;  ohi[1] = d1;  ohi[2] = d2;
-    VL_C_END_(obits, VL_WORDS_I(lsb) + 3);
-}
-static inline WDataOutP VL_CONSTHI_W_4X(int obits, int lsb, WDataOutP o,
-                                        EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
-    WDataOutP ohi = o + VL_WORDS_I(lsb);
-    ohi[0] = d0;  ohi[1] = d1;  ohi[2] = d2;  ohi[3] = d3;
-    VL_C_END_(obits, VL_WORDS_I(lsb) + 4);
-}
-static inline WDataOutP VL_CONSTHI_W_5X(int obits, int lsb, WDataOutP o,
-                                        EData d4,
-                                        EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
-    WDataOutP ohi = o + VL_WORDS_I(lsb);
-    ohi[0] = d0;  ohi[1] = d1;  ohi[2] = d2;  ohi[3] = d3;
-    ohi[4] = d4;
-    VL_C_END_(obits, VL_WORDS_I(lsb) + 5);
-}
-static inline WDataOutP VL_CONSTHI_W_6X(int obits, int lsb, WDataOutP o,
-                                        EData d5, EData d4,
-                                        EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
-    WDataOutP ohi = o + VL_WORDS_I(lsb);
-    ohi[0] = d0;  ohi[1] = d1;  ohi[2] = d2;  ohi[3] = d3;
-    ohi[4] = d4;  ohi[5] = d5;
-    VL_C_END_(obits, VL_WORDS_I(lsb) + 6);
-}
-static inline WDataOutP VL_CONSTHI_W_7X(int obits, int lsb, WDataOutP o,
-                                        EData d6, EData d5, EData d4,
-                                        EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
-    WDataOutP ohi = o + VL_WORDS_I(lsb);
-    ohi[0] = d0;  ohi[1] = d1;  ohi[2] = d2;  ohi[3] = d3;
-    ohi[4] = d4;  ohi[5] = d5;  ohi[6] = d6;
-    VL_C_END_(obits, VL_WORDS_I(lsb) + 7);
-}
-static inline WDataOutP VL_CONSTHI_W_8X(int obits, int lsb, WDataOutP o,
-                                        EData d7, EData d6, EData d5, EData d4,
-                                        EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
-    WDataOutP ohi = o + VL_WORDS_I(lsb);
-    ohi[0] = d0;  ohi[1] = d1;  ohi[2] = d2;  ohi[3] = d3;
-    ohi[4] = d4;  ohi[5] = d5;  ohi[6] = d6;  ohi[7] = d7;
-    VL_C_END_(obits, VL_WORDS_I(lsb) + 8);
-}
-
-#undef VL_C_END_
-
-// Partial constant, lower words of vector wider than 8*32, starting at bit number lsb
-static inline void VL_CONSTLO_W_8X(int lsb, WDataOutP obase,
-                                   EData d7, EData d6, EData d5, EData d4,
-                                   EData d3, EData d2, EData d1, EData d0) VL_MT_SAFE {
-    WDataOutP o = obase + VL_WORDS_I(lsb);
-    o[0] = d0; o[1] = d1; o[2] = d2; o[3] = d3; o[4] = d4; o[5] = d5; o[6] = d6; o[7] = d7;
-}
-// clang-format on
-
-//======================================================================
-// Strings
-
-extern std::string VL_PUTC_N(const std::string& lhs, IData rhs, CData ths) VL_PURE;
-extern CData VL_GETC_N(const std::string& lhs, IData rhs) VL_PURE;
-extern std::string VL_SUBSTR_N(const std::string& lhs, IData rhs, IData ths) VL_PURE;
-
-inline IData VL_CMP_NN(const std::string& lhs, const std::string& rhs, bool ignoreCase) VL_PURE {
-    // SystemVerilog does not allow a string variable to contain '\0'.
-    // So C functions such as strcmp() can correctly compare strings.
-    if (ignoreCase) {
-        return VL_STRCASECMP(lhs.c_str(), rhs.c_str());
-    } else {
-        return std::strcmp(lhs.c_str(), rhs.c_str());
-    }
-}
-
-extern IData VL_ATOI_N(const std::string& str, int base) VL_PURE;
-extern IData VL_NTOI_I(int obits, const std::string& str) VL_PURE;
-extern QData VL_NTOI_Q(int obits, const std::string& str) VL_PURE;
-extern void VL_NTOI_W(int obits, WDataOutP owp, const std::string& str) VL_PURE;
-
-extern IData VL_FGETS_NI(std::string& dest, IData fpi) VL_MT_SAFE;
-
-//======================================================================
-// Dist functions
-
-extern IData VL_DIST_CHI_SQUARE(IData& seedr, IData udeg_of_free) VL_MT_SAFE;
-extern IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE;
-extern IData VL_DIST_EXPONENTIAL(IData& seedr, IData umean) VL_MT_SAFE;
-extern IData VL_DIST_NORMAL(IData& seedr, IData umean, IData udeviation) VL_MT_SAFE;
-extern IData VL_DIST_POISSON(IData& seedr, IData umean) VL_MT_SAFE;
-extern IData VL_DIST_T(IData& seedr, IData udeg_of_free) VL_MT_SAFE;
-extern IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE;
-
-//======================================================================
-// Conversion functions
-
-extern std::string VL_CVT_PACK_STR_NW(int lwords, const WDataInP lwp) VL_PURE;
-extern std::string VL_CVT_PACK_STR_ND(const VlQueue& q) VL_PURE;
-inline std::string VL_CVT_PACK_STR_NQ(QData lhs) VL_PURE {
-    VlWide lw;
-    VL_SET_WQ(lw, lhs);
-    return VL_CVT_PACK_STR_NW(VL_WQ_WORDS_E, lw);
-}
-inline std::string VL_CVT_PACK_STR_NN(const std::string& lhs) VL_PURE { return lhs; }
-inline std::string& VL_CVT_PACK_STR_NN(std::string& lhs) VL_PURE { return lhs; }
-inline std::string VL_CVT_PACK_STR_NI(IData lhs) VL_PURE {
-    VlWide lw;
-    VL_SET_WI(lw, lhs);
-    return VL_CVT_PACK_STR_NW(1, lw);
-}
-inline std::string VL_CONCATN_NNN(const std::string& lhs, const std::string& rhs) VL_PURE {
-    return lhs + rhs;
-}
-inline std::string VL_REPLICATEN_NNQ(const std::string& lhs, IData rep) VL_PURE {
-    std::string result;
-    result.reserve(lhs.length() * rep);
-    for (unsigned times = 0; times < rep; ++times) result += lhs;
-    return result;
-}
-inline std::string VL_REPLICATEN_NNI(const std::string& lhs, IData rep) VL_PURE {
-    return VL_REPLICATEN_NNQ(lhs, rep);
-}
-
-inline IData VL_LEN_IN(const std::string& ld) { return static_cast(ld.length()); }
-extern std::string VL_TOLOWER_NN(const std::string& ld) VL_PURE;
-extern std::string VL_TOUPPER_NN(const std::string& ld) VL_PURE;
-
-extern IData VL_FERROR_IN(IData fpi, std::string& outputr) VL_MT_SAFE;
-extern IData VL_FERROR_IW(IData fpi, int obits, WDataOutP outwp) VL_MT_SAFE;
-extern IData VL_FOPEN_NN(const std::string& filename, const std::string& mode) VL_MT_SAFE;
-extern IData VL_FOPEN_MCD_N(const std::string& filename) VL_MT_SAFE;
-extern void VL_READMEM_N(bool hex, int bits, QData depth, int array_lsb,
-                         const std::string& filename, void* memp, QData start,
-                         QData end) VL_MT_SAFE;
-extern void VL_WRITEMEM_N(bool hex, int bits, QData depth, int array_lsb,
-                          const std::string& filename, const void* memp, QData start,
-                          QData end) VL_MT_SAFE;
-extern IData VL_SSCANF_INNX(int lbits, const std::string& ld, const std::string& format, int argc,
-                            ...) VL_MT_SAFE;
-extern void VL_SFORMAT_NX(int obits_ignored, std::string& output, const std::string& format,
-                          int argc, ...) VL_MT_SAFE;
-extern std::string VL_SFORMATF_N_NX(const std::string& format, int argc, ...) VL_MT_SAFE;
-extern void VL_TIMEFORMAT_IINI(bool hasUnits, int units, bool hasPrecision, int precision,
-                               bool hasSuffix, const std::string& suffix, bool hasWidth, int width,
-                               VerilatedContext* contextp) VL_MT_SAFE;
-extern IData VL_VALUEPLUSARGS_INW(int rbits, const std::string& ld, WDataOutP rwp) VL_MT_SAFE;
-inline IData VL_VALUEPLUSARGS_IND(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE {
-    VlWide<2> rwp;
-    const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
-    if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp));
-    return got;
-}
-inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, CData& rdr) VL_MT_SAFE {
-    VlWide<2> rwp;
-    const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
-    if (got) rdr = rwp[0];
-    return got;
-}
-inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, SData& rdr) VL_MT_SAFE {
-    VlWide<2> rwp;
-    const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
-    if (got) rdr = rwp[0];
-    return got;
-}
-inline IData VL_VALUEPLUSARGS_INI(int rbits, const std::string& ld, IData& rdr) VL_MT_SAFE {
-    VlWide<2> rwp;
-    const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
-    if (got) rdr = rwp[0];
-    return got;
-}
-inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, QData& rdr) VL_MT_SAFE {
-    VlWide<2> rwp;
-    const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
-    if (got) rdr = VL_SET_QW(rwp);
-    return got;
-}
-inline IData VL_VALUEPLUSARGS_INQ(int rbits, const std::string& ld, double& rdr) VL_MT_SAFE {
-    VlWide<2> rwp;
-    const IData got = VL_VALUEPLUSARGS_INW(rbits, ld, rwp);
-    if (got) rdr = VL_CVT_D_Q(VL_SET_QW(rwp));
-    return got;
-}
-extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr) VL_MT_SAFE;
-
-uint64_t VL_MURMUR64_HASH(const char* key) VL_PURE;
-
-//======================================================================
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_imp.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_imp.h
deleted file mode 100644
index 6ee4e580e37..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_imp.h
+++ /dev/null
@@ -1,616 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2009-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=========================================================================
-///
-/// \file
-/// \brief Verilated implementation Header, only for verilated.cpp internals.
-///
-/// This file is not part of the Verilated public-facing API.
-/// It is only for internal use by the Verilated libraries.
-///
-//=========================================================================
-
-#ifndef VERILATOR_VERILATED_IMP_H_
-#define VERILATOR_VERILATED_IMP_H_
-
-// clang-format off
-#if !defined(VERILATOR_VERILATED_CPP_) && !defined(VERILATOR_VERILATED_DPI_CPP_) \
-    && !defined(VERILATOR_VERILATED_VPI_CPP_) && !defined(VERILATOR_VERILATED_SAVE_CPP_)
-# error "verilated_imp.h only to be included by verilated*.cpp internals"
-#endif
-
-#include "verilatedos.h"
-#include "verilated.h"
-#include "verilated_syms.h"
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-// clang-format on
-
-class VerilatedScope;
-
-//======================================================================
-// Threaded message passing
-
-// Message, enqueued on an mtask, and consumed on the main eval thread
-class VerilatedMsg final {
-public:
-    // TYPES
-    struct Cmp final {
-        bool operator()(const VerilatedMsg& a, const VerilatedMsg& b) const {
-            return a.mtaskId() < b.mtaskId();
-        }
-    };
-
-private:
-    // MEMBERS
-    uint32_t m_mtaskId;  // MTask that did enqueue
-    std::function m_cb;  // Lambda to execute when message received
-public:
-    // CONSTRUCTORS
-    explicit VerilatedMsg(const std::function& cb)
-        : m_mtaskId{Verilated::mtaskId()}
-        , m_cb{cb} {}
-    ~VerilatedMsg() = default;
-    VerilatedMsg(const VerilatedMsg&) = default;
-    VerilatedMsg(VerilatedMsg&&) = default;
-    VerilatedMsg& operator=(const VerilatedMsg&) = default;
-    VerilatedMsg& operator=(VerilatedMsg&&) = default;
-    // METHODS
-    uint32_t mtaskId() const { return m_mtaskId; }
-    // Execute the lambda function
-    void run() const { m_cb(); }
-};
-
-// Each thread has a queue it pushes to
-// This assumes no thread starts pushing the next tick until the previous has drained.
-// If more aggressiveness is needed, a double-buffered scheme might work well.
-class VerilatedEvalMsgQueue final {
-    using VerilatedThreadQueue = std::multiset;
-
-    std::atomic m_depth;  // Current depth of queue (see comments below)
-
-    mutable VerilatedMutex m_mutex;  // Mutex protecting queue
-    VerilatedThreadQueue m_queue VL_GUARDED_BY(m_mutex);  // Message queue
-public:
-    // CONSTRUCTORS
-    VerilatedEvalMsgQueue()
-        : m_depth{0} {
-        assert(atomic_is_lock_free(&m_depth));
-    }
-    ~VerilatedEvalMsgQueue() = default;
-
-private:
-    VL_UNCOPYABLE(VerilatedEvalMsgQueue);
-
-public:
-    // METHODS
-    // Add message to queue (called by producer)
-    void post(const VerilatedMsg& msg) VL_MT_SAFE_EXCLUDES(m_mutex) {
-        const VerilatedLockGuard lock{m_mutex};
-        m_queue.insert(msg);  // Pass by value to copy the message into queue
-        ++m_depth;
-    }
-    // Service queue until completion (called by consumer)
-    void process() VL_MT_SAFE_EXCLUDES(m_mutex) {
-        // Tracking m_depth is redundant to e.g. getting the mutex and looking at queue size,
-        // but on the reader side it's 4x faster to test an atomic then getting a mutex
-        while (m_depth) {
-            // Wait for a message to be added to the queue
-            // We don't use unique_lock as want to unlock with the message copy still in scope
-            m_mutex.lock();
-            assert(!m_queue.empty());  // Otherwise m_depth is wrong
-            // Unfortunately to release the lock we need to copy the message
-            // (Or have the message be a pointer, but then new/delete cost on each message)
-            // We assume messages are small, so copy
-            const auto it = m_queue.begin();
-            const VerilatedMsg msg = *(it);
-            m_queue.erase(it);
-            m_mutex.unlock();
-            --m_depth;  // Ok if outside critical section as only this code checks the value
-            {
-                VL_DEBUG_IF(VL_DBG_MSGF("Executing callback from mtaskId=%d\n", msg.mtaskId()););
-                msg.run();
-            }
-        }
-    }
-};
-
-// Each thread has a local queue to build up messages until the end of the eval() call
-class VerilatedThreadMsgQueue final {
-    std::queue m_queue;
-
-public:
-    // CONSTRUCTORS
-    VerilatedThreadMsgQueue() = default;
-    ~VerilatedThreadMsgQueue() = default;
-    // The only call of destructor with a non-empty queue is a fatal error.
-    // So this does not flush the queue, as the destination queue is not known to this class.
-
-private:
-    VL_UNCOPYABLE(VerilatedThreadMsgQueue);
-    // METHODS
-    static VerilatedThreadMsgQueue& threadton() VL_MT_SAFE {
-        static thread_local VerilatedThreadMsgQueue t_s;
-        return t_s;
-    }
-
-public:
-    // Add message to queue, called by producer
-    static void post(const VerilatedMsg& msg) VL_MT_SAFE {
-        // Handle calls to threaded routines outside
-        // of any mtask -- if an initial block calls $finish, say.
-        if (Verilated::mtaskId() == 0) {
-            // No queueing, just do the action immediately
-            msg.run();
-        } else {
-            Verilated::endOfEvalReqdInc();
-            threadton().m_queue.push(msg);  // Pass by value to copy the message into queue
-        }
-    }
-    // Push all messages to the eval's queue
-    static void flush(VerilatedEvalMsgQueue* evalMsgQp) VL_MT_SAFE {
-        while (!threadton().m_queue.empty()) {
-            evalMsgQp->post(threadton().m_queue.front());
-            threadton().m_queue.pop();
-            Verilated::endOfEvalReqdDec();
-        }
-    }
-};
-
-// FILE* list constructed from a file-descriptor
-class VerilatedFpList final {
-    FILE* m_fp[31] = {};
-    std::size_t m_sz = 0;
-
-public:
-    using const_iterator = FILE* const*;
-    explicit VerilatedFpList() = default;
-    const_iterator begin() const { return m_fp; }
-    const_iterator end() const { return m_fp + m_sz; }
-    std::size_t size() const { return m_sz; }
-    static std::size_t capacity() { return 31; }
-    void push_back(FILE* fd) {
-        if (VL_LIKELY(size() < capacity())) m_fp[m_sz++] = fd;
-    }
-};
-
-//======================================================================
-// VerilatedContextImpData
-
-// Class for hidden implementation members inside VerilatedContext
-// Avoids needing std::unordered_map inside verilated.h
-class VerilatedContextImpData final {
-    friend class VerilatedContext;
-    friend class VerilatedContextImp;
-
-protected:
-    // Map of 
-    // Used by scopeInsert, scopeFind, scopeErase, scopeNameMap
-    mutable VerilatedMutex m_nameMutex;  // Protect m_nameMap
-    VerilatedScopeNameMap m_nameMap VL_GUARDED_BY(m_nameMutex);
-};
-
-//======================================================================
-// VerilatedContextImp
-// Class to "add" implementation-only methods to VerilatedContext
-
-class VerilatedContextImp final : VerilatedContext {
-    friend class VerilatedContext;
-
-    // MEMBERS - non-static not allowed, use only VerilatedContext
-    // Select initial value of otherwise uninitialized signals.
-    // Internal note: Globals may multi-construct, see verilated.cpp top.
-
-    // Medium speed, so uses singleton accessing
-    struct Statics final {
-        VerilatedMutex s_randMutex;  // Mutex protecting s_randSeedEpoch
-        // Number incrementing on each reseed, 0=illegal
-        int s_randSeedEpoch = 1;  // Reads ok, wish had a VL_WRITE_GUARDED_BY(s_randMutex)
-    };
-    static Statics& s() VL_MT_SAFE {
-        static Statics s_s;
-        return s_s;
-    }
-
-public:  // But only for verilated*.cpp
-    // CONSTRUCTORS - no data can live here, use only VerilatedContext
-    VerilatedContextImp() = delete;
-    ~VerilatedContextImp() = delete;
-
-    // METHODS - extending into VerilatedContext, call via impp()->
-
-    // Random seed handling
-    uint64_t randSeedDefault64() const VL_MT_SAFE;
-    static uint32_t randSeedEpoch() VL_MT_SAFE { return s().s_randSeedEpoch; }
-
-    // METHODS - timeformat
-    int timeFormatUnits() const VL_MT_SAFE {
-        if (m_s.m_timeFormatUnits == VerilatedContext::Serialized::UNITS_NONE)
-            return timeprecision();
-        return m_s.m_timeFormatUnits;
-    }
-    void timeFormatUnits(int value) VL_MT_SAFE { m_s.m_timeFormatUnits = value; }
-    int timeFormatPrecision() const VL_MT_SAFE { return m_s.m_timeFormatPrecision; }
-    void timeFormatPrecision(int value) VL_MT_SAFE { m_s.m_timeFormatPrecision = value; }
-    int timeFormatWidth() const VL_MT_SAFE { return m_s.m_timeFormatWidth; }
-    void timeFormatWidth(int value) VL_MT_SAFE { m_s.m_timeFormatWidth = value; }
-    std::string timeFormatSuffix() const VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
-        const VerilatedLockGuard lock{m_timeDumpMutex};
-        return m_timeFormatSuffix;
-    }
-    void timeFormatSuffix(const std::string& value) VL_MT_SAFE_EXCLUDES(m_timeDumpMutex) {
-        const VerilatedLockGuard lock{m_timeDumpMutex};
-        m_timeFormatSuffix = value;
-    }
-
-    // METHODS - arguments
-    std::string argPlusMatch(const char* prefixp) VL_MT_SAFE_EXCLUDES(m_argMutex);
-    std::pair argc_argv() VL_MT_SAFE_EXCLUDES(m_argMutex);
-
-    // METHODS - scope name - INTERNAL only for verilated*.cpp
-    void scopeInsert(const VerilatedScope* scopep) VL_MT_SAFE;
-    void scopeErase(const VerilatedScope* scopep) VL_MT_SAFE;
-
-    // METHODS - file IO - INTERNAL only for verilated*.cpp
-
-    IData fdNewMcd(const char* filenamep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
-        const VerilatedLockGuard lock{m_fdMutex};
-        if (m_fdFreeMct.empty()) return 0;
-        const IData idx = m_fdFreeMct.back();
-        m_fdFreeMct.pop_back();
-        m_fdps[idx] = std::fopen(filenamep, "w");
-        if (VL_UNLIKELY(!m_fdps[idx])) return 0;
-        return (1 << idx);
-    }
-    IData fdNew(const char* filenamep, const char* modep) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
-        FILE* const fp = std::fopen(filenamep, modep);
-        if (VL_UNLIKELY(!fp)) return 0;
-        // Bit 31 indicates it's a descriptor not a MCD
-        const VerilatedLockGuard lock{m_fdMutex};
-        if (m_fdFree.empty()) {
-            // Need to create more space in m_fdps and m_fdFree
-            const std::size_t start = std::max(31UL + 1UL + 3UL, m_fdps.size());
-            const std::size_t excess = 10;
-            m_fdps.resize(start + excess);
-            std::fill(m_fdps.begin() + start, m_fdps.end(), static_cast(nullptr));
-            m_fdFree.resize(excess);
-            for (std::size_t i = 0, id = start; i < m_fdFree.size(); ++i, ++id) {
-                m_fdFree[i] = static_cast(id);
-            }
-        }
-        const IData idx = m_fdFree.back();
-        m_fdFree.pop_back();
-        m_fdps[idx] = fp;
-        return (idx | (1UL << 31));  // bit 31 indicates not MCD
-    }
-    void fdFlush(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
-        const VerilatedLockGuard lock{m_fdMutex};
-        const VerilatedFpList fdlist = fdToFpList(fdi);
-        for (const auto& i : fdlist) std::fflush(i);
-    }
-    IData fdSeek(IData fdi, IData offset, IData origin) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
-        const VerilatedLockGuard lock{m_fdMutex};
-        const VerilatedFpList fdlist = fdToFpList(fdi);
-        if (VL_UNLIKELY(fdlist.size() != 1)) return ~0U;  // -1
-        return static_cast(
-            std::fseek(*fdlist.begin(), static_cast(offset), static_cast(origin)));
-    }
-    IData fdTell(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
-        const VerilatedLockGuard lock{m_fdMutex};
-        const VerilatedFpList fdlist = fdToFpList(fdi);
-        if (VL_UNLIKELY(fdlist.size() != 1)) return ~0U;  // -1
-        return static_cast(std::ftell(*fdlist.begin()));
-    }
-    void fdWrite(IData fdi, const std::string& output) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
-        const VerilatedLockGuard lock{m_fdMutex};
-        const VerilatedFpList fdlist = fdToFpList(fdi);
-        for (const auto& i : fdlist) {
-            if (VL_UNLIKELY(!i)) continue;
-            (void)fwrite(output.c_str(), 1, output.size(), i);
-        }
-    }
-    void fdClose(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
-        const VerilatedLockGuard lock{m_fdMutex};
-        if (VL_BITISSET_I(fdi, 31)) {
-            // Non-MCD case
-            const IData idx = VL_MASK_I(31) & fdi;
-            if (VL_UNLIKELY(idx >= m_fdps.size())) return;
-            if (VL_UNLIKELY(idx <= 2)) return;  // stdout/stdin/stderr
-            if (VL_UNLIKELY(!m_fdps[idx])) return;  // Already free
-            std::fclose(m_fdps[idx]);
-            m_fdps[idx] = nullptr;
-            m_fdFree.push_back(idx);
-        } else {
-            // MCD case
-            // Starts at 1 to skip stdout
-            fdi >>= 1;
-            for (int i = 1; (fdi != 0) && (i < 31); i++, fdi >>= 1) {
-                if (fdi & VL_MASK_I(1)) {
-                    std::fclose(m_fdps[i]);
-                    m_fdps[i] = nullptr;
-                    m_fdFreeMct.push_back(i);
-                }
-            }
-        }
-    }
-    FILE* fdToFp(IData fdi) VL_MT_SAFE_EXCLUDES(m_fdMutex) {
-        const VerilatedLockGuard lock{m_fdMutex};
-        const VerilatedFpList fdlist = fdToFpList(fdi);
-        if (VL_UNLIKELY(fdlist.size() != 1)) return nullptr;
-        return *fdlist.begin();
-    }
-
-private:
-    VerilatedFpList fdToFpList(IData fdi) VL_REQUIRES(m_fdMutex) {
-        VerilatedFpList fp;
-        // cppverilator-suppress integerOverflow shiftTooManyBitsSigned
-        if (VL_BITISSET_I(fdi, 31)) {
-            // Non-MCD case
-            const IData idx = fdi & VL_MASK_I(31);
-            switch (idx) {
-            case 0: fp.push_back(stdin); break;
-            case 1: fp.push_back(stdout); break;
-            case 2: fp.push_back(stderr); break;
-            default:
-                if (VL_LIKELY(idx < m_fdps.size())) fp.push_back(m_fdps[idx]);
-                break;
-            }
-        } else {
-            // MCD Case
-            if (fdi & 1) fp.push_back(stdout);
-            fdi >>= 1;
-            for (size_t i = 1; (fdi != 0) && (i < fp.capacity()); ++i, fdi >>= 1) {
-                if (fdi & VL_MASK_I(1)) fp.push_back(m_fdps[i]);
-            }
-        }
-        return fp;
-    }
-
-protected:
-    // METHODS - protected
-    void commandArgsGuts(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex);
-    void commandArgsAddGutsLock(int argc, const char** argv) VL_MT_SAFE_EXCLUDES(m_argMutex);
-    void commandArgsAddGuts(int argc, const char** argv) VL_REQUIRES(m_argMutex);
-    void commandArgVl(const std::string& arg);
-    bool commandArgVlString(const std::string& arg, const std::string& prefix,
-                            std::string& valuer);
-    bool commandArgVlUint64(const std::string& arg, const std::string& prefix, uint64_t& valuer,
-                            uint64_t min = std::numeric_limits::min(),
-                            uint64_t max = std::numeric_limits::max());
-    void commandArgDump() const VL_MT_SAFE_EXCLUDES(m_argMutex);
-};
-
-//======================================================================
-// VerilatedImp
-
-class VerilatedImpData final {
-    // Whole class is internal use only - Global information shared between verilated*.cpp files.
-    // All only medium-speed, so use singleton function
-protected:
-    friend class Verilated;
-    friend class VerilatedImp;
-
-    // TYPES
-    using UserMap = std::map, void*>;
-    using ExportNameMap = std::map;
-
-    // MEMBERS
-    // Nothing below here is save-restored; users expected to re-register appropriately
-
-    VerilatedMutex m_userMapMutex;  // Protect m_userMap
-    // For userInsert, userFind.  As indexed by pointer is common across contexts.
-    UserMap m_userMap VL_GUARDED_BY(m_userMapMutex);  // Map of <(scope,userkey), userData>
-
-    VerilatedMutex m_hierMapMutex;  // Protect m_hierMap
-    // Map that represents scope hierarchy
-    // Used by hierarchyAdd, hierarchyRemove, hierarchyMap
-    VerilatedHierarchyMap m_hierMap VL_GUARDED_BY(m_hierMapMutex);
-
-    // Slow - somewhat static:
-    VerilatedMutex m_exportMutex;  // Protect m_nameMap
-    // Map of 
-    // Used by exportInsert, exportFind, exportName.
-    // Export numbers same across all contexts as just a string-to-number conversion
-    ExportNameMap m_exportMap VL_GUARDED_BY(m_exportMutex);
-    int m_exportNext VL_GUARDED_BY(m_exportMutex) = 0;  // Next export funcnum
-
-    // No guard, as init-time loaded
-    std::vector m_exportFlatCbs;  // Exports when only single scope registered
-    std::vector m_exportFlatMulti;  // Multiple scopes registerd; cannot use m_exportScopes
-
-    // CONSTRUCTORS
-    VerilatedImpData() = default;
-};
-
-class VerilatedImp final {
-    // Whole class is internal use only - Global information shared between verilated*.cpp files.
-protected:
-    friend class Verilated;
-
-    // MEMBERS
-    static VerilatedImpData& s() VL_MT_SAFE {  // Singleton
-        static VerilatedImpData s_s;
-        return s_s;
-    }
-
-public:  // But only for verilated*.cpp
-    // CONSTRUCTORS
-    VerilatedImp() = default;
-    ~VerilatedImp() = default;
-
-private:
-    VL_UNCOPYABLE(VerilatedImp);
-
-public:
-    // METHODS - debug
-    static void versionDump() VL_MT_SAFE;
-
-    // METHODS - user scope tracking
-    // We implement this as a single large map instead of one map per scope.
-    // There's often many more scopes than userdata's and thus having a ~48byte
-    // per map overhead * N scopes would take much more space and cache thrashing.
-    // As scopep's are pointers, this implicitly handles multiple Context's
-    static void userInsert(const void* scopep, void* userKey, void* userData) VL_MT_SAFE {
-        const VerilatedLockGuard lock{s().m_userMapMutex};
-        const auto it = s().m_userMap.find(std::make_pair(scopep, userKey));
-        if (it != s().m_userMap.end()) {
-            it->second = userData;
-        } else {
-            s().m_userMap.emplace(std::make_pair(scopep, userKey), userData);
-        }
-    }
-    static void* userFind(const void* scopep, void* userKey) VL_MT_SAFE {
-        const VerilatedLockGuard lock{s().m_userMapMutex};
-        const auto& it = vlstd::as_const(s().m_userMap).find(std::make_pair(scopep, userKey));
-        if (VL_UNLIKELY(it == s().m_userMap.end())) return nullptr;
-        return it->second;
-    }
-
-    // METHODS - But only for verilated.cpp
-
-    // Symbol table destruction cleans up the entries for each scope.
-    static void userEraseScope(const VerilatedScope* scopep) VL_MT_SAFE {
-        // Slow ok - called once/scope on destruction, so we only iterate.
-        const VerilatedLockGuard lock{s().m_userMapMutex};
-        for (auto it = s().m_userMap.begin(); it != s().m_userMap.end();) {
-            if (it->first.first == scopep) {
-                s().m_userMap.erase(it++);
-            } else {
-                ++it;
-            }
-        }
-    }
-    static void userDump() VL_MT_SAFE {
-        const VerilatedLockGuard lock{s().m_userMapMutex};  // Avoid it changing in middle of dump
-        bool first = true;
-        for (const auto& i : s().m_userMap) {
-            if (first) {
-                VL_PRINTF_MT("  userDump:\n");
-                first = false;
-            }
-            VL_PRINTF_MT("    DPI_USER_DATA scope %p key %p: %p\n", i.first.first, i.first.second,
-                         i.second);
-        }
-    }
-
-    // METHODS - hierarchy - only for verilated*.cpp
-    static void hierarchyAdd(const VerilatedScope* fromp, const VerilatedScope* top) VL_MT_SAFE {
-        // Slow ok - called at construction for VPI accessible elements
-        const VerilatedLockGuard lock{s().m_hierMapMutex};
-        s().m_hierMap[fromp].push_back(top);
-    }
-    static void hierarchyRemove(const VerilatedScope* fromp,
-                                const VerilatedScope* top) VL_MT_SAFE {
-        // Slow ok - called at destruction for VPI accessible elements
-        const VerilatedLockGuard lock{s().m_hierMapMutex};
-        VerilatedHierarchyMap& map = s().m_hierMap;
-        if (map.find(fromp) == map.end()) return;
-        auto& scopes = map[fromp];
-        const auto it = find(scopes.begin(), scopes.end(), top);
-        if (it != scopes.end()) scopes.erase(it);
-    }
-    static void hierarchyClear() VL_MT_SAFE {
-        const VerilatedLockGuard lock{s().m_hierMapMutex};
-        VerilatedHierarchyMap& map = s().m_hierMap;
-        map.clear();
-    }
-    static const VerilatedHierarchyMap* hierarchyMap() VL_MT_SAFE_POSTINIT {
-        // Thread save only assuming this is called only after model construction completed
-        return &s().m_hierMap;
-    }
-
-    // METHODS - export names - only for verilated*.cpp
-
-    // Each function prototype is converted to a function number which we
-    // then use to index a 2D table also indexed by scope number, because we
-    // can't know at Verilation time what scopes will exist in other modules
-    // in the design that also happen to have our same callback function.
-    // Rather than a 2D map, the integer scheme saves 500ish ns on a likely
-    // miss at the cost of a multiply, and all lookups move to slowpath.
-private:
-    static int exportInsertName(const char* namep) VL_MT_SAFE {
-        const VerilatedLockGuard lock{s().m_exportMutex};
-        const auto it = s().m_exportMap.find(namep);
-        if (it == s().m_exportMap.end()) {
-            s().m_exportMap.emplace(namep, s().m_exportNext++);
-            return s().m_exportNext++;
-        } else {
-            return it->second;
-        }
-    }
-
-public:
-    static int exportInsert(const char* namep, void* cb) VL_MT_SAFE {
-        const int funcnum = VerilatedImp::exportInsertName(namep);
-        const VerilatedLockGuard lock{s().m_exportMutex};
-        // Slow ok - called once/function at creation
-        if (funcnum >= s().m_exportFlatCbs.size()) {
-            s().m_exportFlatCbs.resize(funcnum + 1);
-            s().m_exportFlatMulti.resize(funcnum + 1);
-        }
-        if (!s().m_exportFlatMulti[funcnum]) {
-            if (s().m_exportFlatCbs[funcnum] == cb) {  // Duplicate
-            } else if (!s().m_exportFlatCbs[funcnum]) {  // First
-                s().m_exportFlatCbs[funcnum] = cb;
-            } else {  // Multiple registrants
-                s().m_exportFlatCbs[funcnum] = nullptr;
-                s().m_exportFlatMulti[funcnum] = true;
-            }
-        }
-        return funcnum;
-    }
-    static int exportFindNum(const char* namep) VL_MT_SAFE {
-        const VerilatedLockGuard lock{s().m_exportMutex};
-        const auto& it = s().m_exportMap.find(namep);
-        if (VL_LIKELY(it != s().m_exportMap.end())) return it->second;
-        const std::string msg = "%Error: Testbench C called "s + namep
-                                + " but no such DPI export function name exists in ANY model";
-        VL_FATAL_MT("unknown", 0, "", msg.c_str());
-        return -1;
-    }
-    static const std::vector& exportFlatCbs() VL_MT_SAFE { return s().m_exportFlatCbs; }
-    static const char* exportName(int funcnum) VL_MT_SAFE {
-        // Slowpath; find name for given export; errors only so no map to reverse-map it
-        const VerilatedLockGuard lock{s().m_exportMutex};
-        for (const auto& i : s().m_exportMap) {
-            if (i.second == funcnum) return i.first;
-        }
-        return "*UNKNOWN*";
-    }
-    static void exportsDump() VL_MT_SAFE {
-        const VerilatedLockGuard lock{s().m_exportMutex};
-        bool first = true;
-        for (const auto& i : s().m_exportMap) {
-            if (first) {
-                VL_PRINTF_MT("  exportDump:\n");
-                first = false;
-            }
-            VL_PRINTF_MT("    DPI_EXPORT_NAME %05d: %s\n", i.second, i.first);
-        }
-    }
-    // We don't free up m_exportMap until the end, because we can't be sure
-    // what other models are using the assigned funcnum's.
-};
-
-//======================================================================
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_intrinsics.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_intrinsics.h
deleted file mode 100644
index 6e6f568f319..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_intrinsics.h
+++ /dev/null
@@ -1,45 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilator common target-specific intrinsics header
-///
-/// This file is not part of the Verilated public-facing API.
-///
-/// It is only for internal use; code using machine-specific intrinsics for
-/// optimization should include this header rather than directly including
-/// the target-specific headers. We provide macros to check for availability
-/// of instruction sets, and a common mechanism to disable them.
-///
-//*************************************************************************
-
-#ifndef VERILATOR_VERILATED_INTRINSICS_H_
-#define VERILATOR_VERILATED_INTRINSICS_H_
-
-// clang-format off
-
-// Use VL_PORTABLE_ONLY to disable all intrinsics based optimization
-#ifndef VL_PORTABLE_ONLY
-# if defined(__SSE2__) && !defined(VL_DISABLE_SSE2)
-#  define VL_HAVE_SSE2 1
-#  include 
-# endif
-# if defined(__AVX2__) && defined(VL_HAVE_SSE2) && !defined(VL_DISABLE_AVX2)
-#  define VL_HAVE_AVX2 1
-#  include 
-# endif
-#endif
-
-// clang-format on
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_probdist.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_probdist.cpp
deleted file mode 100644
index e2b75d3c74a..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_probdist.cpp
+++ /dev/null
@@ -1,240 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=========================================================================
-///
-/// \file
-/// \brief Verilated probability distribution implementation code
-///
-/// Verilator always adds this file to the Makefile for the linker.
-///
-/// Those macro/function/variable starting or ending in _ are internal,
-/// however many of the other function/macros here are also internal.
-///
-//=========================================================================
-
-#include "verilated_config.h"
-#include "verilatedos.h"
-
-#include "verilated.h"
-
-//===========================================================================
-// Dist
-
-static double _vl_dbase_uniform(IData& seedr, int32_t start, int32_t end) VL_MT_SAFE {
-    union u_s {
-        float s;
-        unsigned stemp;
-    } u;
-
-    const double d = 0.00000011920928955078125;
-    if (VL_UNLIKELY(seedr == 0)) seedr = 259341593;
-
-    double a;
-    double b;
-    if (VL_UNCOVERABLE(start >= end)) {  // With current usage shound't occur
-        a = 0.0;  // LCOV_EXCL_LINE
-        b = 2147483647.0;  // LCOV_EXCL_LINE
-    } else {
-        a = static_cast(start);
-        b = static_cast(end);
-    }
-    seedr = 69069 * seedr + 1;
-    u.stemp = seedr;
-    u.stemp = (u.stemp >> 9) | 0x3f800000;
-
-    double c = static_cast(u.s);
-    c = c + (c * d);
-    c = ((b - a) * (c - 1.0)) + a;
-    return c;
-}
-
-static double _vl_dbase_normal(IData& seedr, int32_t mean, int32_t deviation) VL_MT_SAFE {
-    double v1 = 0.0;
-    double v2 = 0.0;
-    double s = 1.0;
-    while ((s >= 1.0) || (s == 0.0)) {
-        v1 = _vl_dbase_uniform(seedr, -1, 1);
-        v2 = _vl_dbase_uniform(seedr, -1, 1);
-        s = v1 * v1 + v2 * v2;
-    }
-    s = v1 * std::sqrt(-2.0 * log(s) / s);
-    v1 = static_cast(deviation);
-    v2 = static_cast(mean);
-    return (s * v1 + v2);
-}
-
-static double _vl_dbase_exponential(IData& seedr, int32_t mean) VL_MT_SAFE {
-    double n = _vl_dbase_uniform(seedr, 0, 1);
-    if (n != 0) n = -log(n) * mean;
-    return n;
-}
-
-static double _vl_dbase_chi_square(IData& seedr, int32_t deg_of_free) VL_MT_SAFE {
-    double x;
-    if (deg_of_free % 2) {
-        x = _vl_dbase_normal(seedr, 0, 1);
-        x = x * x;
-    } else {
-        x = 0.0;
-    }
-    for (int32_t k = 2; k <= deg_of_free; k += 2) x = x + 2 * _vl_dbase_exponential(seedr, 1);
-    return x;
-}
-
-IData VL_DIST_CHI_SQUARE(IData& seedr, IData udf) VL_MT_SAFE {
-    const int32_t df = static_cast(udf);
-    if (VL_UNLIKELY(df <= 0)) {
-        // Chi_square distribution must have positive degree of freedom
-        return 0;
-    }
-    double r = _vl_dbase_chi_square(seedr, df);
-    int32_t i;
-    if (r >= 0) {
-        i = static_cast(r + 0.5);
-    } else {
-        r = -r;  // LCOV_EXCL_LINE
-        i = static_cast(r + 0.5);  // LCOV_EXCL_LINE
-        i = -i;  // LCOV_EXCL_LINE
-    }
-    return static_cast(i);
-}
-
-IData VL_DIST_ERLANG(IData& seedr, IData uk, IData umean) VL_MT_SAFE {
-    const int32_t k = static_cast(uk);
-    const int32_t mean = static_cast(umean);
-    if (VL_UNLIKELY(k <= 0)) {
-        // k-stage erlangian distribution must have positive k
-        return 0;
-    }
-    double x = 1.0;
-    for (int32_t i = 1; i <= k; ++i) x = x * _vl_dbase_uniform(seedr, 0, 1);
-    const double a = static_cast(mean);
-    const double b = static_cast(k);
-    double r = -a * log(x) / b;
-    int32_t i;
-    if (r >= 0) {
-        i = static_cast(r + 0.5);
-    } else {
-        r = -r;
-        i = static_cast(r + 0.5);
-        i = -i;
-    }
-    return static_cast(i);
-}
-
-IData VL_DIST_EXPONENTIAL(IData& seedr, IData umean) VL_MT_SAFE {
-    const int32_t mean = static_cast(umean);
-    if (VL_UNLIKELY(mean <= 0)) {
-        // Exponential distribution must have a positive mean
-        return 0;
-    }
-    int32_t i;
-    double r = _vl_dbase_exponential(seedr, mean);
-    if (r >= 0) {
-        i = static_cast(r + 0.5);
-    } else {
-        r = -r;  // LCOV_EXCL_LINE
-        i = static_cast(r + 0.5);  // LCOV_EXCL_LINE
-        i = -i;  // LCOV_EXCL_LINE
-    }
-    return static_cast(i);
-}
-
-IData VL_DIST_NORMAL(IData& seedr, IData umean, IData usd) VL_MT_SAFE {
-    const int32_t mean = static_cast(umean);
-    const int32_t sd = static_cast(usd);
-    double r = _vl_dbase_normal(seedr, mean, sd);
-    int32_t i;
-    if (r >= 0) {
-        i = static_cast(r + 0.5);
-    } else {
-        r = -r;
-        i = static_cast(r + 0.5);
-        i = -i;
-    }
-    return static_cast(i);
-}
-
-IData VL_DIST_POISSON(IData& seedr, IData umean) VL_MT_SAFE {
-    const int32_t mean = static_cast(umean);
-    if (VL_UNLIKELY(mean <= 0)) {
-        // Poisson distribution must have a positive mean
-        return 0;
-    }
-    int32_t i = 0;
-    double q = -static_cast(mean);
-    double p = exp(q);
-    q = _vl_dbase_uniform(seedr, 0, 1);
-    while (p < q) {
-        ++i;
-        q = _vl_dbase_uniform(seedr, 0, 1) * q;
-    }
-    return static_cast(i);
-}
-
-IData VL_DIST_T(IData& seedr, IData udf) VL_MT_SAFE {
-    const int32_t df = static_cast(udf);
-    if (VL_UNLIKELY(df <= 0)) {
-        // t distribution must have positive degree of freedom
-        return 0;
-    }
-    const double chi2 = _vl_dbase_chi_square(seedr, df);
-    const double div = chi2 / static_cast(df);
-    const double root = std::sqrt(div);
-    double r = _vl_dbase_normal(seedr, 0, 1) / root;
-    int32_t i;
-    if (r >= 0) {
-        i = static_cast(r + 0.5);
-    } else {
-        r = -r;
-        i = static_cast(r + 0.5);
-        i = -i;
-    }
-    return static_cast(i);
-}
-
-IData VL_DIST_UNIFORM(IData& seedr, IData ustart, IData uend) VL_MT_SAFE {
-    int32_t start = static_cast(ustart);
-    int32_t end = static_cast(uend);
-    if (VL_UNLIKELY(start >= end)) return start;
-    int32_t i;
-    if (end != std::numeric_limits::max()) {
-        ++end;
-        const double r = _vl_dbase_uniform(seedr, start, end);
-        if (r >= 0) {
-            i = static_cast(r);
-        } else {
-            i = static_cast(r - 1);
-        }
-        if (i < start) i = start;
-        if (i >= end) i = end - 1;
-    } else if (start != std::numeric_limits::min()) {
-        --start;
-        const double r = _vl_dbase_uniform(seedr, start, end) + 1.0;
-        if (r >= 0) {
-            i = static_cast(r);
-        } else {
-            i = static_cast(r - 1);  // LCOV_EXCL_LINE
-        }
-        if (i <= start) i = start + 1;
-        if (i > end) i = end;
-    } else {
-        double r = (_vl_dbase_uniform(seedr, start, end) + 2147483648.0) / 4294967295.0;
-        r = r * 4294967296.0 - 2147483648.0;
-        if (r >= 0) {
-            i = static_cast(r);
-        } else {
-            i = static_cast(r - 1);
-        }
-    }
-    return static_cast(i);
-}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.cpp
deleted file mode 100644
index 621482bf05f..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.cpp
+++ /dev/null
@@ -1,225 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2012-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated run-time profiling implementation code
-///
-//=============================================================================
-
-#include "verilatedos.h"
-
-#include "verilated_profiler.h"
-
-#include "verilated_threads.h"
-
-#include 
-#include 
-
-//=============================================================================
-// Globals
-
-// Internal note: Globals may multi-construct, see verilated.cpp top.
-
-thread_local VlExecutionProfiler::ExecutionTrace VlExecutionProfiler::t_trace;
-
-constexpr const char* const VlExecutionRecord::s_ascii[];
-
-//=============================================================================
-// VlExecutionProfiler implementation
-
-template 
-static size_t roundUptoMultipleOf(size_t value) {
-    static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
-    size_t mask = N - 1;
-    return (value + mask) & ~mask;
-}
-
-VlExecutionProfiler::VlExecutionProfiler(VerilatedContext& context)
-    : m_context{context} {
-    // Setup profiling on main thread
-    setupThread(0);
-}
-
-void VlExecutionProfiler::configure() {
-
-    if (VL_UNLIKELY(m_enabled)) {
-        --m_windowCount;
-        if (VL_UNLIKELY(m_windowCount == m_context.profExecWindow())) {
-            VL_DEBUG_IF(VL_DBG_MSGF("+ profile start collection\n"););
-            clear();  // Clear the profile after the cache warm-up cycles.
-            m_tickBegin = VL_CPU_TICK();
-        } else if (VL_UNLIKELY(m_windowCount == 0)) {
-            const uint64_t tickEnd = VL_CPU_TICK();
-            VL_DEBUG_IF(VL_DBG_MSGF("+ profile end\n"););
-            const std::string& fileName = m_context.profExecFilename();
-            dump(fileName.c_str(), tickEnd);
-            m_enabled = false;
-        }
-        return;
-    }
-
-    const uint64_t startReq = m_context.profExecStart() + 1;  // + 1, so we can start at time 0
-
-    if (VL_UNLIKELY(m_lastStartReq < startReq && VL_TIME_Q() >= m_context.profExecStart())) {
-        VL_DEBUG_IF(VL_DBG_MSGF("+ profile start warmup\n"););
-        VL_DEBUG_IF(assert(m_windowCount == 0););
-        m_enabled = true;
-        m_windowCount = m_context.profExecWindow() * 2;
-        m_lastStartReq = startReq;
-    }
-}
-
-VerilatedVirtualBase* VlExecutionProfiler::construct(VerilatedContext& context) {
-    VlExecutionProfiler* const selfp = new VlExecutionProfiler{context};
-    if (VlThreadPool* const threadPoolp = static_cast(context.threadPoolp())) {
-        for (int i = 0; i < threadPoolp->numThreads(); ++i) {
-            // Data to pass to worker thread initialization
-            struct Data final {
-                VlExecutionProfiler* const selfp;
-                const uint32_t threadId;
-            } data{selfp, static_cast(i + 1)};
-
-            // Initialize worker thread
-            threadPoolp->workerp(i)->addTask(
-                [](void* userp, bool) {
-                    Data* const datap = static_cast(userp);
-                    datap->selfp->setupThread(datap->threadId);
-                },
-                &data);
-
-            // Wait until initialization is complete
-            threadPoolp->workerp(i)->wait();
-        }
-    }
-    return selfp;
-}
-
-void VlExecutionProfiler::setupThread(uint32_t threadId) {
-    // Reserve some space in the thread-local profiling buffer, in order to try to avoid malloc
-    // while profiling.
-    t_trace.reserve(RESERVED_TRACE_CAPACITY);
-    // Register thread-local buffer in list of all buffers
-    bool exists;
-    {
-        const VerilatedLockGuard lock{m_mutex};
-        exists = !m_traceps.emplace(threadId, &t_trace).second;
-    }
-    if (VL_UNLIKELY(exists)) {
-        VL_FATAL_MT(__FILE__, __LINE__, "", "multiple initialization of profiler on some thread");
-    }
-}
-
-void VlExecutionProfiler::clear() VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    for (const auto& pair : m_traceps) {
-        ExecutionTrace* const tracep = pair.second;
-        const size_t reserve = roundUptoMultipleOf(tracep->size());
-        tracep->clear();
-        tracep->reserve(reserve);
-    }
-}
-
-void VlExecutionProfiler::dump(const char* filenamep, uint64_t tickEnd)
-    VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    VL_DEBUG_IF(VL_DBG_MSGF("+prof+exec writing to '%s'\n", filenamep););
-
-    FILE* const fp = std::fopen(filenamep, "w");
-    if (VL_UNLIKELY(!fp)) VL_FATAL_MT(filenamep, 0, "", "+prof+exec+file file not writable");
-
-    // TODO Perhaps merge with verilated_coverage output format, so can
-    // have a common merging and reporting tool, etc.
-    fprintf(fp, "VLPROFVERSION 2.2 # Verilator execution profile version 2.2\n");
-    fprintf(fp, "VLPROF arg +verilator+prof+exec+start+%" PRIu64 "\n",
-            Verilated::threadContextp()->profExecStart());
-    fprintf(fp, "VLPROF arg +verilator+prof+exec+window+%u\n",
-            Verilated::threadContextp()->profExecWindow());
-    std::string numa = "no threads";
-    if (const VlThreadPool* const threadPoolp
-        = static_cast(Verilated::threadContextp()->threadPoolp())) {
-        numa = threadPoolp->numaStatus();
-    }
-    fprintf(fp, "VLPROF info numa %s\n", numa.c_str());
-    // Note that VerilatedContext will by default create as many threads as there are hardware
-    // processors, but not all of them might be utilized. Report the actual number that has trace
-    // entries to avoid over-counting.
-    unsigned threads = 0;
-    for (const auto& pair : m_traceps) {
-        if (!pair.second->empty()) ++threads;
-    }
-    fprintf(fp, "VLPROF stat threads %u\n", threads);
-    fprintf(fp, "VLPROF stat yields %" PRIu64 "\n", VlMTaskVertex::yields());
-
-    // Copy /proc/cpuinfo into this output so verilator_gantt can be run on
-    // a different machine
-    {
-        const std::unique_ptr ifp{new std::ifstream{"/proc/cpuinfo"}};
-        if (!ifp->fail()) {
-            std::string line;
-            while (std::getline(*ifp, line)) { fprintf(fp, "VLPROFPROC %s\n", line.c_str()); }
-        }
-    }
-
-    for (const auto& pair : m_traceps) {
-        const uint32_t threadId = pair.first;
-        ExecutionTrace* const tracep = pair.second;
-        if (tracep->empty()) continue;
-        fprintf(fp, "VLPROFTHREAD %" PRIu32 "\n", threadId);
-
-        for (const VlExecutionRecord& er : *tracep) {
-            const char* const name = VlExecutionRecord::s_ascii[static_cast(er.m_type)];
-            const uint64_t time = er.m_tick - m_tickBegin;
-            fprintf(fp, "VLPROFEXEC %s %" PRIu64, name, time);
-
-            switch (er.m_type) {
-            case VlExecutionRecord::Type::SECTION_POP:
-            case VlExecutionRecord::Type::EXEC_GRAPH_BEGIN:
-            case VlExecutionRecord::Type::EXEC_GRAPH_END:
-                // No payload
-                fprintf(fp, "\n");
-                break;
-            case VlExecutionRecord::Type::MTASK_BEGIN: {
-                const auto& payload = er.m_payload.mtaskBegin;
-                if (payload.m_hierBlock[0] != '\0') {
-                    fprintf(fp, " id %u predictStart %u cpu %u hierBlock %s\n", payload.m_id,
-                            payload.m_predictStart, payload.m_cpu, payload.m_hierBlock);
-                } else {
-                    fprintf(fp, " id %u predictStart %u cpu %u\n", payload.m_id,
-                            payload.m_predictStart, payload.m_cpu);
-                }
-                break;
-            }
-            case VlExecutionRecord::Type::MTASK_END: {
-                const auto& payload = er.m_payload.mtaskEnd;
-                fprintf(fp, " predictCost %u\n", payload.m_predictCost);
-                break;
-            }
-            case VlExecutionRecord::Type::THREAD_SCHEDULE_WAIT_BEGIN:
-            case VlExecutionRecord::Type::THREAD_SCHEDULE_WAIT_END: {
-                const auto& payload = er.m_payload.threadScheduleWait;
-                fprintf(fp, " cpu %u\n", payload.m_cpu);
-                break;
-            }
-            case VlExecutionRecord::Type::SECTION_PUSH: {
-                const auto& payload = er.m_payload.sectionPush;
-                fprintf(fp, " %s\n", payload.m_name);
-                break;
-            }
-            default: abort();  // LCOV_EXCL_LINE
-            }
-        }
-    }
-    fprintf(fp, "VLPROF stat ticks %" PRIu64 "\n", tickEnd - m_tickBegin);
-
-    std::fclose(fp);
-}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.h
deleted file mode 100644
index b5fb7844526..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_profiler.h
+++ /dev/null
@@ -1,305 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2012-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated run-time profiling header
-///
-/// This file is not part of the Verilated public-facing API.
-/// It is only for internal use by Verilated library routines.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_PROFILER_H_
-#define VERILATOR_VERILATED_PROFILER_H_
-
-#include "verilatedos.h"
-
-#include "verilated.h"
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-class VlExecutionProfiler;
-class VlThreadPool;
-
-//=============================================================================
-// Macros to simplify generated code
-
-#define VL_EXEC_TRACE_ADD_RECORD(vlSymsp) \
-    if (VL_UNLIKELY((vlSymsp)->__Vm_executionProfilerp->enabled())) \
-    (vlSymsp)->__Vm_executionProfilerp->addRecord()
-
-//=============================================================================
-// Return high-precision counter for profiling, or 0x0 if not available
-VL_ATTR_ALWINLINE QData VL_CPU_TICK() {
-    uint64_t val;
-    VL_GET_CPU_TICK(val);
-    return val;
-}
-
-//=============================================================================
-// Private class used by VlExecutionProfiler
-
-#define _VL_FOREACH_APPLY(macro, arg) macro(arg, #arg)
-
-// clang-format off
-#define FOREACH_VlExecutionRecord_TYPE(macro) \
-    _VL_FOREACH_APPLY(macro, SECTION_PUSH) \
-    _VL_FOREACH_APPLY(macro, SECTION_POP) \
-    _VL_FOREACH_APPLY(macro, MTASK_BEGIN) \
-    _VL_FOREACH_APPLY(macro, MTASK_END) \
-    _VL_FOREACH_APPLY(macro, THREAD_SCHEDULE_WAIT_BEGIN) \
-    _VL_FOREACH_APPLY(macro, THREAD_SCHEDULE_WAIT_END) \
-    _VL_FOREACH_APPLY(macro, EXEC_GRAPH_BEGIN) \
-    _VL_FOREACH_APPLY(macro, EXEC_GRAPH_END)
-// clang-format on
-
-class VlExecutionRecord final {
-    friend class VlExecutionProfiler;
-
-    // TYPES
-    enum class Type : uint8_t {
-#define VL_FOREACH_MACRO(id, name) id,
-        FOREACH_VlExecutionRecord_TYPE(VL_FOREACH_MACRO)
-#undef VL_FOREACH_MACRO
-    };
-
-    static constexpr const char* const s_ascii[] = {
-#define VL_FOREACH_MACRO(id, name) name,
-        FOREACH_VlExecutionRecord_TYPE(VL_FOREACH_MACRO)
-#undef VL_FOREACH_MACRO
-    };
-
-    union Payload {
-        struct {
-            const char* m_name;  // Name of section being entered
-        } sectionPush;
-        struct {
-            uint32_t m_id;  // MTask id
-            uint32_t m_predictStart;  // Time scheduler predicted would start
-            uint32_t m_cpu;  // Executing CPU id
-            const char* m_hierBlock;  // Name of a hier block with this mtask
-        } mtaskBegin;
-        struct {
-            uint32_t m_predictCost;  // How long scheduler predicted would take
-        } mtaskEnd;
-        struct {
-            uint32_t m_cpu;  // Executing CPU id
-        } threadScheduleWait;
-    };
-
-    // STATE
-    // Layout below allows efficient packing.
-    const uint64_t m_tick = VL_CPU_TICK();  // Tick at construction
-    Payload m_payload;  // The record payload
-    Type m_type;  // The record type
-    static_assert(alignof(uint64_t) >= alignof(Payload), "Padding not allowed");
-    static_assert(alignof(Payload) >= alignof(Type), "Padding not allowed");
-
-public:
-    // CONSTRUCTOR
-    VlExecutionRecord() = default;
-
-    // METHODS
-    void sectionPush(const char* name) {
-        m_payload.sectionPush.m_name = name;
-        m_type = Type::SECTION_PUSH;
-    }
-    void sectionPop() { m_type = Type::SECTION_POP; }
-    void mtaskBegin(uint32_t id, uint32_t predictStart, const char* hierBlock) {
-        m_payload.mtaskBegin.m_id = id;
-        m_payload.mtaskBegin.m_predictStart = predictStart;
-        m_payload.mtaskBegin.m_cpu = VlOs::getcpu();
-        m_payload.mtaskBegin.m_hierBlock = hierBlock;
-        m_type = Type::MTASK_BEGIN;
-    }
-    void mtaskEnd(uint32_t predictCost) {
-        m_payload.mtaskEnd.m_predictCost = predictCost;
-        m_type = Type::MTASK_END;
-    }
-    void threadScheduleWaitBegin() {
-        m_payload.threadScheduleWait.m_cpu = VlOs::getcpu();
-        m_type = Type::THREAD_SCHEDULE_WAIT_BEGIN;
-    }
-    void threadScheduleWaitEnd() {
-        m_payload.threadScheduleWait.m_cpu = VlOs::getcpu();
-        m_type = Type::THREAD_SCHEDULE_WAIT_END;
-    }
-    void execGraphBegin() { m_type = Type::EXEC_GRAPH_BEGIN; }
-    void execGraphEnd() { m_type = Type::EXEC_GRAPH_END; }
-};
-
-static_assert(std::is_trivially_destructible::value,
-              "VlExecutionRecord should be trivially destructible for fast buffer clearing");
-
-//=============================================================================
-// VlExecutionProfiler is for collecting profiling data about model execution
-
-class VlExecutionProfiler final : public VerilatedVirtualBase {
-    // CONSTANTS
-
-    // In order to try to avoid dynamic memory allocations during the actual profiling phase,
-    // trace buffers are pre-allocated to be able to hold [a multiple] of this many records.
-    static constexpr size_t RESERVED_TRACE_CAPACITY = 4096;
-
-    // TYPES
-
-    // Execution traces are recorded into thread local vectors. We can append records of profiling
-    // events to this vector with very low overhead, and then dump them out later. This prevents
-    // the overhead of printf/malloc/IO from corrupting the profiling data. It's super cheap to
-    // append a VlProfileRec struct on the end of a pre-allocated vector; this is the only cost we
-    // pay in real-time during a profiling cycle. Internal note: Globals may multi-construct, see
-    // verilated.cpp top.
-    using ExecutionTrace = std::vector;
-
-    // STATE
-    VerilatedContext& m_context;  // The context this profiler is under
-    static thread_local ExecutionTrace t_trace;  // thread-local trace buffers
-    mutable VerilatedMutex m_mutex;
-    // Map from thread id to &t_trace of given thread
-    std::map m_traceps VL_GUARDED_BY(m_mutex);
-
-    bool m_enabled = false;  // Is profiling currently enabled
-
-    uint64_t m_tickBegin = 0;  // Sample time (rdtsc() on x86) at beginning of collection
-    uint64_t m_lastStartReq = 0;  // Last requested profiling start (in simulation time)
-    uint32_t m_windowCount = 0;  // Track our position in the cache warmup and profile window
-
-public:
-    // CONSTRUCTOR
-    explicit VlExecutionProfiler(VerilatedContext& context);
-    ~VlExecutionProfiler() override = default;
-
-    // METHODS
-
-    // Is profiling enabled
-    bool enabled() const { return m_enabled; }
-    // Append a trace record to the trace buffer of the current thread
-    static VlExecutionRecord& addRecord() {
-        t_trace.emplace_back();
-        return t_trace.back();
-    }
-    // Configure profiler (called in beginning of 'eval')
-    void configure();
-    // Setup profiling on a particular thread;
-    void setupThread(uint32_t threadId);
-    // Clear all profiling data
-    void clear() VL_MT_SAFE_EXCLUDES(m_mutex);
-    // Write profiling data into file
-    void dump(const char* filenamep, uint64_t tickEnd) VL_MT_SAFE_EXCLUDES(m_mutex);
-
-    // Passed to VerilatedContext to create the VlExecutionProfiler profiler instance
-    static VerilatedVirtualBase* construct(VerilatedContext& context);
-};
-
-//=============================================================================
-// VlPgoProfiler is for collecting profiling data for PGO
-
-template 
-class VlPgoProfiler final {
-    // TYPES
-    struct Record final {
-        const std::string m_name;  // Hashed name of mtask/etc
-        const size_t m_counterNumber = 0;  // Which counter has data
-    };
-
-    // Counters are stored packed, all together to reduce cache effects
-    std::array m_counters{};  // Time spent on this record
-    std::vector m_records;  // Record information
-    // An original cost of a profiled hier block. During Verilation with
-    // collected profiling data, costs of hier blocks change thus hashes of
-    // original mtasks does not match those from the previous,
-    // instrumented, run. We shall not assume that a single top-level mtask
-    // will correspond to the hier block as multiple hier block DPIs can be
-    // contracted into a single mtask.  Therefore, the old cost, from
-    // previous instrumented run, is used to stabilize profiled scheduling.
-    const uint64_t m_currentHierBlockCost;
-
-public:
-    // METHODS
-    explicit VlPgoProfiler(uint64_t currentHierBlockCost = 0)
-        : m_currentHierBlockCost{currentHierBlockCost} {}
-    ~VlPgoProfiler() = default;
-    VL_UNMOVABLE(VlPgoProfiler);
-    VL_UNCOPYABLE(VlPgoProfiler);
-    void writeHeader(const std::string& filename) VL_MT_SAFE;
-    void write(const char* modelp, const std::string& filename) VL_MT_SAFE;
-    void addCounter(size_t counter, const std::string& name) {
-        VL_DEBUG_IF(assert(counter < N_Entries););
-        m_records.emplace_back(Record{name, counter});
-    }
-    void startCounter(size_t counter) {
-        // -= so when we add end time in stopCounter, the net effect is adding the difference,
-        // without needing to hold onto a temporary
-        m_counters[counter] -= VL_CPU_TICK();
-    }
-    void stopCounter(size_t counter) { m_counters[counter] += VL_CPU_TICK(); }
-};
-
-template 
-void VlPgoProfiler::writeHeader(const std::string& filename) VL_MT_SAFE {
-    static VerilatedMutex s_mutex;
-    const VerilatedLockGuard lock{s_mutex};
-
-    // On the first call we create the file.  On later calls we append.
-    // So when we have multiple models in an executable, possibly even
-    // running on different threads, each will have a different symtab so
-    // each will collect is own data correctly.  However when each is
-    // destroyed we need to get all the data, not keep overwriting and only
-    // get the last model's data.
-
-    FILE* const fp = std::fopen(filename.c_str(), "w");
-    if (VL_UNLIKELY(!fp)) {
-        VL_FATAL_MT(filename.c_str(), 0, "", "+prof+vlt+file file not writable");
-    }
-
-    VL_DEBUG_IF(VL_DBG_MSGF("+prof+vlt+file initializing '%s'\n", filename.c_str()););
-
-    // TODO Perhaps merge with verilated_coverage output format, so can
-    // have a common merging and reporting tool, etc.
-    fprintf(fp, "// Verilated model profile-guided optimization data dump file\n");
-    fprintf(fp, "`verilator_config\n");
-
-    std::fclose(fp);
-}
-
-template 
-void VlPgoProfiler::write(const char* modelp, const std::string& filename) VL_MT_SAFE {
-    static VerilatedMutex s_mutex;
-    const VerilatedLockGuard lock{s_mutex};
-
-    FILE* const fp = std::fopen(filename.c_str(), "a");
-    if (VL_UNLIKELY(!fp)) {
-        VL_FATAL_MT(filename.c_str(), 0, "", "+prof+vlt+file file not writable");
-    }
-
-    VL_DEBUG_IF(VL_DBG_MSGF("+prof+vlt+file writing to '%s'\n", filename.c_str()););
-
-    if (m_currentHierBlockCost) {
-        fprintf(fp, "profile_data -hier-dpi \"%s\" -cost 64'd%" PRIu64 "\n", modelp,
-                m_currentHierBlockCost);
-    }
-
-    for (const Record& rec : m_records) {
-        fprintf(fp, "profile_data -model \"%s\" -mtask \"%s\" -cost 64'd%" PRIu64 "\n", modelp,
-                rec.m_name.c_str(), m_counters[rec.m_counterNumber]);
-    }
-
-    std::fclose(fp);
-}
-
-#endif
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.cpp
deleted file mode 100644
index 2a1f3d562c2..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.cpp
+++ /dev/null
@@ -1,966 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=========================================================================
-///
-/// \file
-/// \brief Verilated randomization implementation code
-///
-/// This file must be compiled and linked against all Verilated objects
-/// that use randomization features.
-///
-/// See the internals documentation docs/internals.rst for details.
-///
-//=========================================================================
-
-#include "verilated_random.h"
-
-#include 
-#include 
-#include 
-#include 
-#include 
-
-#define _VL_SOLVER_HASH_LEN 1
-#define _VL_SOLVER_HASH_LEN_TOTAL 4
-
-// clang-format off
-#if defined(__unix__) || defined(__unix) || (defined(__APPLE__) && defined(__MACH__))
-# define _VL_SOLVER_PIPE  // Allow pipe SMT solving.  Needs fork()
-#endif
-
-#ifdef _VL_SOLVER_PIPE
-# include 
-# include 
-#endif
-
-#if defined(_WIN32) || defined(__MINGW32__)
-# include   // open, read, write, close
-#endif
-// clang-format on
-
-class VlRProcess final : private std::streambuf, public std::iostream {
-    static constexpr int BUFFER_SIZE = 4096;
-    const char* const* m_cmd = nullptr;  // fork() process argv
-#ifdef _VL_SOLVER_PIPE
-    pid_t m_pid = 0;  // fork() process id
-#else
-    int m_pid = 0;  // fork() process id - always zero as disabled
-#endif
-    bool m_pidExited = true;  // If subprocess has exited and can be opened
-    int m_pidStatus = 0;  // fork() process exit status, valid if m_pidExited
-    int m_writeFd = -1;  // File descriptor TO subprocess
-    int m_readFd = -1;  // File descriptor FROM subprocess
-    char m_readBuf[BUFFER_SIZE];
-    char m_writeBuf[BUFFER_SIZE];
-
-public:
-    typedef std::streambuf::traits_type traits_type;
-
-protected:
-    int overflow(int c = traits_type::eof()) override {
-        char c2 = static_cast(c);
-        if (pbase() == pptr()) return 0;
-        size_t size = pptr() - pbase();
-        ssize_t n = ::write(m_writeFd, pbase(), size);
-        // VL_PRINTF_MT("solver-write '%s'\n", std::string(pbase(), size).c_str());
-        if (n == -1) perror("write");
-        if (n <= 0) {
-            wait_report();
-            return traits_type::eof();
-        }
-        if (n == size)
-            setp(m_writeBuf, m_writeBuf + sizeof(m_writeBuf));
-        else
-            setp(m_writeBuf + n, m_writeBuf + sizeof(m_writeBuf));
-        if (c != traits_type::eof()) sputc(c2);
-        return 0;
-    }
-    int underflow() override {
-        sync();
-        ssize_t n = ::read(m_readFd, m_readBuf, sizeof(m_readBuf));
-        if (n == -1) perror("read");
-        if (n <= 0) {
-            wait_report();
-            return traits_type::eof();
-        }
-        setg(m_readBuf, m_readBuf, m_readBuf + n);
-        return traits_type::to_int_type(m_readBuf[0]);
-    }
-    int sync() override {
-        overflow();
-        return 0;
-    }
-
-public:
-    explicit VlRProcess(const char* const* const cmd = nullptr)
-        : std::streambuf{}
-        , std::iostream{this}
-        , m_cmd{cmd} {
-        open(cmd);
-    }
-
-    void wait_report() {
-        if (m_pidExited) return;
-#ifdef _VL_SOLVER_PIPE
-        if (waitpid(m_pid, &m_pidStatus, 0) != m_pid) return;
-        if (m_pidStatus) {
-            std::stringstream msg;
-            msg << "Subprocess command `" << m_cmd[0];
-            for (const char* const* arg = m_cmd + 1; *arg; ++arg) msg << ' ' << *arg;
-            msg << "' failed: ";
-            if (WIFSIGNALED(m_pidStatus))
-                msg << strsignal(WTERMSIG(m_pidStatus))
-                    << (WCOREDUMP(m_pidStatus) ? " (core dumped)" : "");
-            else if (WIFEXITED(m_pidStatus))
-                msg << "exit status " << WEXITSTATUS(m_pidStatus);
-            const std::string str = msg.str();
-            VL_WARN_MT("", 0, "VlRProcess", str.c_str());
-        }
-#endif
-        m_pidExited = true;
-        m_pid = 0;
-        closeFds();
-    }
-
-    void closeFds() {
-        if (m_writeFd != -1) {
-            close(m_writeFd);
-            m_writeFd = -1;
-        }
-        if (m_readFd != -1) {
-            close(m_readFd);
-            m_readFd = -1;
-        }
-    }
-
-    bool open(const char* const* const cmd) {
-        setp(std::begin(m_writeBuf), std::end(m_writeBuf));
-        setg(m_readBuf, m_readBuf, m_readBuf);
-#ifdef _VL_SOLVER_PIPE
-        if (!cmd || !cmd[0]) return false;
-        m_cmd = cmd;
-        int fd_stdin[2];  // Can't use std::array
-        int fd_stdout[2];  // Can't use std::array
-        constexpr int P_RD = 0;
-        constexpr int P_WR = 1;
-
-        if (pipe(fd_stdin) != 0) {
-            perror("VlRProcess::open: pipe");
-            return false;
-        }
-        if (pipe(fd_stdout) != 0) {
-            perror("VlRProcess::open: pipe");
-            close(fd_stdin[P_RD]);
-            close(fd_stdin[P_WR]);
-            return false;
-        }
-
-        if (fd_stdin[P_RD] <= 2 || fd_stdin[P_WR] <= 2 || fd_stdout[P_RD] <= 2
-            || fd_stdout[P_WR] <= 2) {
-            // We'd have to rearrange all of the FD usages in this case.
-            // Too unlikely; verilator isn't a daemon.
-            fprintf(stderr, "stdin/stdout closed before pipe opened\n");
-            close(fd_stdin[P_RD]);
-            close(fd_stdin[P_WR]);
-            close(fd_stdout[P_RD]);
-            close(fd_stdout[P_WR]);
-            return false;
-        }
-
-        const pid_t pid = fork();
-        if (pid < 0) {
-            perror("VlRProcess::open: fork");
-            close(fd_stdin[P_RD]);
-            close(fd_stdin[P_WR]);
-            close(fd_stdout[P_RD]);
-            close(fd_stdout[P_WR]);
-            return false;
-        }
-        if (pid == 0) {
-            // Child
-            close(fd_stdin[P_WR]);
-            dup2(fd_stdin[P_RD], STDIN_FILENO);
-            close(fd_stdout[P_RD]);
-            dup2(fd_stdout[P_WR], STDOUT_FILENO);
-            execvp(cmd[0], const_cast(cmd));
-            std::stringstream msg;
-            msg << "VlRProcess::open: execvp(" << cmd[0] << ")";
-            const std::string str = msg.str();
-            perror(str.c_str());
-            _exit(127);
-        }
-        // Parent
-        m_pid = pid;
-        m_pidExited = false;
-        m_pidStatus = 0;
-        m_readFd = fd_stdout[P_RD];
-        m_writeFd = fd_stdin[P_WR];
-
-        close(fd_stdin[P_RD]);
-        close(fd_stdout[P_WR]);
-
-        return true;
-#else
-        return false;
-#endif
-    }
-};
-
-static VlRProcess& getSolver() {
-    static VlRProcess s_solver;
-    static bool s_done = false;
-    if (s_done) return s_solver;
-    s_done = true;
-
-    static std::vector s_argv;
-    static std::string s_program = Verilated::threadContextp()->solverProgram();
-    s_argv.emplace_back(&s_program[0]);
-    for (char* arg = &s_program[0]; *arg; ++arg) {
-        if (*arg == ' ') {
-            *arg = '\0';
-            s_argv.emplace_back(arg + 1);
-        }
-    }
-    s_argv.emplace_back(nullptr);
-
-    const char* const* const cmd = &s_argv[0];
-    s_solver.open(cmd);
-    s_solver << "(set-logic QF_ABV)\n";
-    s_solver << "(check-sat)\n";
-    s_solver << "(reset)\n";
-    std::string s;
-    getline(s_solver, s);
-    if (s == "sat") return s_solver;
-
-    std::stringstream msg;
-    msg << "Unable to communicate with SAT solver, please check its installation or specify a "
-           "different one in VERILATOR_SOLVER environment variable.\n";
-    msg << " ... Tried: $";
-    for (const char* const* arg = cmd; *arg; ++arg) msg << ' ' << *arg;
-    msg << '\n';
-    const std::string str = msg.str();
-    VL_WARN_MT("", 0, "randomize", str.c_str());
-
-    while (getline(s_solver, s)) {}
-    return s_solver;
-}
-
-static std::string readUntilBalanced(std::istream& stream) {
-    std::string result;
-    std::string token;
-    int parenCount = 1;
-    while (stream >> token) {
-        for (const char c : token) {
-            if (c == '(') {
-                ++parenCount;
-            } else if (c == ')') {
-                --parenCount;
-            }
-        }
-        result += token + " ";
-        if (parenCount == 0) break;
-    }
-    return result;
-}
-
-static std::string parseNestedSelect(const std::string& nested_select_expr,
-                                     std::vector& indices) {
-    std::istringstream nestedStream(nested_select_expr);
-    std::string name, idx;
-    nestedStream >> name;
-    if (name == "(select") {
-        const std::string further_nested_expr = readUntilBalanced(nestedStream);
-        name = parseNestedSelect(further_nested_expr, indices);
-    }
-    std::getline(nestedStream, idx, ')');
-    indices.push_back(idx);
-    return name;
-}
-
-//======================================================================
-// VlRandomizer:: Methods
-
-void VlRandomVar::emitGetValue(std::ostream& s) const { s << ' ' << m_name; }
-void VlRandomVar::emitExtract(std::ostream& s, int i) const {
-    s << " ((_ extract " << i << ' ' << i << ") " << m_name << ')';
-}
-void VlRandomVar::emitType(std::ostream& s) const { s << "(_ BitVec " << width() << ')'; }
-int VlRandomVar::totalWidth() const { return m_width; }
-static bool parseSMTNum(int obits, WDataOutP owp, const std::string& val) {
-    int i;
-    for (i = 0; val[i] && val[i] != '#'; ++i) {}
-    if (val[i++] != '#') return false;
-    switch (val[i++]) {
-    case 'b': _vl_vsss_based(owp, obits, 1, &val[i], 0, val.size() - i); break;
-    case 'o': _vl_vsss_based(owp, obits, 3, &val[i], 0, val.size() - i); break;
-    case 'h':  // FALLTHRU
-    case 'x': _vl_vsss_based(owp, obits, 4, &val[i], 0, val.size() - i); break;
-    default:
-        VL_WARN_MT(__FILE__, __LINE__, "randomize",
-                   "Internal: Unable to parse solver's randomized number");
-        return false;
-    }
-    return true;
-}
-bool VlRandomVar::set(const std::string& idx, const std::string& val) const {
-    VlWide qowp;
-    VL_SET_WQ(qowp, 0ULL);
-    WDataOutP owp = qowp;
-    const int obits = width();
-    VlWide qiwp;
-    VL_SET_WQ(qiwp, 0ULL);
-    if (!idx.empty() && !parseSMTNum(64, qiwp, idx)) return false;
-    const int nidx = qiwp[0];
-    if (obits > VL_QUADSIZE) owp = reinterpret_cast(datap(nidx));
-    if (!parseSMTNum(obits, owp, val)) return false;
-
-    if (obits <= VL_BYTESIZE) {
-        CData* const p = static_cast(datap(nidx));
-        *p = VL_CLEAN_II(obits, obits, owp[0]);
-    } else if (obits <= VL_SHORTSIZE) {
-        SData* const p = static_cast(datap(nidx));
-        *p = VL_CLEAN_II(obits, obits, owp[0]);
-    } else if (obits <= VL_IDATASIZE) {
-        IData* const p = static_cast(datap(nidx));
-        *p = VL_CLEAN_II(obits, obits, owp[0]);
-    } else if (obits <= VL_QUADSIZE) {
-        QData* const p = static_cast(datap(nidx));
-        *p = VL_CLEAN_QQ(obits, obits, VL_SET_QW(owp));
-    } else {
-        _vl_clean_inplace_w(obits, owp);
-    }
-    return true;
-}
-
-void VlRandomizer::randomConstraint(std::ostream& os, VlRNG& rngr, int bits) {
-    const IData hash = VL_RANDOM_RNG_I(rngr) & ((1 << bits) - 1);
-    int varBits = 0;
-    for (const auto& var : m_vars) varBits += var.second->totalWidth();
-    os << "(= #b";
-    for (int i = bits - 1; i >= 0; i--) os << (VL_BITISSET_I(hash, i) ? '1' : '0');
-    if (bits > 1) os << " (concat";
-    for (int i = 0; i < bits; ++i) {
-        IData varBitsLeft = varBits;
-        IData varBitsWant = (varBits + 1) / 2;
-        if (varBits > 2) os << " (bvxor";
-        for (const auto& var : m_vars) {
-            for (int j = 0; j < var.second->totalWidth(); j++, varBitsLeft--) {
-                const bool doEmit = (VL_RANDOM_RNG_I(rngr) % varBitsLeft) < varBitsWant;
-                if (doEmit) {
-                    var.second->emitExtract(os, j);
-                    if (--varBitsWant == 0) break;
-                }
-            }
-            if (varBitsWant == 0) break;
-        }
-        if (varBits > 2) os << ')';
-    }
-    if (bits > 1) os << ')';
-    os << ')';
-}
-
-size_t VlRandomizer::hashConstraints() const {
-    size_t h = 0;
-    for (const auto& c : m_constraints) {
-        h ^= std::hash{}(c) + 0x9e3779b9 + (h << 6) + (h >> 2);
-    }
-    return h;
-}
-
-void VlRandomizer::enumerateRandcValues(const std::string& varName, VlRNG& rngr) {
-    std::vector values;
-    const auto varIt = m_vars.find(varName);
-    if (varIt == m_vars.end()) return;
-    const int width = varIt->second->width();
-
-    std::iostream& os = getSolver();
-    if (!os) return;
-
-    // Set up a single incremental solver session for enumeration
-    os << "(set-option :produce-models true)\n";
-    os << "(set-logic QF_ABV)\n";
-    os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
-    os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
-
-    // Declare all variables (solver needs full context for cross-var constraints)
-    for (const auto& var : m_vars) {
-        if (var.second->dimension() > 0) {
-            auto arrVarsp = std::make_shared(m_arr_vars);
-            var.second->setArrayInfo(arrVarsp);
-        }
-        os << "(declare-fun " << var.first << " () ";
-        var.second->emitType(os);
-        os << ")\n";
-    }
-
-    // Assert all user constraints
-    for (const std::string& constraint : m_constraints) {
-        os << "(assert (= #b1 " << constraint << "))\n";
-    }
-
-    // Incrementally enumerate all valid values for this randc variable
-    while (true) {
-        os << "(check-sat)\n";
-        std::string sat;
-        do { std::getline(os, sat); } while (sat.empty());
-        if (sat != "sat") break;
-
-        // Read just this variable's value
-        os << "(get-value (" << varName << "))\n";
-        char c;
-        os >> c;  // '('
-        os >> c;  // '('
-        std::string name, value;
-        os >> name;  // Consume variable name token from solver output
-        (void)name;
-        std::getline(os, value, ')');
-        os >> c;  // ')'
-
-        // Parse the SMT value to uint64_t
-        VlWide qowp;
-        VL_SET_WQ(qowp, 0ULL);
-        if (!parseSMTNum(width, qowp, value)) break;
-        const uint64_t numVal = (width <= 32) ? qowp[0] : VL_SET_QW(qowp);
-
-        values.push_back(numVal);
-
-        // Exclude this value for next iteration (incremental)
-        os << "(assert (not (= " << varName << " (_ bv" << numVal << " " << width << "))))\n";
-    }
-
-    os << "(reset)\n";
-
-    // Shuffle using Fisher-Yates
-    for (size_t i = values.size(); i > 1; --i) {
-        const size_t j = VL_RANDOM_RNG_I(rngr) % i;
-        std::swap(values[i - 1], values[j]);
-    }
-
-    m_randcValueQueues[varName] = std::deque(values.begin(), values.end());
-}
-
-bool VlRandomizer::next(VlRNG& rngr) {
-    if (m_vars.empty() && m_unique_arrays.empty()) return true;
-    for (const std::string& baseName : m_unique_arrays) {
-        const auto it = m_vars.find(baseName);
-        const uint32_t size = m_unique_array_sizes.at(baseName);
-
-        if (it != m_vars.end()) {
-            std::string distinctExpr = "(__Vbv (distinct";
-            for (uint32_t i = 0; i < size; ++i) {
-                char hexIdx[12];
-                sprintf(hexIdx, "#x%08x", i);
-                distinctExpr += " (select " + it->first + " " + hexIdx + ")";
-            }
-            distinctExpr += "))";
-            m_constraints.push_back(distinctExpr);
-        }
-    }
-
-    // Randc queue-based cycling: enumerate valid values once, then pop per call
-    if (!m_randcVarNames.empty()) {
-        const size_t currentHash = hashConstraints();
-        // Invalidate queues if constraints changed (e.g., constraint_mode toggled)
-        if (currentHash != m_randcConstraintHash) {
-            m_randcValueQueues.clear();
-            m_randcConstraintHash = currentHash;
-        }
-        // Refill empty queues (start of new cycle)
-        for (const auto& name : m_randcVarNames) {
-            auto& queue = m_randcValueQueues[name];
-            if (queue.empty()) enumerateRandcValues(name, rngr);
-        }
-    }
-
-    // Pop randc values from queues (will be pinned in solver)
-    std::map randcPinned;
-    for (const auto& name : m_randcVarNames) {
-        auto& queue = m_randcValueQueues[name];
-        if (queue.empty()) return false;  // No valid values exist
-        randcPinned[name] = queue.front();
-        queue.pop_front();
-    }
-
-    // If solve-before constraints are present, use phased solving
-    if (!m_solveBefore.empty()) return nextPhased(rngr);
-
-    std::iostream& os = getSolver();
-    if (!os) return false;
-
-    os << "(set-option :produce-models true)\n";
-    os << "(set-logic QF_ABV)\n";
-    os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
-    os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
-    for (const auto& var : m_vars) {
-        if (var.second->dimension() > 0) {
-            auto arrVarsp = std::make_shared(m_arr_vars);
-            var.second->setArrayInfo(arrVarsp);
-        }
-        os << "(declare-fun " << var.first << " () ";
-        var.second->emitType(os);
-        os << ")\n";
-    }
-
-    for (const std::string& constraint : m_constraints) {
-        os << "(assert (= #b1 " << constraint << "))\n";
-    }
-
-    // Pin randc values from pre-enumerated queues
-    for (const auto& pair : randcPinned) {
-        const int w = m_vars.at(pair.first)->width();
-        os << "(assert (= " << pair.first << " (_ bv" << pair.second << " " << w << ")))\n";
-    }
-
-    os << "(check-sat)\n";
-
-    bool sat = parseSolution(os, true);
-    if (!sat) {
-        // If unsat, use named assertions to get unsat-core
-        os << "(reset)\n";
-        os << "(set-option :produce-unsat-cores true)\n";
-        os << "(set-logic QF_ABV)\n";
-        os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
-        os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
-        for (const auto& var : m_vars) {
-            if (var.second->dimension() > 0) {
-                auto arrVarsp = std::make_shared(m_arr_vars);
-                var.second->setArrayInfo(arrVarsp);
-            }
-            os << "(declare-fun " << var.first << " () ";
-            var.second->emitType(os);
-            os << ")\n";
-        }
-        int j = 0;
-        for (const std::string& constraint : m_constraints) {
-            os << "(assert (! (= #b1 " << constraint << ") :named cons" << j << "))\n";
-            j++;
-        }
-        os << "(check-sat)\n";
-        sat = parseSolution(os, true);
-        (void)sat;
-        os << "(reset)\n";
-        return false;
-    }
-    for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
-        os << "(assert ";
-        randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
-        os << ")\n";
-        os << "\n(check-sat)\n";
-        sat = parseSolution(os, false);
-        (void)sat;
-    }
-
-    os << "(reset)\n";
-    return true;
-}
-
-bool VlRandomizer::parseSolution(std::iostream& os, bool log) {
-    std::string sat;
-    do { std::getline(os, sat); } while (sat == "");
-    if (sat == "unsat") {
-        if (!log) return false;
-        os << "(get-unsat-core) \n";
-        sat.clear();
-        std::getline(os, sat);
-        std::vector numbers;
-        std::string currentNum;
-        for (char c : sat) {
-            if (std::isdigit(c)) {
-                currentNum += c;
-                numbers.push_back(std::stoi(currentNum));
-                currentNum.clear();
-            }
-        }
-        if (Verilated::threadContextp()->warnUnsatConstr()) {
-            for (int n : numbers) {
-                if (n < m_constraints_line.size()) {
-                    const std::string& constraint_info = m_constraints_line[n];
-                    // Parse "filename:linenum   source" format
-                    size_t colon_pos = constraint_info.find(':');
-                    if (colon_pos != std::string::npos) {
-                        std::string filename = constraint_info.substr(0, colon_pos);
-                        size_t space_pos = constraint_info.find("   ", colon_pos);
-                        std::string linenum_str;
-                        std::string source;
-                        if (space_pos != std::string::npos) {
-                            linenum_str
-                                = constraint_info.substr(colon_pos + 1, space_pos - colon_pos - 1);
-                            source = constraint_info.substr(space_pos + 3);
-                        } else {
-                            linenum_str = constraint_info.substr(colon_pos + 1);
-                        }
-                        const int linenum = std::stoi(linenum_str);
-                        std::string msg = "UNSATCONSTR: Unsatisfied constraint";
-                        if (!source.empty()) {
-                            // Trim leading whitespace and add quotes
-                            size_t start = source.find_first_not_of(" \t");
-                            if (start != std::string::npos) {
-                                msg += ": '" + source.substr(start) + "'";
-                            }
-                        }
-                        VL_WARN_MT(filename.c_str(), linenum, "", msg.c_str());
-                    } else {
-                        VL_PRINTF("%%Warning-UNSATCONSTR: Unsatisfied constraint: %s\n",
-                                  constraint_info.c_str());
-                    }
-                }
-            }
-        }
-        return false;
-    }
-    if (sat != "sat") {
-        std::stringstream msg;
-        msg << "Internal: Solver error: " << sat;
-        const std::string str = msg.str();
-        VL_WARN_MT(__FILE__, __LINE__, "randomize", str.c_str());
-        return false;
-    }
-
-    os << "(get-value (";
-    for (const auto& var : m_vars) {
-        if (var.second->dimension() > 0) {
-            auto arrVarsp = std::make_shared(m_arr_vars);
-            var.second->setArrayInfo(arrVarsp);
-        }
-        var.second->emitGetValue(os);
-    }
-    os << "))\n";
-    // Quasi-parse S-expression of the form ((x #xVALUE) (y #bVALUE) (z #xVALUE))
-    char c;
-    os >> c;
-    if (c != '(') {
-        VL_WARN_MT(__FILE__, __LINE__, "randomize",
-                   "Internal: Unable to parse solver's response: invalid S-expression");
-        return false;
-    }
-    while (true) {
-        os >> c;
-        if (c == ')') break;
-        if (c != '(') {
-            VL_WARN_MT(__FILE__, __LINE__, "randomize",
-                       "Internal: Unable to parse solver's response: invalid S-expression");
-            return false;
-        }
-        std::string name, idx, value;
-        std::vector indices;
-        os >> name;
-        indices.clear();
-        if (name == "(select") {
-            const std::string selectExpr = readUntilBalanced(os);
-            name = parseNestedSelect(selectExpr, indices);
-        }
-        std::getline(os, value, ')');
-        const auto it = m_vars.find(name);
-        if (it == m_vars.end()) continue;
-        const VlRandomVar& varr = *it->second;
-        if (m_randmodep && !varr.randModeIdxNone()) {
-            if (!m_randmodep->at(varr.randModeIdx())) continue;
-        }
-        if (!indices.empty()) {
-            std::ostringstream oss;
-            oss << varr.name();
-            for (const auto& hex_index : indices) {
-                const size_t start = hex_index.find_first_not_of(" ");
-                if (start == std::string::npos || hex_index.substr(start, 2) != "#x") {
-                    VL_FATAL_MT(__FILE__, __LINE__, "randomize",
-                                "hex_index contains invalid format");
-                    continue;
-                }
-                std::string trimmed_hex = hex_index.substr(start + 2);
-
-                if (trimmed_hex.size() <= 8) {  // Small numbers: <= 32 bits
-                    // Convert to decimal and output directly
-                    oss << "[" << std::to_string(std::stoll(trimmed_hex, nullptr, 16)) << "]";
-                } else {  // Large numbers: > 32 bits
-                    // Trim leading zeros and handle empty case
-                    trimmed_hex.erase(0, trimmed_hex.find_first_not_of('0'));
-                    oss << "[" << (trimmed_hex.empty() ? "0" : trimmed_hex) << "]";
-                }
-            }
-            const std::string indexed_name = oss.str();
-
-            const auto iti = std::find_if(m_arr_vars.begin(), m_arr_vars.end(),
-                                          [&indexed_name](const auto& entry) {
-                                              return entry.second->m_name == indexed_name;
-                                          });
-            if (iti != m_arr_vars.end()) {
-                std::ostringstream ss;
-                ss << "#x" << std::hex << std::setw(8) << std::setfill('0')
-                   << iti->second->m_index;
-                idx = ss.str();
-            } else {
-                VL_FATAL_MT(__FILE__, __LINE__, "randomize",
-                            "indexed_name not found in m_arr_vars");
-            }
-        }
-        varr.set(idx, value);
-    }
-    return true;
-}
-
-void VlRandomizer::hard(std::string&& constraint, const char* filename, uint32_t linenum,
-                        const char* source) {
-    m_constraints.emplace_back(std::move(constraint));
-    // Format constraint location: "filename:linenum   source"
-    if (filename[0] != '\0' || source[0] != '\0') {
-        std::string line;
-        if (filename[0] != '\0') {
-            line = std::string(filename) + ":" + std::to_string(linenum);
-            if (source[0] != '\0') line += "   " + std::string(source);
-        } else {
-            line = source;
-        }
-        m_constraints_line.emplace_back(std::move(line));
-    }
-}
-
-void VlRandomizer::clearConstraints() {
-    m_constraints.clear();
-    m_constraints_line.clear();
-    m_solveBefore.clear();
-    // Keep m_vars for class member randomization
-}
-
-void VlRandomizer::clearAll() {
-    m_constraints.clear();
-    m_vars.clear();
-    m_randcVarNames.clear();
-    m_randcValueQueues.clear();
-    m_randcConstraintHash = 0;
-}
-
-void VlRandomizer::markRandc(const char* name) { m_randcVarNames.insert(name); }
-
-void VlRandomizer::solveBefore(const char* beforeName, const char* afterName) {
-    m_solveBefore.emplace_back(std::string(beforeName), std::string(afterName));
-}
-
-bool VlRandomizer::nextPhased(VlRNG& rngr) {
-    // Phased solving for solve...before constraints.
-    // Variables are solved in layers determined by topological sort of the
-    // solve-before dependency graph. Each layer is solved with ALL constraints
-    // (preserving the solution space) but earlier layers' values are pinned.
-
-    // Step 1: Build dependency graph (before -> {after vars})
-    std::map> graph;
-    std::map inDegree;
-    std::set solveBeforeVars;
-
-    for (const auto& pair : m_solveBefore) {
-        const std::string& before = pair.first;
-        const std::string& after = pair.second;
-        // Only consider variables that are actually registered
-        if (m_vars.find(before) == m_vars.end() || m_vars.find(after) == m_vars.end()) continue;
-        graph[before].insert(after);
-        solveBeforeVars.insert(before);
-        solveBeforeVars.insert(after);
-        if (inDegree.find(before) == inDegree.end()) inDegree[before] = 0;
-        if (inDegree.find(after) == inDegree.end()) inDegree[after] = 0;
-    }
-
-    // Compute in-degrees (after depends on before, so edge is before->after,
-    // but for solving order: before has no incoming edge from after)
-    // Actually: "solve x before y" means x should be solved first.
-    // Dependency: y depends on x. Edge: x -> y. in-degree of y increases.
-    for (const auto& entry : graph) {
-        for (const auto& to : entry.second) { inDegree[to]++; }
-    }
-
-    // Step 2: Topological sort into layers (Kahn's algorithm)
-    std::vector> layers;
-    std::set remaining = solveBeforeVars;
-
-    while (!remaining.empty()) {
-        std::vector currentLayer;
-        for (const auto& var : remaining) {
-            if (inDegree[var] == 0) currentLayer.push_back(var);
-        }
-        if (currentLayer.empty()) {
-            VL_WARN_MT("", 0, "randomize", "Circular dependency in solve-before constraints");
-            return false;
-        }
-        std::sort(currentLayer.begin(), currentLayer.end());
-        for (const auto& var : currentLayer) {
-            remaining.erase(var);
-            if (graph.count(var)) {
-                for (const auto& to : graph[var]) { inDegree[to]--; }
-            }
-        }
-        layers.push_back(std::move(currentLayer));
-    }
-
-    // If only one layer, no phased solving needed -- fall through to normal path
-    // (all solve_before vars are independent, no actual ordering required)
-    if (layers.size() <= 1) {
-        // Clear solve_before temporarily and call normal next()
-        const auto saved = std::move(m_solveBefore);
-        m_solveBefore.clear();
-        const bool result = next(rngr);
-        m_solveBefore = std::move(saved);
-        return result;
-    }
-
-    // Step 3: Solve phase by phase
-    std::map solvedValues;  // varName -> SMT value literal
-
-    for (size_t phase = 0; phase < layers.size(); phase++) {
-        const bool isFinalPhase = (phase == layers.size() - 1);
-
-        std::iostream& os = getSolver();
-        if (!os) return false;
-
-        // Solver session setup
-        os << "(set-option :produce-models true)\n";
-        os << "(set-logic QF_ABV)\n";
-        os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
-        os << "(define-fun __Vbool ((v (_ BitVec 1))) Bool (= #b1 v))\n";
-
-        // Declare ALL variables
-        for (const auto& var : m_vars) {
-            if (var.second->dimension() > 0) {
-                auto arrVarsp = std::make_shared(m_arr_vars);
-                var.second->setArrayInfo(arrVarsp);
-            }
-            os << "(declare-fun " << var.first << " () ";
-            var.second->emitType(os);
-            os << ")\n";
-        }
-
-        // Pin all previously solved variables
-        for (const auto& entry : solvedValues) {
-            os << "(assert (= " << entry.first << " " << entry.second << "))\n";
-        }
-
-        // Assert ALL constraints
-        for (const std::string& constraint : m_constraints) {
-            os << "(assert (= #b1 " << constraint << "))\n";
-        }
-
-        // Initial check-sat WITHOUT diversity (guaranteed sat if constraints are consistent)
-        os << "(check-sat)\n";
-
-        if (isFinalPhase) {
-            // Final phase: use parseSolution to write ALL values to memory
-            bool sat = parseSolution(os, true);
-            if (!sat) {
-                os << "(reset)\n";
-                return false;
-            }
-            // Diversity loop (same as normal next())
-            for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
-                os << "(assert ";
-                randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
-                os << ")\n";
-                os << "\n(check-sat)\n";
-                sat = parseSolution(os, false);
-                (void)sat;
-            }
-            os << "(reset)\n";
-        } else {
-            // Intermediate phase: extract values for current layer variables only
-            std::string satResponse;
-            do { std::getline(os, satResponse); } while (satResponse.empty());
-
-            if (satResponse != "sat") {
-                os << "(reset)\n";
-                return false;
-            }
-
-            // Build get-value variable list for this layer
-            const auto& layerVars = layers[phase];
-            auto getValueCmd = [&]() {
-                os << "(get-value (";
-                for (const auto& varName : layerVars) {
-                    if (m_vars.count(varName)) os << varName << " ";
-                }
-                os << "))\n";
-            };
-
-            // Helper to parse ((name1 value1) (name2 value2) ...) response
-            auto parseGetValue = [&]() -> bool {
-                char c;
-                os >> c;  // outer '('
-                while (true) {
-                    os >> c;
-                    if (c == ')') break;  // outer closing
-                    if (c != '(') return false;
-                    std::string name;
-                    os >> name;
-
-                    // Read value handling nested parens for (_ bvN W) format
-                    os >> std::ws;
-                    std::string value;
-                    char firstChar;
-                    os.get(firstChar);
-                    if (firstChar == '(') {
-                        // Compound value like (_ bv5 32)
-                        value = "(";
-                        int depth = 1;
-                        while (depth > 0) {
-                            os.get(c);
-                            value += c;
-                            if (c == '(')
-                                depth++;
-                            else if (c == ')')
-                                depth--;
-                        }
-                        // Read closing ')' of the pair
-                        os >> c;
-                    } else {
-                        // Atom value like #x00000005 or #b101
-                        value += firstChar;
-                        while (os.get(c) && c != ')') { value += c; }
-                        // Trim trailing whitespace
-                        const size_t end = value.find_last_not_of(" \t\n\r");
-                        if (end != std::string::npos) value = value.substr(0, end + 1);
-                    }
-
-                    solvedValues[name] = value;
-                }
-                return true;
-            };
-
-            // Get baseline values (deterministic, always valid)
-            getValueCmd();
-            if (!parseGetValue()) {
-                os << "(reset)\n";
-                return false;
-            }
-
-            // Try diversity: add random constraint, re-check. If sat, get
-            // updated (more diverse) values. If unsat, keep baseline values.
-            os << "(assert ";
-            randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
-            os << ")\n";
-            os << "(check-sat)\n";
-            satResponse.clear();
-            do { std::getline(os, satResponse); } while (satResponse.empty());
-            if (satResponse == "sat") {
-                getValueCmd();
-                parseGetValue();
-            }
-
-            os << "(reset)\n";
-        }
-    }
-
-    return true;
-}
-
-#ifdef VL_DEBUG
-void VlRandomizer::dump() const {
-    for (const auto& var : m_vars) {
-        VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name().c_str());
-    }
-    for (const std::string& c : m_constraints) VL_PRINTF("Constraint: %s\n", c.c_str());
-}
-#endif
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.h
deleted file mode 100644
index 53c09f07113..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_random.h
+++ /dev/null
@@ -1,677 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilated randomization header
-///
-/// This file is included automatically by Verilator in some of the C++ files
-/// it generates if randomization features are used.
-///
-/// This file is not part of the Verilated public-facing API.
-/// It is only for internal use.
-///
-/// See the internals documentation docs/internals.rst for details.
-///
-//*************************************************************************
-#ifndef VERILATOR_VERILATED_RANDOM_H_
-#define VERILATOR_VERILATED_RANDOM_H_
-
-#include "verilated.h"
-
-#include 
-#include 
-#include 
-#include 
-#include 
-
-//=============================================================================
-
-// VlRandomExpr and subclasses represent expressions for the constraint solver.
-class ArrayInfo final {
-public:
-    const std::string
-        m_name;  // Name of the array variable, including index notation (e.g., arr[2][1])
-    void* const m_datap;  // Reference to the array variable data
-    const int m_index;  // Flattened (1D) index of the array element
-    const std::vector m_indices;  // Multi-dimensional indices of the array element
-    const std::vector m_idxWidths;  // Multi-dimensional indices' bit widths
-
-    ArrayInfo(const std::string& name, void* datap, int index, const std::vector& indices,
-              const std::vector& idxWidths)
-        : m_name{name}
-        , m_datap{datap}
-        , m_index{index}
-        , m_indices{indices}
-        , m_idxWidths{idxWidths} {}
-};
-using ArrayInfoMap = std::map>;
-
-class VlRandomVar VL_NOT_FINAL {
-    std::string m_name;  // Variable name
-    void* const m_datap;  // Reference to variable data
-    const int m_width;  // Variable width in bits
-    const int m_dimension;  //Variable dimension, default is 0
-    const std::uint32_t m_randModeIdx;  // rand_mode index
-
-public:
-    VlRandomVar(const std::string& name, int width, void* datap, int dimension,
-                std::uint32_t randModeIdx)
-        : m_name{name}
-        , m_datap{datap}
-        , m_width{width}
-        , m_dimension{dimension}
-        , m_randModeIdx{randModeIdx} {}
-    virtual ~VlRandomVar() = default;
-    std::string name() const { return m_name; }
-    int width() const { return m_width; }
-    int dimension() const { return m_dimension; }
-    virtual void* datap(int idx) const { return m_datap; }
-    std::uint32_t randModeIdx() const { return m_randModeIdx; }
-    bool randModeIdxNone() const { return randModeIdx() == std::numeric_limits::max(); }
-    bool set(const std::string& idx, const std::string& val) const;
-    virtual void emitGetValue(std::ostream& s) const;
-    virtual void emitExtract(std::ostream& s, int i) const;
-    virtual void emitType(std::ostream& s) const;
-    virtual int totalWidth() const;
-    mutable std::shared_ptr m_arrVarsRefp;
-    void setArrayInfo(const std::shared_ptr& arrVarsRefp) const {
-        m_arrVarsRefp = arrVarsRefp;
-    }
-    mutable std::map count_cache;
-    int countMatchingElements(const ArrayInfoMap& arr_vars, const std::string& base_name) const {
-        if (VL_LIKELY(count_cache.find(base_name) != count_cache.end()))
-            return count_cache[base_name];
-        int count = 0;
-        for (int index = 0; arr_vars.find(base_name + std::to_string(index)) != arr_vars.end();
-             ++index) {
-            ++count;
-        }
-        count_cache[base_name] = count;
-        return count;
-    }
-};
-template 
-class VlRandomArrayVarTemplate final : public VlRandomVar {
-public:
-    VlRandomArrayVarTemplate(const std::string& name, int width, void* datap, int dimension,
-                             std::uint32_t randModeIdx)
-        : VlRandomVar{name, width, datap, dimension, randModeIdx} {}
-    void* datap(int idx) const override {
-        const std::string indexed_name = name() + std::to_string(idx);
-        const auto it = m_arrVarsRefp->find(indexed_name);
-        if (it != m_arrVarsRefp->end()) {
-            return it->second->m_datap;
-        } else {
-            VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
-            return nullptr;  // LCOV_EXCL_BR_LINE
-        }
-    }
-    void emitHexs(std::ostream& s, const std::vector& indices, const size_t bit_width,
-                  size_t idx) const {
-        for (int j = bit_width - 4; j >= 0; j -= 4) {
-            s << "0123456789abcdef"[(indices[idx] >> j) & 0xf];
-        }
-    }
-    void emitSelect(std::ostream& s, const std::vector& indices,
-                    const std::vector& idxWidths) const {
-        const size_t num_indices = idxWidths.size();
-        size_t wide_size = 0;
-
-        for (size_t idx = 0; idx < num_indices; ++idx) s << "(select ";
-        s << name();
-
-        for (size_t idx = 0; idx < num_indices; ++idx) {
-            const size_t bit_width = idxWidths[idx];
-            s << " #x";
-
-            const size_t emit_count = (bit_width > 32) ? (idxWidths[idx] / 32) : 1;
-
-            for (size_t i = 0; i < emit_count; ++i) {
-                emitHexs(s, indices, (bit_width > 32) ? 32 : bit_width, wide_size + i);
-            }
-
-            wide_size += (idxWidths[idx] > 32) ? (idxWidths[idx] / 32) : 1;
-            s << ")";
-        }
-    }
-    void emitGetValue(std::ostream& s) const override {
-        const int elementCounts = countMatchingElements(*m_arrVarsRefp, name());
-        for (int i = 0; i < elementCounts; ++i) {
-            const std::string indexed_name = name() + std::to_string(i);
-            const auto it = m_arrVarsRefp->find(indexed_name);
-            if (it != m_arrVarsRefp->end()) {
-                const std::vector& indices = it->second->m_indices;
-                const std::vector& idxWidths = it->second->m_idxWidths;
-                emitSelect(s, indices, idxWidths);
-            } else {
-                VL_FATAL_MT(__FILE__, __LINE__, "randomize",
-                            "indexed_name not found in m_arr_vars");
-            }
-        }
-    }
-    void emitType(std::ostream& s) const override {
-        const std::string indexed_name = name() + std::to_string(0);
-        const auto it = m_arrVarsRefp->find(indexed_name);
-        if (it != m_arrVarsRefp->end()) {
-            const std::vector& idxWidths = it->second->m_idxWidths;
-            if (dimension() > 0) {
-                for (int i = 0; i < dimension(); ++i) {
-                    s << "(Array (_ BitVec " << idxWidths[i] << ") ";
-                }
-                s << "(_ BitVec " << width() << ")";
-                for (int i = 0; i < dimension(); ++i) s << ")";
-            }
-        } else {
-            VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
-        }
-    }
-    int totalWidth() const override {
-        const int elementCounts = countMatchingElements(*m_arrVarsRefp, name());
-        return width() * elementCounts;
-    }
-    void emitExtract(std::ostream& s, int i) const override {
-        const int j = i / width();
-        i = i % width();
-        s << " ((_ extract " << i << ' ' << i << ')';
-        const std::string indexed_name = name() + std::to_string(j);
-        const auto it = m_arrVarsRefp->find(indexed_name);
-        if (it != m_arrVarsRefp->end()) {
-            const std::vector& indices = it->second->m_indices;
-            const std::vector& idxWidths = it->second->m_idxWidths;
-            emitSelect(s, indices, idxWidths);
-        } else {
-            VL_FATAL_MT(__FILE__, __LINE__, "randomize", "indexed_name not found in m_arr_vars");
-        }
-        s << ')';
-    }
-};
-
-//=============================================================================
-// Object holding constraints and variable references.
-class VlRandomizer VL_NOT_FINAL {
-    // MEMBERS
-    std::vector m_constraints;  // Solver-dependent constraints
-    std::vector
-        m_constraints_line;  // fileline content of the constraint for unsat constraints
-    std::map> m_vars;  // Solver-dependent
-                                                                       // variables
-    ArrayInfoMap m_arr_vars;  // Tracks each element in array structures for iteration
-    std::vector m_unique_arrays;
-    std::map m_unique_array_sizes;
-    const VlQueue* m_randmodep = nullptr;  // rand_mode state;
-    int m_index = 0;  // Internal counter for key generation
-    std::set m_randcVarNames;  // Names of randc variables for cyclic tracking
-    std::map>
-        m_randcValueQueues;  // Remaining values per randc var (queue-based cycling)
-    size_t m_randcConstraintHash = 0;  // Hash of constraints when queues were built
-    std::vector>
-        m_solveBefore;  // Solve-before ordering pairs (beforeVar, afterVar)
-
-    // PRIVATE METHODS
-    void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
-    bool parseSolution(std::iostream& file, bool log = false);
-    void enumerateRandcValues(const std::string& varName, VlRNG& rngr);
-    size_t hashConstraints() const;
-    bool nextPhased(VlRNG& rngr);  // Phased solving for solve...before
-
-public:
-    // CONSTRUCTORS
-    VlRandomizer() = default;
-    ~VlRandomizer() = default;
-
-    // METHODS
-    // Finds the next solution satisfying the constraints
-    bool next(VlRNG& rngr);
-
-    // ---  Process the key for associative array  ---
-
-    // process_key: Handle integral keys (<= 32-bit)
-    template 
-    typename std::enable_if::value && (sizeof(T_Key) <= 4)>::type
-    process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index,
-                const std::string& base_name, size_t& idx_width) {
-        integral_index.push_back(static_cast(key));
-        indexed_name
-            = base_name + "[" + std::to_string(integral_index[integral_index.size() - 1]) + "]";
-        idx_width = sizeof(T_Key) * 8;
-    }
-
-    // process_key: Handle integral keys (> 32-bit), split into 2 x 32-bit segments
-    template 
-    typename std::enable_if::value && (sizeof(T_Key) > 4)>::type
-    process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index,
-                const std::string& base_name, size_t& idx_width) {
-        constexpr size_t segment_bits = 32;
-        constexpr T_Key mask = (static_cast(1) << segment_bits) - 1;
-        integral_index.push_back(static_cast(key >> segment_bits));
-        integral_index.push_back(static_cast(key & mask));
-
-        std::ostringstream hex_stream;
-        hex_stream << std::hex << key;
-        std::string index_string = hex_stream.str();
-        index_string.erase(0, index_string.find_first_not_of('0'));
-        index_string = index_string.empty() ? "0" : index_string;
-
-        indexed_name = base_name + "[" + index_string + "]";
-
-        idx_width = sizeof(T_Key) * 8;
-    }
-
-    // process_key: Handle wide keys (VlWide-like), segment is 32-bit per element
-    template 
-    typename std::enable_if::value>::type
-    process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index,
-                const std::string& base_name, size_t& idx_width) {
-        std::ostringstream hex_stream;
-        for (size_t i = key.size(); i > 0; --i) {
-            const size_t segment_value = key.at(i - 1);
-            hex_stream << std::hex << segment_value;
-            integral_index.push_back(segment_value);
-        }
-        std::string index_string = hex_stream.str();
-        index_string.erase(0, index_string.find_first_not_of('0'));
-        index_string = index_string.empty() ? "0" : index_string;
-
-        indexed_name = base_name + "[" + index_string + "]";
-        idx_width = key.size() * 32;
-    }
-
-    // process_key: Handle string key, encoded as 128-bit hex
-    template 
-    typename std::enable_if::value>::type
-    process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index,
-                const std::string& base_name, size_t& idx_width) {
-        // Convert the input string to its ASCII hexadecimal representation
-        std::ostringstream oss;
-        for (unsigned char c : key) {
-            oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(c);
-        }
-        std::string hex_str = oss.str();
-        // Ensure the hex string is exactly 128 bits (32 hex characters)
-        hex_str = hex_str.size() > 32 ? hex_str.substr(0, 32)
-                                      : std::string(32 - hex_str.size(), '0') + hex_str;
-
-        // Split the hex string into 4 segments (32-bit per segment)
-        integral_index.clear();
-        for (size_t i = 0; i < hex_str.size(); i += 8) {
-            integral_index.push_back(std::stoul(hex_str.substr(i, 8), nullptr, 16));
-        }
-
-        indexed_name = base_name + "["
-                       + (hex_str.find_first_not_of('0') == std::string::npos
-                              ? "0"
-                              : hex_str.substr(hex_str.find_first_not_of('0')))
-                       + "]";
-
-        idx_width = 128;
-    }
-
-    // process_key: Unsupported key type fallback
-    template 
-    typename std::enable_if::value
-                            && !std::is_same::value
-                            && !VlIsVlWide::value>::type
-    process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index,
-                const std::string& base_name, size_t& idx_width) {
-        VL_FATAL_MT(__FILE__, __LINE__, "randomize",
-                    "Unsupported: Only integral and string index of associative array is "
-                    "supported currently.");
-    }
-
-    // ---  write_var to register variables  ---
-    // Register scalar variable (non-struct, basic type)
-    template 
-    typename std::enable_if::value && !IsVlUnpacked::value,
-                            void>::type
-    write_var(T& var, int width, const char* name, int dimension,
-              std::uint32_t randmodeIdx = std::numeric_limits::max()) {
-        if (m_vars.find(name) != m_vars.end()) return;
-        // TODO: make_unique once VlRandomizer is per-instance not per-ref
-        m_vars[name]
-            = std::make_shared(name, width, &var, dimension, randmodeIdx);
-    }
-
-    // Register user-defined struct variable by recursively writing members
-    template 
-    typename std::enable_if::value, void>::type
-    write_var(T& var, int width, const char* name, int dimension,
-              std::uint32_t randmodeIdx = std::numeric_limits::max()) {
-        modifyMembers(var, var.memberIndices(), name);
-    }
-
-    // Register queue of non-struct types
-    template 
-    typename std::enable_if::value, void>::type
-    write_var(VlQueue& var, int width, const char* name, int dimension,
-              std::uint32_t randmodeIdx = std::numeric_limits::max()) {
-        if (m_vars.find(name) != m_vars.end()) return;
-        m_vars[name] = std::make_shared>>(
-            name, width, &var, dimension, randmodeIdx);
-        if (dimension > 0) {
-            m_index = 0;
-            record_arr_table(var, name, dimension, {}, {});
-        }
-    }
-
-    // Register queue of structs
-    template 
-    typename std::enable_if::value, void>::type
-    write_var(VlQueue& var, int width, const char* name, int dimension,
-              std::uint32_t randmodeIdx = std::numeric_limits::max()) {
-        if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
-    }
-    // Register unpacked array of non-struct types
-    template 
-    typename std::enable_if::value, void>::type
-    write_var(VlUnpacked& var, uint32_t width, const std::string& name,
-              uint32_t dimension,
-              std::uint32_t randmodeIdx = std::numeric_limits::max()) {
-
-        if (m_vars.find(name) != m_vars.end()) return;
-
-        m_vars[name] = std::make_shared>>(
-            name, width, &var, dimension, randmodeIdx);
-
-        if (dimension > 0) {
-            m_index = 0;
-            record_arr_table(var, name, dimension, {}, {});
-        }
-    }
-    // Register unpacked array of structs
-    template 
-    typename std::enable_if::value, void>::type
-    write_var(VlUnpacked& var, int width, const char* name, int dimension,
-              std::uint32_t randmodeIdx = std::numeric_limits::max()) {
-        if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
-    }
-
-    // Register associative array of non-struct types
-    template 
-    typename std::enable_if::value, void>::type
-    write_var(VlAssocArray& var, int width, const char* name, int dimension,
-              std::uint32_t randmodeIdx = std::numeric_limits::max()) {
-        if (m_vars.find(name) != m_vars.end()) return;
-        m_vars[name]
-            = std::make_shared>>(
-                name, width, &var, dimension, randmodeIdx);
-        if (dimension > 0) {
-            m_index = 0;
-            record_arr_table(var, name, dimension, {}, {});
-        }
-    }
-
-    // Register associative array of structs
-    template 
-    typename std::enable_if::value, void>::type
-    write_var(VlAssocArray& var, int width, const char* name, int dimension,
-              std::uint32_t randmodeIdx = std::numeric_limits::max()) {
-        if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
-    }
-
-    // ---  Record Arrays: flat and struct  ---
-
-    // Record a flat (non-class) element into the array variable table
-    template 
-    typename std::enable_if::value || VlIsVlWide::value, void>::type
-    record_arr_table(T& var, const std::string& name, int dimension, std::vector indices,
-                     std::vector idxWidths) {
-        const std::string key = generateKey(name, m_index);
-        m_arr_vars[key] = std::make_shared(name, &var, m_index, indices, idxWidths);
-        ++m_index;
-    }
-
-    // This is the "Sender" API for the generated code
-    void rand_unique(const std::string& name, uint32_t size) {
-        m_unique_arrays.push_back(name);
-        m_unique_array_sizes[name] = size;
-    }
-
-    // Recursively record all elements in an unpacked array
-    template 
-    void record_arr_table(VlUnpacked& var, const std::string& name, int dimension,
-                          std::vector indices, std::vector idxWidths) {
-        if ((dimension > 0) && (N_Depth != 0)) {
-            idxWidths.push_back(32);
-            for (size_t i = 0; i < N_Depth; ++i) {
-                const std::string indexed_name = name + "[" + std::to_string(i) + "]";
-                indices.push_back(i);
-                record_arr_table(var.operator[](i), indexed_name, dimension - 1, indices,
-                                 idxWidths);
-                indices.pop_back();
-            }
-        }
-    }
-
-    // Recursively record all elements in a queue
-    template 
-    void record_arr_table(VlQueue& var, const std::string& name, int dimension,
-                          std::vector indices, std::vector idxWidths) {
-        if ((dimension > 0) && (var.size() != 0)) {
-            idxWidths.push_back(32);
-            for (size_t i = 0; i < var.size(); ++i) {
-                const std::string indexed_name = name + "[" + std::to_string(i) + "]";
-                indices.push_back(i);
-                record_arr_table(var.atWrite(i), indexed_name, dimension - 1, indices, idxWidths);
-                indices.pop_back();
-            }
-        }
-    }
-
-    // Recursively record all elements in an associative array
-    template 
-    void record_arr_table(VlAssocArray& var, const std::string& name,
-                          int dimension, std::vector indices,
-                          std::vector idxWidths) {
-        if ((dimension > 0) && (var.size() != 0)) {
-            for (auto it = var.begin(); it != var.end(); ++it) {
-                const T_Key& key = it->first;
-                const T_Value& value = it->second;
-
-                std::string indexed_name;
-                std::vector integral_index;
-                size_t idx_width = 0;
-
-                process_key(key, indexed_name, integral_index, name, idx_width);
-
-                // Update indices and widths
-                idxWidths.push_back(idx_width);
-                indices.insert(indices.end(), integral_index.begin(), integral_index.end());
-
-                record_arr_table(var.at(key), indexed_name, dimension - 1, indices, idxWidths);
-
-                // Cleanup indices and widths
-                idxWidths.pop_back();
-                indices.resize(indices.size() - integral_index.size());
-            }
-        }
-    }
-
-    // Register a single structArray element via write_var
-    template 
-    typename std::enable_if::value, void>::type
-    record_struct_arr(T& var, const std::string& name, int dimension, std::vector indices,
-                      std::vector idxWidths) {
-        std::ostringstream oss;
-        for (size_t i = 0; i < indices.size(); ++i) {
-            oss << std::hex << std::setw(int(idxWidths[i] / 4)) << std::setfill('0')
-                << static_cast(indices[i]);
-            if (i < indices.size() - 1) oss << ".";
-        }
-        write_var(var, 1ULL,
-                  oss.str().length() > 0 ? (name + "." + oss.str()).c_str() : name.c_str(), 1ULL);
-    }
-
-    // Recursively process VlUnpacked of structs
-    template 
-    void record_struct_arr(VlUnpacked& var, const std::string& name, int dimension,
-                           std::vector indices, std::vector idxWidths) {
-        if (dimension > 0 && N_Depth != 0) {
-            constexpr size_t idx_width = 1 << VL_CLOG2_CE_Q(VL_CLOG2_CE_Q(N_Depth) + 1);
-            idxWidths.push_back(idx_width);
-            for (size_t i = 0; i < N_Depth; ++i) {
-                indices.push_back(i);
-                record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths);
-                indices.pop_back();
-            }
-        }
-    }
-
-    // Recursively process VlQueue of structs
-    template 
-    void record_struct_arr(VlQueue& var, const std::string& name, int dimension,
-                           std::vector indices, std::vector idxWidths) {
-        if ((dimension > 0) && (var.size() != 0)) {
-            idxWidths.push_back(32);
-            for (size_t i = 0; i < var.size(); ++i) {
-                indices.push_back(i);
-                record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths);
-                indices.pop_back();
-            }
-        }
-    }
-
-    // Recursively process associative arrays of structs
-    template 
-    void record_struct_arr(VlAssocArray& var, const std::string& name,
-                           int dimension, const std::vector& indices,
-                           const std::vector& idxWidths) {
-        if ((dimension > 0) && (!var.empty())) {
-            for (auto it = var.begin(); it != var.end(); ++it) {
-                const T_Key& key = it->first;
-                const T_Value& value = it->second;
-
-                std::string indexed_name;
-                std::vector integral_index;
-                size_t idx_width = 0;
-
-                process_key(key, indexed_name, integral_index, name, idx_width);
-                std::ostringstream oss;
-                for (int i = 0; i < integral_index.size(); ++i)
-                    oss << std::hex << static_cast(integral_index[i]);
-
-                std::string result = oss.str();
-                result.insert(result.begin(), int(idx_width / 4) - result.size(), '0');
-                record_struct_arr(var.at(key), name + "." + result, dimension - 1, indices,
-                                  idxWidths);
-            }
-        }
-    }
-
-    // ---  Helper functions  ---
-
-    // Helper: Register all members of a user-defined struct
-    template 
-    void modifyMembers(T& obj, std::index_sequence, const std::string& baseName) {
-        // Use the indices to access each member via std::get
-        (void)std::initializer_list{
-            (write_var(std::get(obj.getMembers(obj)), obj.memberWidth()[I],
-                       (baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I]),
-             0)...};
-    }
-
-    // Helper: Generate unique variable key from name and index
-    std::string generateKey(const std::string& name, int idx) {
-        if (!name.empty() && name[0] == '\\') {
-            const size_t space_pos = name.find(' ');
-            return (space_pos != std::string::npos ? name.substr(0, space_pos) : name)
-                   + std::to_string(idx);
-        }
-        const size_t bracket_pos = name.find('[');
-        return (bracket_pos != std::string::npos ? name.substr(0, bracket_pos) : name)
-               + std::to_string(idx);
-    }
-
-    void hard(std::string&& constraint, const char* filename = "", uint32_t linenum = 0,
-              const char* source = "");
-    void clearConstraints();
-    void clearAll();  // Clear both constraints and variables
-    void markRandc(const char* name);  // Mark variable as randc for cyclic tracking
-    void solveBefore(const char* beforeName,
-                     const char* afterName);  // Register solve-before ordering
-    void set_randmode(const VlQueue& randmode) { m_randmodep = &randmode; }
-#ifdef VL_DEBUG
-    void dump() const;
-#endif
-};
-
-//=============================================================================
-
-// Light wrapper for RNG used by std::randomize() to support scope-level randomization.
-class VlStdRandomizer final : public VlRandomizer {
-    // MEMBERS
-    VlRNG m_rng;  // Random number generator
-
-public:
-    // CONSTRUCTORS
-    VlStdRandomizer() = default;
-    ~VlStdRandomizer() = default;
-
-private:
-    // Wide type specialization (>64 bits)
-    template 
-    typename std::enable_if::value, bool>::type
-    basicStdRandomizationImpl(T& value, size_t width) {
-        VL_RANDOM_RNG_W(m_rng, width, value);
-        // Mask off garbage bits in last word
-        const int words = VL_WORDS_I(width);
-        const int bitsInLastWord = width & VL_SIZEBITS_I;
-        if (bitsInLastWord) value.at(words - 1) &= VL_MASK_I(bitsInLastWord);
-        return true;
-    }
-
-    // Scalar type specialization (<=64 bits)
-    template 
-    typename std::enable_if::value, bool>::type
-    basicStdRandomizationImpl(T& value, size_t width) {
-        if (width <= 32) {
-            value = VL_MASK_I(width) & VL_RANDOM_RNG_I(m_rng);
-        } else {
-            value = VL_MASK_Q(width) & VL_RANDOM_RNG_Q(m_rng);
-        }
-        return true;
-    }
-
-public:
-    // Scalar/wide randomization
-    template 
-    bool basicStdRandomization(T& value, size_t width) {
-        return basicStdRandomizationImpl(value, width);
-    }
-
-    // Unpacked array randomization
-    template 
-    bool basicStdRandomization(VlUnpacked& value, size_t width) {
-        for (size_t i = 0; i < N_Depth; ++i) { basicStdRandomization(value.operator[](i), width); }
-        return true;
-    }
-
-    // Queue/dynamic array randomization
-    template 
-    bool basicStdRandomization(VlQueue& value, size_t width) {
-        for (int i = 0; i < value.size(); ++i) { basicStdRandomization(value.atWrite(i), width); }
-        return true;
-    }
-
-    // Associative array randomization
-    template 
-    bool basicStdRandomization(VlAssocArray& value, size_t width) {
-        T_Key key;
-        for (int exists = value.first(key); exists; exists = value.next(key)) {
-            basicStdRandomization(value.at(key), width);
-        }
-        return true;
-    }
-    bool next() { return VlRandomizer::next(m_rng); }
-};
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.cpp
deleted file mode 100644
index 83237bb2f92..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.cpp
+++ /dev/null
@@ -1,669 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated C++ tracing in SAIF format implementation code
-///
-/// This file must be compiled and linked against all Verilated objects
-/// that use --trace-saif.
-///
-/// Use "verilator --trace-saif" to add this to the Makefile for the linker.
-///
-//=============================================================================
-
-// clang-format off
-
-#include "verilatedos.h"
-#include "verilated.h"
-#include "verilated_saif_c.h"
-
-#include 
-#include 
-#include 
-#include 
-
-#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
-# include 
-#else
-# include 
-#endif
-
-#ifndef O_LARGEFILE  // WIN32 headers omit this
-# define O_LARGEFILE 0
-#endif
-#ifndef O_NONBLOCK  // WIN32 headers omit this
-# define O_NONBLOCK 0
-#endif
-#ifndef O_CLOEXEC  // WIN32 headers omit this
-# define O_CLOEXEC 0
-#endif
-
-// clang-format on
-
-//=============================================================================
-// Specialization of the generics for this trace format
-
-#define VL_SUB_T VerilatedSaif
-#define VL_BUF_T VerilatedSaifBuffer
-#include "verilated_trace_imp.h"
-#undef VL_SUB_T
-#undef VL_BUF_T
-
-//=============================================================================
-// VerilatedSaifActivityBit
-
-class VerilatedSaifActivityBit final {
-    // MEMBERS
-    bool m_lastVal = false;  // Last emitted activity bit value
-    uint64_t m_highTime = 0;  // Total time when bit was high
-    size_t m_transitions = 0;  // Total number of bit transitions
-
-public:
-    // METHODS
-    VL_ATTR_ALWINLINE
-    void aggregateVal(uint64_t dt, bool newVal) {
-        m_transitions += newVal != m_lastVal ? 1 : 0;
-        m_highTime += m_lastVal ? dt : 0;
-        m_lastVal = newVal;
-    }
-
-    // ACCESSORS
-    VL_ATTR_ALWINLINE bool bitValue() const { return m_lastVal; }
-    VL_ATTR_ALWINLINE uint64_t highTime() const { return m_highTime; }
-    VL_ATTR_ALWINLINE uint64_t toggleCount() const { return m_transitions; }
-};
-
-//=============================================================================
-// VerilatedSaifActivityVar
-
-class VerilatedSaifActivityVar final {
-    // MEMBERS
-    uint64_t m_lastTime = 0;  // Last time when variable value was updated
-    VerilatedSaifActivityBit* m_bits;  // Pointer to variable bits objects
-    uint32_t m_width;  // Width of variable (in bits)
-
-public:
-    // CONSTRUCTORS
-    VerilatedSaifActivityVar(uint32_t width, VerilatedSaifActivityBit* bits)
-        : m_bits{bits}
-        , m_width{width} {}
-
-    VerilatedSaifActivityVar(VerilatedSaifActivityVar&&) = default;
-    VerilatedSaifActivityVar& operator=(VerilatedSaifActivityVar&&) = default;
-
-    // METHODS
-    VL_ATTR_ALWINLINE void emitBit(uint64_t time, CData newval);
-
-    template 
-    VL_ATTR_ALWINLINE void emitData(uint64_t time, DataType newval, uint32_t bits) {
-        static_assert(std::is_integral::value,
-                      "The emitted value must be of integral type");
-
-        const uint64_t dt = time - m_lastTime;
-        for (size_t i = 0; i < std::min(m_width, bits); ++i) {
-            m_bits[i].aggregateVal(dt, (newval >> i) & 1);
-        }
-        updateLastTime(time);
-    }
-
-    VL_ATTR_ALWINLINE void emitWData(uint64_t time, const WData* newvalp, uint32_t bits);
-    VL_ATTR_ALWINLINE void updateLastTime(uint64_t val) { m_lastTime = val; }
-
-    // ACCESSORS
-    VL_ATTR_ALWINLINE uint32_t width() const { return m_width; }
-    VL_ATTR_ALWINLINE VerilatedSaifActivityBit& bit(std::size_t index);
-    VL_ATTR_ALWINLINE uint64_t lastUpdateTime() const { return m_lastTime; }
-
-private:
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedSaifActivityVar);
-};
-
-//=============================================================================
-// VerilatedSaifActivityScope
-
-class VerilatedSaifActivityScope final {
-    // MEMBERS
-    // Absolute path to the scope
-    std::string m_scopePath;
-    // Name of the activity scope
-    std::string m_scopeName;
-    // Array indices of child scopes
-    std::vector> m_childScopes{};
-    // Children signals codes mapped to their names in the current scope
-    std::vector> m_childActivities{};
-    // Parent scope pointer
-    VerilatedSaifActivityScope* m_parentScope = nullptr;
-
-public:
-    // CONSTRUCTORS
-    VerilatedSaifActivityScope(std::string scopePath, std::string name,
-                               VerilatedSaifActivityScope* parentScope = nullptr)
-        : m_scopePath{std::move(scopePath)}
-        , m_scopeName{std::move(name)}
-        , m_parentScope{parentScope} {}
-
-    VerilatedSaifActivityScope(VerilatedSaifActivityScope&&) = default;
-    VerilatedSaifActivityScope& operator=(VerilatedSaifActivityScope&&) = default;
-
-    // METHODS
-    VL_ATTR_ALWINLINE void addChildScope(std::unique_ptr childScope) {
-        m_childScopes.emplace_back(std::move(childScope));
-    }
-    VL_ATTR_ALWINLINE void addActivityVar(uint32_t code, std::string name) {
-        m_childActivities.emplace_back(code, std::move(name));
-    }
-    VL_ATTR_ALWINLINE bool hasParent() const { return m_parentScope; }
-
-    // ACCESSORS
-    VL_ATTR_ALWINLINE const std::string& path() const { return m_scopePath; }
-    VL_ATTR_ALWINLINE const std::string& name() const { return m_scopeName; }
-    VL_ATTR_ALWINLINE const std::vector>&
-    childScopes() const {
-        return m_childScopes;
-    }
-    VL_ATTR_ALWINLINE
-    const std::vector>& childActivities() const {
-        return m_childActivities;
-    }
-    VL_ATTR_ALWINLINE VerilatedSaifActivityScope* parentScope() const { return m_parentScope; }
-
-private:
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedSaifActivityScope);
-};
-
-//=============================================================================
-// VerilatedSaifActivityAccumulator
-
-class VerilatedSaifActivityAccumulator final {
-    // Give access to the private activities
-    friend class VerilatedSaifBuffer;
-    friend class VerilatedSaif;
-
-    // MEMBERS
-    // Map of scopes paths to codes of activities inside
-    std::unordered_map>>
-        m_scopeToActivities;
-    // Map of variables codes mapped to their activity objects
-    std::unordered_map m_activity;
-    // Memory pool for signals bits objects
-    std::vector> m_activityArena;
-
-public:
-    // METHODS
-    void declare(uint32_t code, const std::string& absoluteScopePath, std::string variableName,
-                 int bits, bool array, int arraynum);
-
-    // CONSTRUCTORS
-    VerilatedSaifActivityAccumulator() = default;
-
-    VerilatedSaifActivityAccumulator(VerilatedSaifActivityAccumulator&&) = default;
-    VerilatedSaifActivityAccumulator& operator=(VerilatedSaifActivityAccumulator&&) = default;
-
-private:
-    VL_UNCOPYABLE(VerilatedSaifActivityAccumulator);
-};
-
-//=============================================================================
-//=============================================================================
-//=============================================================================
-// VerilatedSaifActivityVar implementation
-
-VL_ATTR_ALWINLINE
-void VerilatedSaifActivityVar::emitBit(const uint64_t time, const CData newval) {
-    assert(m_lastTime <= time);
-    m_bits[0].aggregateVal(time - m_lastTime, newval);
-    updateLastTime(time);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedSaifActivityVar::emitWData(const uint64_t time, const WData* newvalp,
-                                         const uint32_t bits) {
-    assert(m_lastTime <= time);
-    const uint64_t dt = time - m_lastTime;
-    for (std::size_t i = 0; i < std::min(m_width, bits); ++i) {
-        const size_t wordIndex = i / VL_EDATASIZE;
-        m_bits[i].aggregateVal(dt, (newvalp[wordIndex] >> VL_BITBIT_E(i)) & 1);
-    }
-
-    updateLastTime(time);
-}
-
-VerilatedSaifActivityBit& VerilatedSaifActivityVar::bit(const std::size_t index) {
-    assert(index < m_width);
-    return m_bits[index];
-}
-
-//=============================================================================
-//=============================================================================
-//=============================================================================
-// VerilatedSaifActivityAccumulator implementation
-
-void VerilatedSaifActivityAccumulator::declare(uint32_t code, const std::string& absoluteScopePath,
-                                               std::string variableName, int bits, bool array,
-                                               int arraynum) {
-    const size_t block_size = 1024;
-    if (m_activityArena.empty()
-        || m_activityArena.back().size() + bits > m_activityArena.back().capacity()) {
-        m_activityArena.emplace_back();
-        m_activityArena.back().reserve(block_size);
-    }
-    const size_t bitsIdx = m_activityArena.back().size();
-    m_activityArena.back().resize(m_activityArena.back().size() + bits);
-
-    if (array) {
-        variableName += '[';
-        variableName += std::to_string(arraynum);
-        variableName += ']';
-    }
-    m_scopeToActivities[absoluteScopePath].emplace_back(code, variableName);
-    m_activity.emplace(code, VerilatedSaifActivityVar{static_cast(bits),
-                                                      m_activityArena.back().data() + bitsIdx});
-}
-
-//=============================================================================
-//=============================================================================
-//=============================================================================
-// VerilatedSaif implementation
-
-VerilatedSaif::VerilatedSaif(void* filep) {
-    m_activityAccumulators.emplace_back(std::make_unique());
-}
-
-void VerilatedSaif::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    if (isOpen()) return;
-
-    m_filename = filename;  // "" is ok, as someone may overload open
-    m_filep = ::open(m_filename.c_str(),
-                     O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE | O_NONBLOCK | O_CLOEXEC, 0666);
-    m_isOpen = true;
-
-    initializeSaifFileContents();
-
-    Super::traceInit();
-}
-
-void VerilatedSaif::initializeSaifFileContents() {
-    printStr("// Generated by verilated_saif\n");
-    printStr("(SAIFILE\n");
-    printStr("(SAIFVERSION \"2.0\")\n");
-    printStr("(DIRECTION \"backward\")\n");
-    printStr("(PROGRAM_NAME \"Verilator\")\n");
-    printStr("(DIVIDER / )\n");
-    printStr("(TIMESCALE ");
-    printStr(timeResStr());
-    printStr(")\n");
-}
-
-void VerilatedSaif::emitTimeChange(uint64_t timeui) { m_time = timeui; }
-
-VerilatedSaif::~VerilatedSaif() { close(); }
-
-void VerilatedSaif::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
-    // This function is on the flush() call path
-    const VerilatedLockGuard lock{m_mutex};
-    if (!isOpen()) return;
-
-    finalizeSaifFileContents();
-    clearCurrentlyCollectedData();
-
-    writeBuffered(true);
-    ::close(m_filep);
-    m_isOpen = false;
-
-    Super::closeBase();
-}
-
-void VerilatedSaif::finalizeSaifFileContents() {
-    printStr("(DURATION ");
-    printStr(std::to_string(currentTime()));
-    printStr(")\n");
-
-    incrementIndent();
-    for (const auto& topScope : m_scopes) recursivelyPrintScopes(*topScope);
-    decrementIndent();
-
-    printStr(")\n");  // SAIFILE
-}
-
-void VerilatedSaif::recursivelyPrintScopes(const VerilatedSaifActivityScope& scope) {
-    openInstanceScope(scope.name());
-    printScopeActivities(scope);
-    for (const auto& childScope : scope.childScopes()) recursivelyPrintScopes(*childScope);
-    closeInstanceScope();
-}
-
-void VerilatedSaif::openInstanceScope(const std::string& instanceName) {
-    printIndent();
-    printStr("(INSTANCE ");
-    printStr(instanceName);
-    printStr("\n");
-    incrementIndent();
-}
-
-void VerilatedSaif::closeInstanceScope() {
-    decrementIndent();
-    printIndent();
-    printStr(")\n");  // INSTANCE
-}
-
-void VerilatedSaif::printScopeActivities(const VerilatedSaifActivityScope& scope) {
-    bool anyNetWritten = false;
-
-    for (auto& accumulator : m_activityAccumulators) {
-        anyNetWritten |= printScopeActivitiesFromAccumulatorIfPresent(scope.path(), *accumulator,
-                                                                      anyNetWritten);
-    }
-
-    if (anyNetWritten) closeNetScope();
-}
-
-bool VerilatedSaif::printScopeActivitiesFromAccumulatorIfPresent(
-    const std::string& absoluteScopePath, VerilatedSaifActivityAccumulator& accumulator,
-    bool anyNetWritten) {
-    if (accumulator.m_scopeToActivities.count(absoluteScopePath) == 0) return false;
-
-    for (const auto& childSignal : accumulator.m_scopeToActivities.at(absoluteScopePath)) {
-        VerilatedSaifActivityVar& activityVariable = accumulator.m_activity.at(childSignal.first);
-        anyNetWritten
-            = printActivityStats(activityVariable, childSignal.second.c_str(), anyNetWritten);
-    }
-
-    return anyNetWritten;
-}
-
-void VerilatedSaif::openNetScope() {
-    printIndent();
-    printStr("(NET\n");
-    incrementIndent();
-}
-
-void VerilatedSaif::closeNetScope() {
-    decrementIndent();
-    printIndent();
-    printStr(")\n");  // NET
-}
-
-bool VerilatedSaif::printActivityStats(VerilatedSaifActivityVar& activity,
-                                       const std::string& activityName, bool anyNetWritten) {
-    for (size_t i = 0; i < activity.width(); ++i) {
-        VerilatedSaifActivityBit& bit = activity.bit(i);
-
-        bit.aggregateVal(currentTime() - activity.lastUpdateTime(), bit.bitValue());
-
-        if (!anyNetWritten) {
-            openNetScope();
-            anyNetWritten = true;
-        }
-
-        printIndent();
-        printStr("(");
-        printStr(activityName);
-        if (activity.width() > 1) {
-            printStr("\\[");
-            printStr(std::to_string(i));
-            printStr("\\]");
-        }
-
-        // We only have two-value logic so TZ, TX and TB will always be 0
-        printStr(" (T0 ");
-        printStr(std::to_string(currentTime() - bit.highTime()));
-        printStr(") (T1 ");
-        printStr(std::to_string(bit.highTime()));
-        printStr(") (TZ 0) (TX 0) (TB 0) (TC ");
-        printStr(std::to_string(bit.toggleCount()));
-        printStr("))\n");
-    }
-
-    activity.updateLastTime(currentTime());
-
-    return anyNetWritten;
-}
-
-void VerilatedSaif::clearCurrentlyCollectedData() {
-    m_currentScope = nullptr;
-    m_scopes.clear();
-    m_activityAccumulators.clear();
-}
-
-void VerilatedSaif::printStr(const char* str) {
-    m_buffer.append(str);
-    writeBuffered(false);
-}
-
-void VerilatedSaif::printStr(const std::string& str) {
-    m_buffer.append(str);
-    writeBuffered(false);
-}
-
-void VerilatedSaif::writeBuffered(bool force) {
-    if (VL_UNLIKELY(m_buffer.size() >= WRITE_BUFFER_SIZE || force)) {
-        if (VL_UNLIKELY(!m_buffer.empty())) {
-            ::write(m_filep, m_buffer.data(), m_buffer.size());
-            m_buffer = "";
-            m_buffer.reserve(WRITE_BUFFER_SIZE * 2);
-        }
-    }
-}
-
-//=============================================================================
-// Definitions
-
-void VerilatedSaif::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    Super::flushBase();
-}
-
-void VerilatedSaif::incrementIndent() { m_indent += 1; }
-
-void VerilatedSaif::decrementIndent() { m_indent -= 1; }
-
-void VerilatedSaif::printIndent() {
-    printStr(std::string(m_indent, ' '));  // Must use () constructor
-}
-
-void VerilatedSaif::pushPrefix(const char* namep, VerilatedTracePrefixType type) {
-    assert(!m_prefixStack.empty());  // Constructor makes an empty entry
-    const std::string name{namep};
-    // An empty name means this is the root of a model created with
-    // name()=="".  The tools get upset if we try to pass this as empty, so
-    // we put the signals under a new $rootio scope, but the signals
-    // further down will be peers, not children (as usual for name()!="").
-    const std::string prevPrefix = m_prefixStack.back().first;
-    if (name == "$rootio" && !prevPrefix.empty()) {
-        // Upper has name, we can suppress inserting $rootio, but still push so popPrefix works
-        m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
-        return;
-    } else if (name.empty()) {
-        m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
-        return;
-    }
-
-    if (type != VerilatedTracePrefixType::ARRAY_UNPACKED
-        && type != VerilatedTracePrefixType::ARRAY_PACKED) {
-
-        std::string scopePath = prevPrefix + name;
-        std::string scopeName = lastWord(scopePath);
-
-        auto newScope = std::make_unique(
-            std::move(scopePath), std::move(scopeName), m_currentScope);
-        VerilatedSaifActivityScope* newScopePtr = newScope.get();
-
-        if (m_currentScope) {
-            m_currentScope->addChildScope(std::move(newScope));
-        } else {
-            m_scopes.emplace_back(std::move(newScope));
-        }
-
-        m_currentScope = newScopePtr;
-    }
-
-    const std::string newPrefix = prevPrefix + name;
-    bool properScope = (type != VerilatedTracePrefixType::ARRAY_UNPACKED
-                        && type != VerilatedTracePrefixType::ARRAY_PACKED
-                        && type != VerilatedTracePrefixType::ROOTIO_WRAPPER);
-    m_prefixStack.emplace_back(newPrefix + (properScope ? " " : ""), type);
-}
-
-void VerilatedSaif::popPrefix() {
-    if (m_prefixStack.back().second != VerilatedTracePrefixType::ARRAY_UNPACKED
-        && m_prefixStack.back().second != VerilatedTracePrefixType::ARRAY_PACKED
-        && m_prefixStack.back().second != VerilatedTracePrefixType::ROOTIO_WRAPPER
-        && m_currentScope) {
-        m_currentScope = m_currentScope->parentScope();
-    }
-    m_prefixStack.pop_back();
-    assert(!m_prefixStack.empty());  // Always one left, the constructor's initial one
-}
-
-void VerilatedSaif::declare(const uint32_t code, uint32_t fidx, const char* name,
-                            const char* wirep, const bool array, const int arraynum,
-                            const bool bussed, const int msb, const int lsb) {
-    assert(m_activityAccumulators.size() > fidx);
-    VerilatedSaifActivityAccumulator& accumulator = *m_activityAccumulators.at(fidx);
-
-    const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
-
-    const std::string hierarchicalName = m_prefixStack.back().first + name;
-
-    if (!Super::declCode(code, hierarchicalName, bits)) return;
-
-    std::string variableName = lastWord(hierarchicalName);
-    m_currentScope->addActivityVar(code, variableName);
-
-    accumulator.declare(code, m_currentScope->path(), std::move(variableName), bits, array,
-                        arraynum);
-}
-
-void VerilatedSaif::declEvent(const uint32_t code, const uint32_t fidx, const char* name,
-                              const int dtypenum, const VerilatedTraceSigDirection,
-                              const VerilatedTraceSigKind, const VerilatedTraceSigType,
-                              const bool array, const int arraynum) {
-    declare(code, fidx, name, "event", array, arraynum, false, 0, 0);
-}
-
-void VerilatedSaif::declBit(const uint32_t code, const uint32_t fidx, const char* name,
-                            const int dtypenum, const VerilatedTraceSigDirection,
-                            const VerilatedTraceSigKind, const VerilatedTraceSigType,
-                            const bool array, const int arraynum) {
-    declare(code, fidx, name, "wire", array, arraynum, false, 0, 0);
-}
-void VerilatedSaif::declBus(const uint32_t code, const uint32_t fidx, const char* name,
-                            const int dtypenum, const VerilatedTraceSigDirection,
-                            const VerilatedTraceSigKind, const VerilatedTraceSigType,
-                            const bool array, const int arraynum, const int msb, const int lsb) {
-    declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb);
-}
-void VerilatedSaif::declQuad(const uint32_t code, const uint32_t fidx, const char* name,
-                             const int dtypenum, const VerilatedTraceSigDirection,
-                             const VerilatedTraceSigKind, const VerilatedTraceSigType,
-                             const bool array, const int arraynum, const int msb, const int lsb) {
-    declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb);
-}
-void VerilatedSaif::declArray(const uint32_t code, const uint32_t fidx, const char* name,
-                              const int dtypenum, const VerilatedTraceSigDirection,
-                              const VerilatedTraceSigKind, const VerilatedTraceSigType,
-                              const bool array, const int arraynum, const int msb, const int lsb) {
-    declare(code, fidx, name, "wire", array, arraynum, true, msb, lsb);
-}
-void VerilatedSaif::declDouble(const uint32_t code, const uint32_t fidx, const char* name,
-                               const int dtypenum, const VerilatedTraceSigDirection,
-                               const VerilatedTraceSigKind, const VerilatedTraceSigType,
-                               const bool array, const int arraynum) {
-    declare(code, fidx, name, "real", array, arraynum, false, 63, 0);
-}
-
-//=============================================================================
-// Get/commit trace buffer
-
-VerilatedSaif::Buffer* VerilatedSaif::getTraceBuffer(uint32_t fidx) { return new Buffer{*this}; }
-
-void VerilatedSaif::commitTraceBuffer(VerilatedSaif::Buffer* bufp) { delete bufp; }
-
-//=============================================================================
-//=============================================================================
-//=============================================================================
-// VerilatedSaifBuffer implementation
-
-//=============================================================================
-// emit* trace routines
-
-// Note: emit* are only ever called from one place (full* in
-// verilated_trace_imp.h, which is included in this file at the top),
-// so always inline them.
-
-VL_ATTR_ALWINLINE
-void VerilatedSaifBuffer::emitEvent(const uint32_t code) {
-    // NOP
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedSaifBuffer::emitBit(const uint32_t code, const CData newval) {
-    assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code)
-           && "Activity must be declared earlier");
-    VerilatedSaifActivityVar& activity
-        = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code);
-    activity.emitBit(m_owner.currentTime(), newval);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedSaifBuffer::emitCData(const uint32_t code, const CData newval, const int bits) {
-    assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code)
-           && "Activity must be declared earlier");
-    VerilatedSaifActivityVar& activity
-        = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code);
-    activity.emitData(m_owner.currentTime(), newval, bits);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedSaifBuffer::emitSData(const uint32_t code, const SData newval, const int bits) {
-    assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code)
-           && "Activity must be declared earlier");
-    VerilatedSaifActivityVar& activity
-        = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code);
-    activity.emitData(m_owner.currentTime(), newval, bits);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedSaifBuffer::emitIData(const uint32_t code, const IData newval, const int bits) {
-    assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code)
-           && "Activity must be declared earlier");
-    VerilatedSaifActivityVar& activity
-        = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code);
-    activity.emitData(m_owner.currentTime(), newval, bits);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedSaifBuffer::emitQData(const uint32_t code, const QData newval, const int bits) {
-    assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code)
-           && "Activity must be declared earlier");
-    VerilatedSaifActivityVar& activity
-        = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code);
-    activity.emitData(m_owner.currentTime(), newval, bits);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedSaifBuffer::emitWData(const uint32_t code, const WData* newvalp, const int bits) {
-    assert(m_owner.m_activityAccumulators.at(m_fidx)->m_activity.count(code)
-           && "Activity must be declared earlier");
-    VerilatedSaifActivityVar& activity
-        = m_owner.m_activityAccumulators.at(m_fidx)->m_activity.at(code);
-    activity.emitWData(m_owner.currentTime(), newvalp, bits);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedSaifBuffer::emitDouble(const uint32_t code, const double newval) {
-    // NOP
-}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.h
deleted file mode 100644
index 28f35166c93..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_c.h
+++ /dev/null
@@ -1,292 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated tracing in SAIF format header
-///
-/// User wrapper code should use this header when creating SAIF traces.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_SAIF_C_H_
-#define VERILATOR_VERILATED_SAIF_C_H_
-
-#include "verilated.h"
-#include "verilated_trace.h"
-
-#include 
-#include 
-#include 
-
-class VerilatedSaifBuffer;
-class VerilatedSaifActivityAccumulator;
-class VerilatedSaifActivityScope;
-class VerilatedSaifActivityVar;
-class VerilatedSaifActivityBit;
-
-//=============================================================================
-// VerilatedSaif
-// Base class to create a Verilator SAIF dump
-// This is an internally used class - see VerilatedSaifC for what to call from applications
-
-class VerilatedSaif VL_NOT_FINAL : public VerilatedTrace {
-public:
-    using Super = VerilatedTrace;
-
-private:
-    friend VerilatedSaifBuffer;  // Give the buffer access to the private bits
-
-    //=========================================================================
-    // SAIF-specific internals
-
-    int m_filep = 0;  // File we're writing to
-    bool m_isOpen = false;  // True indicates open file
-    std::string m_filename;  // Filename we're writing to (if open)
-    std::string m_buffer;  // Write data buffer
-
-    int m_indent = 0;  // Indentation size in spaces
-    static constexpr size_t WRITE_BUFFER_SIZE = 256 * 1024;  // Bytes between write calls
-
-    // Currently active scope
-    VerilatedSaifActivityScope* m_currentScope = nullptr;
-    // Array of declared scopes
-    std::vector> m_scopes{};
-    // Activity accumulators used to store variables statistics over simulation time
-    std::vector> m_activityAccumulators{};
-    // Total time of the currently traced simulation
-    uint64_t m_time = 0;
-
-    // Stack of declared scopes combined names
-    std::vector> m_prefixStack{
-        {"", VerilatedTracePrefixType::SCOPE_MODULE}};
-
-    // METHODS
-    VL_ATTR_ALWINLINE uint64_t currentTime() const { return m_time; }
-
-    void initializeSaifFileContents();
-    void finalizeSaifFileContents();
-    void recursivelyPrintScopes(const VerilatedSaifActivityScope& scope);
-    void openInstanceScope(const std::string& instanceName);
-    void closeInstanceScope();
-    void printScopeActivities(const VerilatedSaifActivityScope& scope);
-    bool
-    printScopeActivitiesFromAccumulatorIfPresent(const std::string& absoluteScopePath,
-                                                 VerilatedSaifActivityAccumulator& accumulator,
-                                                 bool anyNetWritten);
-    void openNetScope();
-    void closeNetScope();
-    bool printActivityStats(VerilatedSaifActivityVar& activity, const std::string& activityName,
-                            bool anyNetWritten);
-
-    void incrementIndent();
-    void decrementIndent();
-    void printIndent();
-
-    void printStr(const char* str);
-    void printStr(const std::string& str);
-    void writeBuffered(bool force);
-
-    void clearCurrentlyCollectedData();
-
-    void declare(uint32_t code, uint32_t fidx, const char* name, const char* wirep, bool array,
-                 int arraynum, bool bussed, int msb, int lsb);
-
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedSaif);
-
-protected:
-    //=========================================================================
-    // Implementation of VerilatedTrace interface
-
-    // Called when the trace moves forward to a new time point
-    void emitTimeChange(uint64_t timeui) override;
-
-    // Hooks called from VerilatedTrace
-    bool preFullDump() override { return isOpen(); }
-    bool preChangeDump() override { return isOpen(); }
-
-    // Trace buffer management
-    Buffer* getTraceBuffer(uint32_t fidx) override;
-    void commitTraceBuffer(Buffer*) override;
-
-    // Configure sub-class
-    void configure(const VerilatedTraceConfig&) override {}
-
-public:
-    //=========================================================================
-    // External interface to client code
-
-    // CONSTRUCTOR
-    explicit VerilatedSaif(void* filep = nullptr);
-    ~VerilatedSaif();
-
-    // METHODS - All must be thread safe
-    // Open the file; call isOpen() to see if errors
-    void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
-    // Close the file
-    void close() VL_MT_SAFE_EXCLUDES(m_mutex);
-    // Flush any remaining data to this file
-    void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
-    // Return if file is open
-    bool isOpen() const VL_MT_SAFE { return m_isOpen; }
-
-    //=========================================================================
-    // Internal interface to Verilator generated code
-
-    void pushPrefix(const char*, VerilatedTracePrefixType);
-    void popPrefix();
-
-    void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                   VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                   bool array, int arraynum);
-    void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                 VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                 bool array, int arraynum);
-    void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                 VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                 bool array, int arraynum, int msb, int lsb);
-    void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                  VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                  bool array, int arraynum, int msb, int lsb);
-    void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                   VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                   bool array, int arraynum, int msb, int lsb);
-    void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                    VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                    bool array, int arraynum);
-};
-
-#ifndef DOXYGEN
-// Declare specialization here as it's used in VerilatedSaifC just below
-template <>
-void VerilatedSaif::Super::dump(uint64_t time);
-template <>
-void VerilatedSaif::Super::set_time_unit(const char* unitp);
-template <>
-void VerilatedSaif::Super::set_time_unit(const std::string& unit);
-template <>
-void VerilatedSaif::Super::set_time_resolution(const char* unitp);
-template <>
-void VerilatedSaif::Super::set_time_resolution(const std::string& unit);
-template <>
-void VerilatedSaif::Super::dumpvars(int level, const std::string& hier);
-#endif  // DOXYGEN
-
-//=============================================================================
-// VerilatedSaifBuffer
-
-class VerilatedSaifBuffer VL_NOT_FINAL {
-    // Give the trace file and sub-classes access to the private bits
-    friend VerilatedSaif;
-    friend VerilatedSaif::Super;
-    friend VerilatedSaif::Buffer;
-    friend VerilatedSaif::OffloadBuffer;
-
-    VerilatedSaif& m_owner;  // Trace file owning this buffer. Required by subclasses.
-    uint32_t m_fidx;  // Index of target activity accumulator
-
-    // CONSTRUCTORS
-    explicit VerilatedSaifBuffer(VerilatedSaif& owner)
-        : m_owner{owner}
-        , m_fidx{0} {}
-    explicit VerilatedSaifBuffer(VerilatedSaif& owner, uint32_t fidx)
-        : m_owner{owner}
-        , m_fidx{fidx} {}
-    virtual ~VerilatedSaifBuffer() = default;
-
-    //=========================================================================
-    // Implementation of VerilatedTraceBuffer interface
-    // Implementations of duck-typed methods for VerilatedTraceBuffer. These are
-    // called from only one place (the full* methods), so always inline them.
-    VL_ATTR_ALWINLINE void emitEvent(uint32_t code);
-    VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
-    VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
-    VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
-    VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
-    VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
-    VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
-    VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
-};
-
-//=============================================================================
-// VerilatedSaifC
-// Class representing a SAIF dump file in C standalone (no SystemC)
-// simulations. Also derived for use in SystemC simulations.
-
-class VerilatedSaifC VL_NOT_FINAL : public VerilatedTraceBaseC {
-    VerilatedSaif m_sptrace;  // Trace file being created
-
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedSaifC);
-
-public:
-    // Construct the dump. Optional argument is ignored
-    explicit VerilatedSaifC(void* filep = nullptr)
-        : m_sptrace{filep} {}
-    // Destruct, flush, and close the dump
-    virtual ~VerilatedSaifC() { close(); }
-
-    // METHODS - User called
-
-    // Return if file is open
-    bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); }
-    // Open a new SAIF file
-    // This includes a complete header dump each time it is called,
-    // just as if this object was deleted and reconstructed.
-    virtual void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); }
-
-    void rolloverSize(size_t size) VL_MT_SAFE {}  // NOP
-
-    // Close dump
-    void close() VL_MT_SAFE {
-        m_sptrace.close();
-        modelConnected(false);
-    }
-    // Flush dump
-    void flush() VL_MT_SAFE { m_sptrace.flush(); }
-    // Write one cycle of dump data
-    // Call with the current context's time just after eval'ed,
-    // e.g. ->dump(contextp->time())
-    void dump(uint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
-    // Write one cycle of dump data - backward compatible and to reduce
-    // conversion warnings.  It's better to use a uint64_t time instead.
-    void dump(double timestamp) { dump(static_cast(timestamp)); }
-    void dump(uint32_t timestamp) { dump(static_cast(timestamp)); }
-    void dump(int timestamp) { dump(static_cast(timestamp)); }
-
-    // METHODS - Internal/backward compatible
-    // \protectedsection
-
-    // Set time units (s/ms, defaults to ns)
-    // Users should not need to call this, as for Verilated models, these
-    // propagate from the Verilated default timeunit
-    void set_time_unit(const char* unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); }
-    void set_time_unit(const std::string& unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); }
-    // Set time resolution (s/ms, defaults to ns)
-    // Users should not need to call this, as for Verilated models, these
-    // propagate from the Verilated default timeprecision
-    void set_time_resolution(const char* unit) VL_MT_SAFE { m_sptrace.set_time_resolution(unit); }
-    void set_time_resolution(const std::string& unit) VL_MT_SAFE {
-        m_sptrace.set_time_resolution(unit);
-    }
-    // Set variables to dump, using $dumpvars format
-    // If level = 0, dump everything and hier is then ignored
-    void dumpvars(int level, const std::string& hier) VL_MT_SAFE {
-        m_sptrace.dumpvars(level, hier);
-    }
-
-    // Internal class access
-    VerilatedSaif* spTrace() { return &m_sptrace; }
-};
-
-#endif  // guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_sc.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_sc.h
deleted file mode 100644
index e0c5a50fad6..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_saif_sc.h
+++ /dev/null
@@ -1,56 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated tracing in SAIF format for SystemC header
-///
-/// User wrapper code should use this header when creating SAIF SystemC traces.
-///
-/// This class is not threadsafe, as the SystemC kernel is not threadsafe.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_SAIF_SC_H_
-#define VERILATOR_VERILATED_SAIF_SC_H_
-
-#include "verilatedos.h"
-
-#include "verilated_saif_c.h"
-#include "verilated_sc_trace.h"
-
-//=============================================================================
-// VerilatedSaifSc
-/// Trace file used to create SAIF dump for SystemC version of Verilated models. It's very similar
-/// to its C version (see the class VerilatedSaifC)
-
-class VerilatedSaifSc final : VerilatedScTraceBase, public VerilatedSaifC {
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedSaifSc);
-
-public:
-    VerilatedSaifSc() {
-        spTrace()->set_time_unit(VerilatedScTraceBase::getScTimeUnit());
-        spTrace()->set_time_resolution(VerilatedScTraceBase::getScTimeResolution());
-    }
-
-    // METHODS
-    // Override VerilatedSaifC. Must be called after starting simulation.
-    void open(const char* filename) override VL_MT_SAFE {
-        VerilatedScTraceBase::checkScElaborationDone();
-        VerilatedSaifC::open(filename);
-    }
-
-    // METHODS - for SC kernel
-    // Called from SystemC kernel
-    void cycle() override { VerilatedSaifC::dump(sc_core::sc_time_stamp().to_double()); }
-};
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.cpp
deleted file mode 100644
index 0569ba0aa3c..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.cpp
+++ /dev/null
@@ -1,270 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated save/restore implementation code
-///
-/// This file must be compiled and linked against all Verilated objects
-/// that use --savable.
-///
-/// Use "verilator --savable" to add this to the Makefile for the linker.
-///
-//=============================================================================
-
-#define VERILATOR_VERILATED_SAVE_CPP_
-
-#include "verilatedos.h"
-
-#include "verilated_save.h"
-
-#include "verilated.h"
-#include "verilated_imp.h"
-
-#include 
-#include 
-
-// clang-format off
-#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
-# include 
-#else
-# include 
-#endif
-
-#ifndef O_LARGEFILE  // WIN32 headers omit this
-# define O_LARGEFILE 0
-#endif
-#ifndef O_NONBLOCK  // WIN32 headers omit this
-# define O_NONBLOCK 0
-#endif
-#ifndef O_CLOEXEC  // WIN32 headers omit this
-# define O_CLOEXEC 0
-#endif
-// clang-format on
-
-// CONSTANTS
-// Value of first bytes of each file (must be multiple of 8 bytes)
-static const char* const VLTSAVE_HEADER_STR = "verilatorsave02\n";
-// Value of last bytes of each file (must be multiple of 8 bytes)
-static const char* const VLTSAVE_TRAILER_STR = "vltsaved";
-
-//=============================================================================
-//=============================================================================
-//=============================================================================
-// Serialization
-
-bool VerilatedDeserialize::readDiffers(const void* __restrict datap,
-                                       size_t size) VL_MT_UNSAFE_ONE {
-    bufferCheck();
-    const uint8_t* __restrict dp = static_cast(datap);
-    uint8_t miss = 0;
-    while (size--) miss |= (*dp++ ^ *m_cp++);
-    return (miss != 0);
-}
-
-VerilatedDeserialize& VerilatedDeserialize::readAssert(const void* __restrict datap,
-                                                       size_t size) VL_MT_UNSAFE_ONE {
-    if (VL_UNLIKELY(readDiffers(datap, size))) {
-        const std::string fn = filename();
-        const std::string msg
-            = "Can't deserialize save-restore file as was made from different model: "
-              + filename();
-        VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
-        // Die before we close() as close would infinite loop
-    }
-    return *this;  // For function chaining
-}
-
-void VerilatedSerialize::header() VL_MT_UNSAFE_ONE {
-    VerilatedSerialize& os = *this;  // So can cut and paste standard << code below
-    assert((std::strlen(VLTSAVE_HEADER_STR) & 7) == 0);  // Keep aligned
-    os.write(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR));
-}
-
-void VerilatedDeserialize::header() VL_MT_UNSAFE_ONE {
-    VerilatedDeserialize& os = *this;  // So can cut and paste standard >> code below
-    if (VL_UNLIKELY(os.readDiffers(VLTSAVE_HEADER_STR, std::strlen(VLTSAVE_HEADER_STR)))) {
-        const std::string fn = filename();
-        const std::string msg
-            = "Can't deserialize; file has wrong header signature, or file not found: "s
-              + filename();
-        VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
-        // Die before we close() as close would infinite loop
-    }
-}
-
-void VerilatedSerialize::trailer() VL_MT_UNSAFE_ONE {
-    VerilatedSerialize& os = *this;  // So can cut and paste standard << code below
-    assert((std::strlen(VLTSAVE_TRAILER_STR) & 7) == 0);  // Keep aligned
-    os.write(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR));
-}
-
-void VerilatedDeserialize::trailer() VL_MT_UNSAFE_ONE {
-    VerilatedDeserialize& os = *this;  // So can cut and paste standard >> code below
-    if (VL_UNLIKELY(os.readDiffers(VLTSAVE_TRAILER_STR, std::strlen(VLTSAVE_TRAILER_STR)))) {
-        const std::string fn = filename();
-        const std::string msg
-            = "Can't deserialize; file has wrong end-of-file signature: "s + filename();
-        VL_FATAL_MT(fn.c_str(), 0, "", msg.c_str());
-        // Die before we close() as close would infinite loop
-    }
-}
-
-//=============================================================================
-//=============================================================================
-//=============================================================================
-// Opening/Closing
-
-void VerilatedSave::open(const char* filenamep) VL_MT_UNSAFE_ONE {
-    m_assertOne.check();
-    if (isOpen()) return;
-    VL_DEBUG_IF(VL_DBG_MSGF("- save: opening save file %s\n", filenamep););
-
-    if (VL_UNCOVERABLE(filenamep[0] == '|')) {
-        assert(0);  // LCOV_EXCL_LINE // Not supported yet.
-    } else {
-        // cppcheck-suppress duplicateExpression
-        m_fd = ::open(filenamep,
-                      O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE | O_NONBLOCK | O_CLOEXEC, 0666);
-        if (VL_UNLIKELY(m_fd < 0)) {
-            // User code can check isOpen()
-            m_isOpen = false;
-            return;
-        }
-    }
-    m_isOpen = true;
-    m_filename = filenamep;
-    m_cp = m_bufp;
-    header();
-}
-
-void VerilatedRestore::open(const char* filenamep) VL_MT_UNSAFE_ONE {
-    m_assertOne.check();
-    if (isOpen()) return;
-    VL_DEBUG_IF(VL_DBG_MSGF("- restore: opening restore file %s\n", filenamep););
-
-    if (VL_UNCOVERABLE(filenamep[0] == '|')) {
-        assert(0);  // LCOV_EXCL_LINE // Not supported yet.
-    } else {
-        // cppcheck-suppress duplicateExpression
-        m_fd = ::open(filenamep, O_CREAT | O_RDONLY | O_LARGEFILE | O_CLOEXEC, 0666);
-        if (VL_UNLIKELY(m_fd < 0)) {
-            // User code can check isOpen()
-            m_isOpen = false;
-            return;
-        }
-    }
-    m_isOpen = true;
-    m_filename = filenamep;
-    m_cp = m_bufp;
-    m_endp = m_bufp;
-    header();
-}
-
-void VerilatedSave::closeImp() VL_MT_UNSAFE_ONE {
-    if (!isOpen()) return;
-    trailer();
-    flushImp();
-    m_isOpen = false;
-    ::close(m_fd);  // May get error, just ignore it
-}
-
-void VerilatedRestore::closeImp() VL_MT_UNSAFE_ONE {
-    if (!isOpen()) return;
-    trailer();
-    flushImp();
-    m_isOpen = false;
-    ::close(m_fd);  // May get error, just ignore it
-}
-
-//=============================================================================
-// Buffer management
-
-void VerilatedSave::flushImp() VL_MT_UNSAFE_ONE {
-    m_assertOne.check();
-    if (VL_UNLIKELY(!isOpen())) return;
-    const uint8_t* wp = m_bufp;
-    while (true) {
-        const ssize_t remaining = (m_cp - wp);
-        if (remaining == 0) break;
-        errno = 0;
-        const ssize_t got = ::write(m_fd, wp, remaining);
-        if (got > 0) {
-            wp += got;
-        } else if (VL_UNCOVERABLE(got < 0)) {
-            if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
-                // LCOV_EXCL_START
-                // write failed, presume error (perhaps out of disk space)
-                const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
-                VL_FATAL_MT("", 0, "", msg.c_str());
-                close();
-                break;
-                // LCOV_EXCL_STOP
-            }
-        }
-    }
-    m_cp = m_bufp;  // Reset buffer
-}
-
-void VerilatedRestore::fill() VL_MT_UNSAFE_ONE {
-    m_assertOne.check();
-    if (VL_UNLIKELY(!isOpen())) return;
-    // Move remaining characters down to start of buffer.  (No memcpy, overlaps allowed)
-    uint8_t* rp = m_bufp;
-    for (const uint8_t* sp = m_cp; sp < m_endp; *rp++ = *sp++) {}  // Overlaps
-    m_endp = m_bufp + (m_endp - m_cp);
-    m_cp = m_bufp;  // Reset buffer
-    // Read into buffer starting at m_endp
-    while (true) {
-        const ssize_t remaining = (m_bufp + bufferSize() - m_endp);
-        if (remaining == 0) break;
-        errno = 0;
-        const ssize_t got = ::read(m_fd, m_endp, remaining);
-        if (got > 0) {
-            m_endp += got;
-        } else if (VL_UNCOVERABLE(got < 0)) {
-            if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
-                // LCOV_EXCL_START
-                // write failed, presume error (perhaps out of disk space)
-                const std::string msg = std::string{__FUNCTION__} + ": " + std::strerror(errno);
-                VL_FATAL_MT("", 0, "", msg.c_str());
-                close();
-                break;
-                // LCOV_EXCL_STOP
-            }
-        } else {  // got==0, EOF
-            // Fill buffer from here to end with NULLs so reader's don't
-            // need to check eof each character.
-            while (m_endp < m_bufp + bufferSize()) *m_endp++ = '\0';
-            break;
-        }
-    }
-}
-
-//=============================================================================
-// Serialization of types
-
-VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp) {
-    os.write(rhsp->serialized1Ptr(), rhsp->serialized1Size());
-    os << rhsp->impp()->timeFormatSuffix();
-    os << rhsp->dumpfile();
-    return os;
-}
-VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp) {
-    os.read(rhsp->serialized1Ptr(), rhsp->serialized1Size());
-    std::string s;
-    os >> s;
-    rhsp->impp()->timeFormatSuffix(s);
-    os >> s;
-    rhsp->dumpfile(s);
-    return os;
-}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.h
deleted file mode 100644
index aa547af68c6..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_save.h
+++ /dev/null
@@ -1,335 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2000-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated save-restore serialization header
-///
-/// This must be included in user wrapper code that wants to use
-/// save/restore.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_SAVE_C_H_
-#define VERILATOR_VERILATED_SAVE_C_H_
-
-#include "verilatedos.h"
-
-#include "verilated.h"
-
-#include 
-
-//=============================================================================
-// VerilatedSerialize
-/// Class for writing serialization of structures to a stream representation.
-///
-/// User wrapper code will more typically use VerilatedSave which uses this
-/// as a subclass to write a file.
-///
-/// This class is not thread safe, it must be called by a single thread
-
-class VerilatedSerialize VL_NOT_FINAL {
-protected:
-    // MEMBERS
-    // For speed, keep m_cp as the first member of this structure
-    uint8_t* m_cp;  // Current pointer into m_bufp buffer
-    uint8_t* m_bufp;  // Output buffer
-    bool m_isOpen = false;  // True indicates open file/stream
-    std::string m_filename;  // Filename, for error messages
-    VerilatedAssertOneThread m_assertOne;  // Assert only called from single thread
-
-    static constexpr size_t bufferSize() {
-        return 256 * 1024L;
-    }  // See below for slack calculation
-    static constexpr size_t bufferInsertSize() { return 16 * 1024L; }
-
-    void header() VL_MT_UNSAFE_ONE;
-    void trailer() VL_MT_UNSAFE_ONE;
-
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedSerialize);
-
-public:
-    /// Construct
-    VerilatedSerialize() {
-        m_bufp = new uint8_t[bufferSize()];
-        m_cp = m_bufp;
-    }
-    /// Flush, close, and destruct
-    virtual ~VerilatedSerialize() {
-        // Child classes will need to typically call closeImp() in destructors
-        if (m_bufp) VL_DO_CLEAR(delete[] m_bufp, m_bufp = nullptr);
-    }
-    // METHODS
-    /// Return true if file is open
-    bool isOpen() const { return m_isOpen; }
-    /// Return current filename
-    std::string filename() const { return m_filename; }
-    /// Close the stream
-    virtual void close() VL_MT_UNSAFE_ONE { flush(); }
-    /// Flush pending data to stream
-    virtual void flush() VL_MT_UNSAFE_ONE {}
-    /// Write data to stream
-    VerilatedSerialize& write(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
-        const uint8_t* __restrict dp = static_cast(datap);
-        while (size) {
-            bufferCheck();
-            size_t blk = size;
-            if (blk > bufferInsertSize()) blk = bufferInsertSize();
-            const uint8_t* __restrict maxp = dp + blk;
-            for (; dp < maxp; *m_cp++ = *dp++) {}
-            size -= blk;
-        }
-        return *this;  // For function chaining
-    }
-
-private:
-    VerilatedSerialize& bufferCheck() VL_MT_UNSAFE_ONE {
-        // Flush the write buffer if there's not enough space left for new information
-        // We only call this once per vector, so we need enough slop for a very wide "b###" line
-        if (VL_UNLIKELY(m_cp > (m_bufp + (bufferSize() - bufferInsertSize())))) flush();
-        return *this;  // For function chaining
-    }
-};
-
-//=============================================================================
-// VerilatedDeserialize
-/// Class for loading structures from a stream representation.
-///
-/// User wrapper code will more typically use VerilatedRestore which uses
-/// this as a subclass to a read from a file.
-///
-/// This class is not thread safe, it must be called by a single thread
-
-class VerilatedDeserialize VL_NOT_FINAL {
-protected:
-    // MEMBERS
-    // For speed, keep m_cp as the first member of this structure
-    uint8_t* m_cp;  // Current pointer into m_bufp buffer
-    uint8_t* m_bufp;  // Output buffer
-    uint8_t* m_endp = nullptr;  // Last valid byte in m_bufp buffer
-    bool m_isOpen = false;  // True indicates open file/stream
-    std::string m_filename;  // Filename, for error messages
-    VerilatedAssertOneThread m_assertOne;  // Assert only called from single thread
-
-    static constexpr size_t bufferSize() {
-        return 256 * 1024L;
-    }  // See below for slack calculation
-    static constexpr size_t bufferInsertSize() { return 16 * 1024L; }
-
-    virtual void fill() = 0;
-    void header() VL_MT_UNSAFE_ONE;
-    void trailer() VL_MT_UNSAFE_ONE;
-
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedDeserialize);
-
-public:
-    /// Construct
-    VerilatedDeserialize() {
-        m_bufp = new uint8_t[bufferSize()];
-        m_cp = m_bufp;
-    }
-    /// Destruct
-    virtual ~VerilatedDeserialize() {
-        // Child classes will need to typically call closeImp() in destructors
-        if (m_bufp) VL_DO_CLEAR(delete[] m_bufp, m_bufp = nullptr);
-    }
-    // METHODS
-    /// Return true if file is open
-    bool isOpen() const { return m_isOpen; }
-    /// Return current filename
-    std::string filename() const { return m_filename; }
-    /// Close the stream
-    virtual void close() VL_MT_UNSAFE_ONE { flush(); }
-    /// Flush pending data to stream
-    virtual void flush() VL_MT_UNSAFE_ONE {}
-    /// Read data from stream
-    VerilatedDeserialize& read(void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE {
-        uint8_t* __restrict dp = static_cast(datap);
-        while (size) {
-            bufferCheck();
-            size_t blk = size;
-            if (blk > bufferInsertSize()) blk = bufferInsertSize();
-            const uint8_t* __restrict maxp = dp + blk;
-            for (; dp < maxp; *dp++ = *m_cp++) {}
-            size -= blk;
-        }
-        return *this;  // For function chaining
-    }
-
-    // Internal use:
-    // Read a datum and compare with expected value
-    VerilatedDeserialize& readAssert(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
-    VerilatedDeserialize& readAssert(uint64_t data) VL_MT_UNSAFE_ONE {
-        return readAssert(&data, sizeof(data));
-    }
-
-private:
-    bool readDiffers(const void* __restrict datap, size_t size) VL_MT_UNSAFE_ONE;
-    VerilatedDeserialize& bufferCheck() VL_MT_UNSAFE_ONE {
-        // Flush the write buffer if there's not enough space left for new information
-        // We only call this once per vector, so we need enough slop for a very wide "b###" line
-        if (VL_UNLIKELY((m_cp + bufferInsertSize()) > m_endp)) fill();
-        return *this;  // For function chaining
-    }
-};
-
-//=============================================================================
-// VerilatedSave
-/// Stream-like object that serializes Verilated model to a file.
-///
-/// This class is not thread safe, it must be called by a single thread
-
-class VerilatedSave final : public VerilatedSerialize {
-private:
-    int m_fd = -1;  // File descriptor we're writing to
-
-    void closeImp() VL_MT_UNSAFE_ONE;
-    void flushImp() VL_MT_UNSAFE_ONE;
-
-public:
-    // CONSTRUCTORS
-    /// Construct new object
-    VerilatedSave() = default;
-    /// Flush, close and destruct
-    ~VerilatedSave() override { closeImp(); }
-    // METHODS
-    /// Open the file; call isOpen() to see if errors
-    void open(const char* filenamep) VL_MT_UNSAFE_ONE;
-    /// Open the file; call isOpen() to see if errors
-    void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
-    /// Flush and close the file
-    void close() override VL_MT_UNSAFE_ONE { closeImp(); }
-    /// Flush data to file
-    void flush() override VL_MT_UNSAFE_ONE { flushImp(); }
-};
-
-//=============================================================================
-// VerilatedRestore
-/// Stream-like object that serializes Verilated model from a file.
-///
-/// This class is not thread safe, it must be called by a single thread
-
-class VerilatedRestore final : public VerilatedDeserialize {
-private:
-    int m_fd = -1;  // File descriptor we're writing to
-
-    void closeImp() VL_MT_UNSAFE_ONE;
-    void flushImp() VL_MT_UNSAFE_ONE {}
-
-public:
-    // CONSTRUCTORS
-    /// Construct new object
-    VerilatedRestore() = default;
-    /// Flush, close and destruct
-    ~VerilatedRestore() override { closeImp(); }
-
-    // METHODS
-    /// Open the file; call isOpen() to see if errors
-    void open(const char* filenamep) VL_MT_UNSAFE_ONE;
-    /// Open the file; call isOpen() to see if errors
-    void open(const std::string& filename) VL_MT_UNSAFE_ONE { open(filename.c_str()); }
-    /// Close the file
-    void close() override VL_MT_UNSAFE_ONE { closeImp(); }
-    void flush() override VL_MT_UNSAFE_ONE { flushImp(); }
-    void fill() override VL_MT_UNSAFE_ONE;
-};
-
-//=============================================================================
-
-inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint64_t& rhs) {
-    return os.write(&rhs, sizeof(rhs));
-}
-inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint64_t& rhs) {
-    return os.read(&rhs, sizeof(rhs));
-}
-inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint32_t& rhs) {
-    return os.write(&rhs, sizeof(rhs));
-}
-inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint32_t& rhs) {
-    return os.read(&rhs, sizeof(rhs));
-}
-inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint16_t& rhs) {
-    return os.write(&rhs, sizeof(rhs));
-}
-inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint16_t& rhs) {
-    return os.read(&rhs, sizeof(rhs));
-}
-inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const uint8_t& rhs) {
-    return os.write(&rhs, sizeof(rhs));
-}
-inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, uint8_t& rhs) {
-    return os.read(&rhs, sizeof(rhs));
-}
-inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const bool& rhs) {
-    return os.write(&rhs, sizeof(rhs));
-}
-inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, bool& rhs) {
-    return os.read(&rhs, sizeof(rhs));
-}
-inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const double& rhs) {
-    return os.write(&rhs, sizeof(rhs));
-}
-inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, double& rhs) {
-    return os.read(&rhs, sizeof(rhs));
-}
-inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const float& rhs) {
-    return os.write(&rhs, sizeof(rhs));
-}
-inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, float& rhs) {
-    return os.read(&rhs, sizeof(rhs));
-}
-inline VerilatedSerialize& operator<<(VerilatedSerialize& os, const std::string& rhs) {
-    const uint32_t len = rhs.length();
-    os << len;
-    return os.write(rhs.data(), len);
-}
-inline VerilatedDeserialize& operator>>(VerilatedDeserialize& os, std::string& rhs) {
-    uint32_t len = 0;
-    os >> len;
-    rhs.resize(len);
-    // cppcheck-suppress cstyleCast  // NOLINTNEXTLINE(google-readability-casting)
-    return os.read((void*)(rhs.data()), len);
-}
-VerilatedSerialize& operator<<(VerilatedSerialize& os, VerilatedContext* rhsp);
-VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VerilatedContext* rhsp);
-
-template 
-VerilatedSerialize& operator<<(VerilatedSerialize& os, VlAssocArray& rhs) {
-    os << rhs.atDefault();
-    const uint32_t len = rhs.size();
-    os << len;
-    for (const auto& i : rhs) {
-        const T_Key index = i.first;  // Copy to get around const_iterator
-        const T_Value value = i.second;
-        os << index << value;
-    }
-    return os;
-}
-template 
-VerilatedDeserialize& operator>>(VerilatedDeserialize& os, VlAssocArray& rhs) {
-    os >> rhs.atDefault();
-    uint32_t len = 0;
-    os >> len;
-    rhs.clear();
-    for (uint32_t i = 0; i < len; ++i) {
-        T_Key index;
-        T_Value value;
-        os >> index;
-        os >> value;
-        rhs.at(index) = value;
-    }
-    return os;
-}
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc.h
deleted file mode 100644
index d59a9f5c92a..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc.h
+++ /dev/null
@@ -1,52 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2009-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilated SystemC header for all Verilated SystemC files
-///
-/// This file is included automatically by Verilator at the top of all
-/// SystemC files it generates.  It contains functions Verilated code uses
-/// internally to connect the Verilated model into SystemC.
-///
-/// User wrapper code is not required to include nor use anything in this
-/// header, but may prefer to include it in place of "verilated.h" when
-/// using Verilator with SystemC.
-///
-//*************************************************************************
-
-#ifndef VERILATOR_VERILATED_SC_H_
-#define VERILATOR_VERILATED_SC_H_
-
-#include "verilatedos.h"
-
-#include 
-
-//=============================================================================
-// For \internal use, get a pointer to m_data in the sc_bv_base class,
-// getting around that it is protected.  So make an exposing class, then
-// use cast magic to get at it.  Saves patching get_datap in SystemC.
-#define VL_SC_BV_DATAP(bv) (VlScBvExposer::sp_datap(bv))
-// This class is thread safe (though most of SystemC is not).
-class VlScBvExposer final : public sc_dt::sc_bv_base {
-public:
-    static const uint32_t* sp_datap(const sc_dt::sc_bv_base& base) VL_MT_SAFE {
-        return static_cast(&base)->sp_datatp();
-    }
-    const uint32_t* sp_datatp() const { return reinterpret_cast(m_data); }
-    // Above reads this protected element in sc_bv_base:
-    //   sc_digit* m_data; // data array
-};
-
-//=========================================================================
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc_trace.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc_trace.h
deleted file mode 100644
index 5f53b103cec..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sc_trace.h
+++ /dev/null
@@ -1,247 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated tracing for SystemC implementation code
-///
-///
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_SC_TRACE_H_
-#define VERILATOR_VERILATED_SC_TRACE_H_
-
-#include "verilatedos.h"
-
-#include "verilated.h"
-#include "verilated_sc.h"
-
-#if SYSTEMC_VERSION >= 20140417 && SYSTEMC_VERSION < 20231124
-// SystemC's simulation phase callback introduced in 2.3.1, and removed since 3.0.0 (PubRev)
-#define _VL_HAVE_SYSTEMC_PHASE_CALLBACK
-#endif
-#if SYSTEMC_VERSION >= 20231124
-// SystemC's stage callback introduced in 3.0.0 (PubRev)
-#define _VL_HAVE_SYSTEMC_STAGE_CALLBACK
-#endif
-
-//=============================================================================
-// VerilatedScTraceBase
-// Base class for VCD/FST trace format on SystemC
-// This is an internally used class - see VerilatedVcdSc and VerilatedFstSc for what to call from
-// applications
-//
-/// This class utilizes SystemC's callbacks, which allows to dump signals inside the Verilated
-/// module automatically as time advances.
-///
-/// For SystemC prior to 2.3.1, the only approach for being notified after each update is by adding
-/// a trace file (sc_trace_file) to the simulation context. And after this version the simulation
-/// phase callback approach has been introduced (sc_trace_file also utilizes this), which is
-/// presented only if it's enabled with the `--enable-phase-callbacks` option. However, when it's
-/// enabled with `--enable-phase-callbacks=tracing`, trace files will be therefore disabled, thus
-/// failing to provide its functionality.
-///
-/// To provide a universal way for tracing, the class attempts to register a phase callback first.
-/// If it fails (proving that the feature has been disabled), it'll use the trace file approach
-/// instead.
-
-class VerilatedScTraceBase VL_NOT_FINAL : private sc_core::sc_object,
-#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK
-                                          private sc_core::sc_stage_callback_if,
-#endif
-                                          private sc_core::sc_trace_file {
-    bool m_enableDeltaCycles = false;
-    bool m_traceFileAdded = false;
-    static void stubReportHandler(const sc_core::sc_report&, const sc_core::sc_actions&) {};
-
-public:
-    void enableDeltaCycles(bool flag = true) {
-        using namespace sc_core;
-#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK
-        // Save old report handler before overriding it
-        const auto oldHandler = sc_report_handler::get_handler();
-        // Override the old handler to hide 'phase callbacks not enabled' message
-        sc_report_handler::set_handler(&stubReportHandler);
-        if (flag) {
-            // Register simulation phase callback for delta cycles
-            sc_object::register_simulation_phase_callback(SC_END_OF_UPDATE);
-        } else {
-            sc_object::unregister_simulation_phase_callback(SC_END_OF_UPDATE);
-        }
-        // Restore the old handler
-        sc_report_handler::set_handler(oldHandler);
-#endif
-#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK
-        if (flag) {
-            sc_register_stage_callback(*this, SC_POST_UPDATE);
-        } else {
-            sc_unregister_stage_callback(*this, SC_POST_UPDATE);
-        }
-#endif
-        m_enableDeltaCycles = flag;
-    }
-
-protected:
-    VerilatedScTraceBase()
-        : sc_object(sc_core::sc_gen_unique_name("$$$$verilator_sc_trace$$$$"))
-        , sc_trace_file() {
-        registerTraceCallback();
-    };
-    ~VerilatedScTraceBase() override {
-        using namespace sc_core;
-#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK
-        // remove_trace_file added in 2.3.1 and removed in 3.0.0
-        // Phase callback is automatically unregistered in ~sc_object(). Only the trace file is
-        // needed to be removed here
-        if (m_traceFileAdded) simcontext()->remove_trace_file(this);
-#else
-        (void)m_traceFileAdded;
-#endif
-#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK
-        sc_unregister_stage_callback(*this, SC_PRE_TIMESTEP | SC_POST_UPDATE);
-#endif
-    };
-    void registerTraceCallback() {
-        using namespace sc_core;
-#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK
-        // Save old report handler before overriding it
-        const auto oldHandler = sc_report_handler::get_handler();
-        // Override the old handler to hide 'phase callbacks not enabled' message
-        sc_report_handler::set_handler(&stubReportHandler);
-        // Register regular simulation phase (non-delta cycle) callback
-        phase_cb_mask cb_mask = sc_object::register_simulation_phase_callback(SC_BEFORE_TIMESTEP);
-        if (cb_mask == SC_UNITIALIZED) {
-#endif
-#if SYSTEMC_VERSION < 20231124  // add_trace_file removed in 3.0.0
-            // Phase callback not enabled, use trace file instead
-            simcontext()->add_trace_file(this);
-            m_traceFileAdded = true;
-#endif
-#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK
-        }
-        // Restore the old handler
-        sc_report_handler::set_handler(oldHandler);
-#endif
-#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK
-        sc_register_stage_callback(*this, SC_PRE_TIMESTEP);
-#endif
-    }
-    static std::string getScTimeUnit() {
-        // We want to avoid a depreciated warning, but still be back compatible.
-        // Turning off the message just for this still results in an
-        // annoying "to turn off" message.
-        const sc_core::sc_time t1sec{1, sc_core::SC_SEC};
-        if (t1sec.to_default_time_units() == 0) {
-            VL_FATAL_MT(__FILE__, __LINE__, "",  // LCOV_EXCL_LINE
-                        "Cannot to get valid SystemC default time unit for trace file");
-        }
-        const sc_core::sc_time tunits{1.0 / t1sec.to_default_time_units(), sc_core::SC_SEC};
-        return tunits.to_string();
-    }
-    static std::string getScTimeResolution() {
-        return sc_core::sc_get_time_resolution().to_string();
-    }
-    static void checkScElaborationDone() {
-        if (!sc_core::sc_get_curr_simcontext()->elaboration_done()) {
-            Verilated::scTraceBeforeElaborationError();
-        }
-    }
-
-    // METHODS - for SC kernel
-#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK
-    // Override sc_object. Called if using phase callback
-    void simulation_phase_callback() final { cycle(); }
-#endif
-#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK
-    // Override sc_stage_callback_if. Called if using stage callback
-    void stage_callback(const sc_core::sc_stage&) final { cycle(); }
-#endif
-    // Override sc_trace_file. Called if using trace file
-    void cycle(bool delta_cycle) final {
-        if (!delta_cycle || m_enableDeltaCycles) cycle();
-    }
-    // METHODS - callbacks
-    // Subclasses should implement this callback method
-    virtual void cycle() = 0;
-
-private:
-    // METHODS - Fake outs for linker
-    // LCOV_EXCL_START
-
-#ifdef NC_SYSTEMC
-    // Cadence Incisive has these as abstract functions so we must create them
-    void set_time_unit(int exponent10_seconds) override {}  // deprecated
-#endif
-    void set_time_unit(double v, sc_core::sc_time_unit tu) override {}
-
-    //--------------------------------------------------
-    // SystemC 2.1.v1
-
-    void write_comment(const std::string&) override {}
-    void trace(sc_core::sc_trace_file*) const override {}
-    void trace(const unsigned int&, const std::string&, const char**) override {}
-
-#define DECL_TRACE_METHOD_A(tp) \
-    void trace(const tp& object, const std::string& name) override {}
-#define DECL_TRACE_METHOD_B(tp) \
-    void trace(const tp& object, const std::string& name, int width) override {}
-
-    // clang-format off
-    // Formatting matches that of sc_trace.h
-#if SYSTEMC_VERSION >= 20171012  // SystemC >= 2.3.2
-    DECL_TRACE_METHOD_A( sc_core::sc_event )
-    DECL_TRACE_METHOD_A( sc_core::sc_time )
-#endif
-
-    DECL_TRACE_METHOD_A( bool )
-    DECL_TRACE_METHOD_A( sc_dt::sc_bit )
-    DECL_TRACE_METHOD_A( sc_dt::sc_logic )
-
-    DECL_TRACE_METHOD_B( unsigned char )
-    DECL_TRACE_METHOD_B( unsigned short )
-    DECL_TRACE_METHOD_B( unsigned int )
-    DECL_TRACE_METHOD_B( unsigned long )
-    DECL_TRACE_METHOD_B( char )
-    DECL_TRACE_METHOD_B( short )
-    DECL_TRACE_METHOD_B( int )
-    DECL_TRACE_METHOD_B( long )
-    DECL_TRACE_METHOD_B( sc_dt::int64 )
-    DECL_TRACE_METHOD_B( sc_dt::uint64 )
-
-    DECL_TRACE_METHOD_A( float )
-    DECL_TRACE_METHOD_A( double )
-    DECL_TRACE_METHOD_A( sc_dt::sc_int_base )
-    DECL_TRACE_METHOD_A( sc_dt::sc_uint_base )
-    DECL_TRACE_METHOD_A( sc_dt::sc_signed )
-    DECL_TRACE_METHOD_A( sc_dt::sc_unsigned )
-
-    DECL_TRACE_METHOD_A( sc_dt::sc_fxval )
-    DECL_TRACE_METHOD_A( sc_dt::sc_fxval_fast )
-    DECL_TRACE_METHOD_A( sc_dt::sc_fxnum )
-    DECL_TRACE_METHOD_A( sc_dt::sc_fxnum_fast )
-
-    DECL_TRACE_METHOD_A( sc_dt::sc_bv_base )
-    DECL_TRACE_METHOD_A( sc_dt::sc_lv_base )
-    // LCOV_EXCL_STOP
-    // clang-format on
-
-#undef DECL_TRACE_METHOD_A
-#undef DECL_TRACE_METHOD_B
-};
-
-#ifdef _VL_HAVE_SYSTEMC_PHASE_CALLBACK
-#undef _VL_HAVE_SYSTEMC_PHASE_CALLBACK
-#endif
-#ifdef _VL_HAVE_SYSTEMC_STAGE_CALLBACK
-#undef _VL_HAVE_SYSTEMC_STAGE_CALLBACK
-#endif
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sym_props.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sym_props.h
deleted file mode 100644
index 1cdd3a9905e..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_sym_props.h
+++ /dev/null
@@ -1,278 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilated symbol inspection header
-///
-/// This file is for inclusion by internal files that need to inspect
-/// specific symbols.  Applications typically use the VPI instead.
-///
-/// User wrapper code wanting to inspect the symbol table should use
-/// verilated_syms.h instead.
-///
-//*************************************************************************
-// These classes are thread safe, and read only.
-
-#ifndef VERILATOR_VERILATED_SYM_PROPS_H_
-#define VERILATOR_VERILATED_SYM_PROPS_H_
-
-#include "verilatedos.h"
-
-#include 
-
-//===========================================================================
-// Verilator range
-// Thread safety: Assume is constructed only with model, then any number of readers
-
-// See also V3Ast::VNumRange
-class VerilatedRange final {
-    int m_left = 0;
-    int m_right = 0;
-
-protected:
-    friend class VerilatedVarProps;
-    friend class VerilatedScope;
-    VerilatedRange() = default;
-    void init(int left, int right) {
-        m_left = left;
-        m_right = right;
-    }
-
-public:
-    VerilatedRange(int left, int right)
-        : m_left{left}
-        , m_right{right} {}
-    ~VerilatedRange() = default;
-    int left() const VL_PURE { return m_left; }
-    int right() const VL_PURE { return m_right; }
-    int low() const VL_PURE { return (m_left < m_right) ? m_left : m_right; }
-    int high() const VL_PURE { return (m_left > m_right) ? m_left : m_right; }
-    int elements() const VL_PURE {
-        return (VL_LIKELY(m_left >= m_right) ? (m_left - m_right + 1) : (m_right - m_left + 1));
-    }
-    int increment() const VL_PURE { return (m_left >= m_right) ? 1 : -1; }
-};
-
-//===========================================================================
-// Verilator variable
-// Thread safety: Assume is constructed only with model, then any number of readers
-
-class VerilatedVarProps VL_NOT_FINAL {
-    // TYPES
-    static constexpr uint32_t MAGIC = 0xddc4f829UL;
-    // MEMBERS
-    const uint32_t m_magic;  // Magic number
-    const VerilatedVarType m_vltype;  // Data type
-    const VerilatedVarFlags m_vlflags;  // Direction
-    std::vector m_unpacked;  // Unpacked array ranges
-    std::vector m_packed;  // Packed array ranges
-    VerilatedRange m_packedDpi;  // Flattened packed array range
-    void initUnpacked(int udims, const int* ulims) {
-        for (int i = 0; i < udims; ++i) {
-            const int uleft = ulims ? ulims[2 * i + 0] : 0;
-            const int uright = ulims ? ulims[2 * i + 1] : 0;
-            m_unpacked.emplace_back(uleft, uright);
-        }
-    }
-    void initPacked(int pdims, const int* plims) {
-        int packedSize = 1;
-        for (int i = 0; i < pdims; ++i) {
-            const int pleft = plims ? plims[2 * i + 0] : 0;
-            const int pright = plims ? plims[2 * i + 1] : 0;
-            m_packed.emplace_back(pleft, pright);
-            packedSize *= abs(pleft - pright) + 1;
-        }
-        if (pdims == 1) {
-            // Preserve packed array range if the packed component is 1-D
-            m_packedDpi = m_packed.front();
-        } else {
-            m_packedDpi = VerilatedRange{packedSize - 1, 0};
-        }
-    }
-    // CONSTRUCTORS
-protected:
-    friend class VerilatedScope;
-    VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims, int pdims)
-        : m_magic{MAGIC}
-        , m_vltype{vltype}
-        , m_vlflags{vlflags} {
-        // Only preallocate the ranges
-        initUnpacked(udims, nullptr);
-        initPacked(pdims, nullptr);
-    }
-
-public:
-    class Unpacked {};
-    // Without packed
-    VerilatedVarProps(VerilatedVarType vltype, int vlflags)
-        : m_magic{MAGIC}
-        , m_vltype{vltype}
-        , m_vlflags(VerilatedVarFlags(vlflags)) {}  // Need () or GCC 4.8 false warning
-
-    VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims)
-        : m_magic{MAGIC}
-        , m_vltype{vltype}
-        , m_vlflags(VerilatedVarFlags(vlflags)) {  // Need () or GCC 4.8 false warning
-        initUnpacked(udims, ulims);
-    }
-    // With packed
-    class Packed {};
-    VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pdims, const int* plims)
-        : m_magic{MAGIC}
-        , m_vltype{vltype}
-        , m_vlflags(VerilatedVarFlags(vlflags)) {  // Need () or GCC 4.8 false warning
-        initPacked(pdims, plims);
-    }
-    VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims,
-                      Packed, int pdims, const int* plims)
-        : m_magic{MAGIC}
-        , m_vltype{vltype}
-        , m_vlflags(VerilatedVarFlags(vlflags)) {  // Need () or GCC 4.8 false warning
-        initUnpacked(udims, ulims);
-        initPacked(pdims, plims);
-    }
-
-    ~VerilatedVarProps() = default;
-    // METHODS
-    bool magicOk() const { return m_magic == MAGIC; }
-    VerilatedVarType vltype() const VL_MT_SAFE { return m_vltype; }
-    VerilatedVarFlags vldir() const {
-        return static_cast(static_cast(m_vlflags) & VLVF_MASK_DIR);
-    }
-    uint32_t entSize() const VL_MT_SAFE;
-    uint32_t entBits() const VL_MT_SAFE {
-        uint32_t bits = 1;
-        for (auto it : m_packed) bits *= it.elements();
-        return bits;
-    }
-    bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); }
-    bool isForceable() const { return ((m_vlflags & VLVF_FORCEABLE) != 0); }
-    bool isContinuously() const { return ((m_vlflags & VLVF_CONTINUOUSLY) != 0); }
-    // DPI compatible C standard layout
-    bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); }
-    bool isSigned() const { return ((m_vlflags & VLVF_SIGNED) != 0); }
-    bool isBitVar() const { return ((m_vlflags & VLVF_BITVAR) != 0); }
-    int udims() const VL_MT_SAFE { return m_unpacked.size(); }
-    int pdims() const VL_MT_SAFE { return m_packed.size(); }
-    int dims() const VL_MT_SAFE { return pdims() + udims(); }
-    const std::vector& packedRanges() const VL_MT_SAFE { return m_packed; }
-    const std::vector& unpackedRanges() const VL_MT_SAFE { return m_unpacked; }
-    const VerilatedRange* range(int dim) const VL_MT_SAFE {
-        if (dim < udims())
-            return &m_unpacked[dim];
-        else if (dim < dims())
-            return &m_packed[dim - udims()];
-        else
-            return nullptr;
-    }
-    // DPI accessors (with packed dimensions flattened!)
-    int left(int dim) const VL_MT_SAFE {
-        return dim == 0                                ? m_packedDpi.left()
-               : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left()
-                                                       : 0;
-    }
-    int right(int dim) const VL_MT_SAFE {
-        return dim == 0                                ? m_packedDpi.right()
-               : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right()
-                                                       : 0;
-    }
-    int low(int dim) const VL_MT_SAFE {
-        return dim == 0                                ? m_packedDpi.low()
-               : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low()
-                                                       : 0;
-    }
-    int high(int dim) const VL_MT_SAFE {
-        return dim == 0                                ? m_packedDpi.high()
-               : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high()
-                                                       : 0;
-    }
-    int increment(int dim) const {
-        return dim == 0                                ? m_packedDpi.increment()
-               : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment()
-                                                       : 0;
-    }
-    int elements(int dim) const VL_MT_SAFE {
-        return dim == 0                                ? m_packedDpi.elements()
-               : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements()
-                                                       : 0;
-    }
-    // Total size in bytes (note DPI limited to 4GB)
-    size_t totalSize() const;
-    // Adjust a data pointer to access a given array element, NULL if something goes bad
-    void* datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE;
-};
-
-//===========================================================================
-// Verilator DPI open array variable
-
-class VerilatedDpiOpenVar final {
-    // MEMBERS
-    const VerilatedVarProps* const m_propsp;  // Variable properties
-    void* const m_datap;  // Location of data (local to thread always, so safe)
-public:
-    // CONSTRUCTORS
-    VerilatedDpiOpenVar(const VerilatedVarProps* propsp, void* datap)
-        : m_propsp{propsp}
-        , m_datap{datap} {}
-    VerilatedDpiOpenVar(const VerilatedVarProps* propsp, const void* datap)
-        : m_propsp{propsp}
-        , m_datap{const_cast(datap)} {}
-    ~VerilatedDpiOpenVar() = default;
-    // METHODS
-    void* datap() const VL_MT_SAFE { return m_datap; }
-    // METHODS - from VerilatedVarProps
-    bool magicOk() const { return m_propsp->magicOk(); }
-    VerilatedVarType vltype() const { return m_propsp->vltype(); }
-    bool isDpiStdLayout() const { return m_propsp->isDpiCLayout(); }
-    int entBits() const { return m_propsp->entBits(); }
-    int udims() const VL_MT_SAFE { return m_propsp->udims(); }
-    int left(int dim) const VL_MT_SAFE { return m_propsp->left(dim); }
-    int right(int dim) const VL_MT_SAFE { return m_propsp->right(dim); }
-    int low(int dim) const { return m_propsp->low(dim); }
-    int high(int dim) const { return m_propsp->high(dim); }
-    int increment(int dim) const { return m_propsp->increment(dim); }
-    int elements(int dim) const { return m_propsp->elements(dim); }
-    size_t totalSize() const { return m_propsp->totalSize(); }
-    void* datapAdjustIndex(void* datap, int dim, int indx) const VL_MT_SAFE {
-        return m_propsp->datapAdjustIndex(datap, dim, indx);
-    }
-};
-
-//===========================================================================
-// Verilator variable
-// Thread safety: Assume is constructed only with model, then any number of readers
-
-class VerilatedVar final : public VerilatedVarProps {
-    // MEMBERS
-    void* const m_datap;  // Location of data
-    const char* const m_namep;  // Name - slowpath
-protected:
-    const bool m_isParam;
-    friend class VerilatedScope;
-    // CONSTRUCTORS
-    VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype,
-                 VerilatedVarFlags vlflags, int udims, int pdims, bool isParam)
-        : VerilatedVarProps{vltype, vlflags, udims, pdims}
-        , m_datap{datap}
-        , m_namep{namep}
-        , m_isParam{isParam} {}
-
-public:
-    ~VerilatedVar() = default;
-    // ACCESSORS
-    void* datap() const { return m_datap; }
-    const char* name() const { return m_namep; }
-    bool isParam() const { return m_isParam; }
-};
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_syms.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_syms.h
deleted file mode 100644
index 949dbde9eed..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_syms.h
+++ /dev/null
@@ -1,75 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode"
-//-*- *************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilated symbol inspection header
-///
-/// This file is for inclusion by user wrapper code that needs to inspect
-/// the symbol table.  It is not included in verilated.h (instead see
-/// verilated_sym_props.h) as it requires some heavyweight C++ classes.
-///
-/// These classes are rarely used by user code; typical user code will
-/// instead use the VPI to access this information.
-///
-/// These classes are thread safe and read only. It is constructed only
-/// when a model is built (from the main thread).
-///
-//*************************************************************************
-
-#ifndef VERILATOR_VERILATED_SYMS_H_
-#define VERILATOR_VERILATED_SYMS_H_
-
-#include "verilatedos.h"
-
-#include "verilated.h"
-#include "verilated_sym_props.h"
-
-#include 
-#include 
-#include 
-
-//======================================================================
-// Types
-
-// Class to sort maps keyed by const char*'s
-struct VerilatedCStrCmp final {
-    bool operator()(const char* a, const char* b) const { return std::strcmp(a, b) < 0; }
-};
-
-// Map of sorted scope names to find associated scope class
-// This is a class instead of typedef/using to allow forward declaration in verilated.h
-class VerilatedScopeNameMap final
-    : public std::map {
-public:
-    VerilatedScopeNameMap() = default;
-    ~VerilatedScopeNameMap() = default;
-};
-
-// Map of sorted variable names to find associated variable class
-// This is a class instead of typedef/using to allow forward declaration in verilated.h
-class VerilatedVarNameMap final : public std::map {
-public:
-    VerilatedVarNameMap() = default;
-    ~VerilatedVarNameMap() = default;
-};
-
-// Map of parent scope to vector of children scopes
-// This is a class instead of typedef/using to allow forward declaration in verilated.h
-class VerilatedHierarchyMap final
-    : public std::unordered_map> {
-public:
-    VerilatedHierarchyMap() = default;
-    ~VerilatedHierarchyMap() = default;
-};
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.cpp
deleted file mode 100644
index d814b585c7e..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.cpp
+++ /dev/null
@@ -1,281 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2012-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated thread pool implementation code
-///
-/// This file must be compiled and linked against all Verilated objects
-/// that use --threads.
-///
-/// Use "verilator --threads" to add this to the Makefile for the linker.
-///
-//=============================================================================
-
-#include "verilatedos.h"
-
-#include "verilated_threads.h"
-
-#include 
-#include 
-#include 
-#include 
-#include 
-
-#ifdef __FreeBSD__
-#include 
-#endif
-
-//=============================================================================
-// Globals
-
-// Internal note: Globals may multi-construct, see verilated.cpp top.
-
-std::atomic VlMTaskVertex::s_yields;
-
-//=============================================================================
-// VlMTaskVertex
-
-VlMTaskVertex::VlMTaskVertex(uint32_t upstreamDepCount)
-    : m_upstreamDepsDone{0}
-    , m_upstreamDepCount{upstreamDepCount} {
-    assert(atomic_is_lock_free(&m_upstreamDepsDone));
-}
-
-//=============================================================================
-// VlWorkerThread
-
-VlWorkerThread::VlWorkerThread(VerilatedContext* contextp)
-    : m_ready_size{0}
-    , m_contextp{contextp} {
-#ifdef VL_USE_PTHREADS
-    // Init attributes
-    pthread_attr_t attr;
-    pthread_attr_init(&attr);
-    // Attempt to use the same stack size as the current (main) thread if possible
-    const size_t stacksize = pthread_get_stacksize_np(pthread_self());
-    if (!stacksize || pthread_attr_setstacksize(&attr, stacksize)) {
-        // Fall back on default atributes if failed to get/set stack size
-        pthread_attr_destroy(&attr);
-        pthread_attr_init(&attr);
-    }
-    // Create thread
-    if (pthread_create(&m_pthread, &attr, &VlWorkerThread::start, this)) {
-        std::cerr << "pthread_create failed" << std::endl;
-        std::abort();
-    }
-    // Destroy attributes
-    pthread_attr_destroy(&attr);
-#else
-    m_cthread = std::thread(start, this);
-#endif
-}
-
-VlWorkerThread::~VlWorkerThread() {
-    shutdown();
-    // The thread should exit; join it.
-#ifdef VL_USE_PTHREADS
-    pthread_join(m_pthread, nullptr);
-#else
-    m_cthread.join();
-#endif
-}
-
-static void shutdownTask(void*, bool) {  // LCOV_EXCL_LINE
-    // Deliberately empty, we use the address of this function as a magic number
-}
-
-void VlWorkerThread::shutdown() { addTask(shutdownTask, nullptr); }
-
-void VlWorkerThread::wait() {
-    // Enqueue a task that sets this flag. Execution is in-order so this ensures completion.
-    std::atomic flag{false};
-    addTask([](void* flagp, bool) { static_cast*>(flagp)->store(true); }, &flag);
-    // Spin wait
-    for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
-        if (flag.load()) return;
-        VL_CPU_RELAX();
-    }
-    // Yield wait
-    while (!flag.load()) std::this_thread::yield();
-}
-
-void VlWorkerThread::main() {
-    // Initialize thread_locals
-    Verilated::threadContextp(m_contextp);
-    // One work item
-    ExecRec work;
-    // Wait for the first task without spinning, in case the thread is never actually used.
-    dequeWork(&work);
-    // Loop until shutdown task is received
-    while (VL_UNLIKELY(work.m_fnp != shutdownTask)) {
-        work.m_fnp(work.m_selfp, work.m_evenCycle);
-        // Wait for next task with spinning.
-        dequeWork(&work);
-    }
-}
-
-void* VlWorkerThread::start(void* argp) {
-    reinterpret_cast(argp)->main();
-    return nullptr;
-}
-
-//=============================================================================
-// VlThreadPool
-
-VlThreadPool::VlThreadPool(VerilatedContext* contextp, unsigned nThreads) {
-    for (unsigned i = 0; i < nThreads; ++i) {
-        m_workers.push_back(new VlWorkerThread{contextp});
-        m_unassignedWorkers.push(i);
-    }
-    m_numaStatus = numaAssign(contextp);
-}
-
-VlThreadPool::~VlThreadPool() {
-    // Each ~WorkerThread will wait for its thread to exit.
-    for (auto& i : m_workers) delete i;
-}
-
-std::string VlThreadPool::numaAssign(VerilatedContext* contextp) {
-#if defined(__linux) || defined(CPU_ZERO) || defined(VL_CPPCHECK)  // Linux-like pthreads
-    if (contextp && !contextp->useNumaAssign()) { return "NUMA assignment not requested"; }
-    std::string numa_strategy = VlOs::getenvStr("VERILATOR_NUMA_STRATEGY", "default");
-    if (numa_strategy == "none") {
-        return "no NUMA assignment requested";
-    } else if (numa_strategy != "default" && numa_strategy != "") {
-        return "%Warning: unknown VERILATOR_NUMA_STRATEGY value '" + numa_strategy + "'";
-    }
-    // Get number of processor available to the current process
-    const unsigned num_proc = VlOs::getProcessAvailableParallelism();
-    if (!num_proc) return "Can't determine number of available threads";
-    // If fewer than hardware threads in the host, user presumably set affinity
-    if (num_proc < std::thread::hardware_concurrency()) return "processor affinity already set";
-
-    // Make a reasonable processor affinity selection
-    const int num_threads = static_cast(m_workers.size());
-    if (num_threads < 2) return "too few threads";
-    if (static_cast(num_threads) > num_proc) return "too many threads";
-
-    // Read CPU info.
-    // Uncertain if any modern system has gaps in the processor id (Solaris
-    // did), but just in case use vectors instead of processor number math.
-    //
-    // Currently ignoring socket number "physical id".
-    // If processor numbers are sequential on sockets, algorithm works out ok.
-    // If processor numbers are strided on sockets, algorithm also works out ok.
-    std::ifstream is{"/proc/cpuinfo"};
-    if (VL_UNLIKELY(!is)) return "%Warning: no /proc/cpuinfo";
-
-    std::vector unassigned_processors;  // Processors to assign in sorted order
-    std::map processor_core;
-    std::multimap core_processors;
-    std::set cores;
-    {
-        int processor = -1;
-        while (!is.eof()) {
-            std::string line;
-            std::getline(is, line);
-            std::string::size_type pos = line.find(":");
-            int number = -1;
-            if (pos != std::string::npos) number = atoi(line.c_str() + pos + 1);
-            if (line.compare(0, std::strlen("processor"), "processor") == 0) {
-                processor = number;
-            } else if (line.compare(0, std::strlen("core id"), "core id") == 0) {
-                const int core = number;
-                // std::cout << "p" << processor << " socket " << socket << " c" << core <<
-                // std::endl;
-                cores.emplace(core);
-                processor_core[processor] = core;
-                core_processors.emplace(core, processor);
-                unassigned_processors.push_back(processor);
-            }
-        }
-    }
-
-    // Start scheduling on the current CPU + 1.
-    // This will help to land on the same socket as current CPU, and also
-    // help make sure that different processes have different masks (when
-    // num_threads is not a common-factor of the processor count).
-    std::sort(unassigned_processors.begin(), unassigned_processors.end());
-    {
-        const int on_cpu = sched_getcpu();  // TODO: this is a system call. Not exactly cheap.
-        bool hit = false;
-        std::vector new_front;
-        std::vector new_back;
-        for (const int processor : unassigned_processors) {
-            if (hit) {
-                new_front.push_back(processor);
-            } else {
-                new_back.push_back(processor);
-            }
-            if (processor == on_cpu) hit = true;
-        }
-        unassigned_processors = new_front;
-        unassigned_processors.insert(unassigned_processors.end(), new_back.begin(),
-                                     new_back.end());
-    }
-
-    // If less threads than cores, we can schedule per-core
-    const bool core_per_thread = num_threads <= cores.size();
-
-    // Compute core mapping
-    std::multimap thread_processors;
-    {
-        std::set assigned_processors;
-        int thread = 0;
-        for (const int processor : unassigned_processors) {
-            // Find free processor, the current thread can use that
-            if (assigned_processors.find(processor) != assigned_processors.end()) continue;
-            assigned_processors.emplace(processor);
-            thread_processors.emplace(thread, processor);
-            if (core_per_thread) {
-                // Also include all other processors same core,
-                // so that another thread doesn't land on different processor in same core
-                const int core = processor_core[processor];
-                const auto bounds = core_processors.equal_range(core);
-                for (auto it{bounds.first}; it != bounds.second; ++it) {
-                    if (assigned_processors.find(it->second) != assigned_processors.end())
-                        continue;
-                    if (it->second == processor) continue;
-                    thread_processors.emplace(thread, it->second);
-                    assigned_processors.emplace(it->second);
-                }
-            }
-            // Prepare for next loop
-            thread = (thread + 1) % num_threads;
-        }
-    }
-
-    // Set affinity
-    std::string status = "assigned ";
-    for (int thread = 0; thread < num_threads; ++thread) {
-        cpu_set_t cpuset;
-        CPU_ZERO(&cpuset);
-
-        const auto bounds = thread_processors.equal_range(thread);
-        for (auto it{bounds.first}; it != bounds.second; ++it) {
-            if (it != bounds.first) status += ',';
-            status += std::to_string(it->second);
-            CPU_SET(it->second, &cpuset);
-        }
-        status += ";";
-
-        const int rc = pthread_setaffinity_np(m_workers[thread]->m_cthread.native_handle(),
-                                              sizeof(cpu_set_t), &cpuset);
-        if (rc != 0) return "%Warning: pthread_setaffinity_np failed";
-    }
-    // std::cout << "Status: " << status << std::endl;
-    return status;
-#else
-    return "non-supported host OS";
-#endif
-}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.h
deleted file mode 100644
index 9ac23392bf0..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_threads.h
+++ /dev/null
@@ -1,260 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2012-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated thread pool and profiling header
-///
-/// This file is not part of the Verilated public-facing API.
-/// It is only for internal use by Verilated library multithreaded
-/// routines.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_THREADS_H_
-#define VERILATOR_VERILATED_THREADS_H_
-
-#include "verilatedos.h"
-
-#include "verilated.h"  // for VerilatedMutex and clang annotations
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-// Use pthreads directly on macOS (could do this on Linux too if needing APIs unavailable via C++)
-#if defined(_POSIX_THREADS) && defined(__APPLE__)
-#define VL_USE_PTHREADS
-#endif
-
-#ifdef VL_USE_PTHREADS
-#include 
-#endif
-
-class VlExecutionProfiler;
-class VlThreadPool;
-
-// VlMTaskVertex and VlThreadpool will work with multiple model class types.
-// Since the type is opaque to VlMTaskVertex and VlThreadPool, represent it
-// as a void* here.
-using VlSelfP = void*;
-
-using VlExecFnp = void (*)(VlSelfP, bool);
-
-// Track dependencies for a single MTask.
-class VlMTaskVertex final {
-    // MEMBERS
-    static std::atomic s_yields;  // Statistics
-
-    // On even cycles, _upstreamDepsDone increases as upstream
-    // dependencies complete. When it reaches _upstreamDepCount,
-    // this MTaskVertex is ready.
-    //
-    // On odd cycles, _upstreamDepsDone decreases as upstream
-    // dependencies complete, and when it reaches zero this MTaskVertex
-    // is ready.
-    //
-    // An atomic is smaller than a mutex, and lock-free.
-    //
-    // (Why does the size of this class matter? If an mtask has many
-    // downstream mtasks to notify, we hope these will pack into a
-    // small number of cache lines to reduce the cost of pointer chasing
-    // during done-notification. Nobody's quantified that cost though.
-    // If we were really serious about shrinking this class, we could
-    // use 16-bit types here...)
-    std::atomic m_upstreamDepsDone;
-    const uint32_t m_upstreamDepCount;
-
-public:
-    // CONSTRUCTORS
-
-    // 'upstreamDepCount' is the number of upstream MTaskVertex's
-    // that must notify this MTaskVertex before it will become ready
-    // to run.
-    explicit VlMTaskVertex(uint32_t upstreamDepCount);
-    ~VlMTaskVertex() = default;
-
-    static uint64_t yields() { return s_yields; }
-    static void yieldThread() {
-        ++s_yields;  // Statistics
-        std::this_thread::yield();
-    }
-
-    // Upstream mtasks must call this when they complete.
-    // Returns true when the current MTaskVertex becomes ready to execute,
-    // false while it's still waiting on more dependencies.
-    bool signalUpstreamDone(bool evenCycle) {
-        if (evenCycle) {
-            const uint32_t upstreamDepsDone
-                = 1 + m_upstreamDepsDone.fetch_add(1, std::memory_order_release);
-            assert(upstreamDepsDone <= m_upstreamDepCount);
-            return (upstreamDepsDone == m_upstreamDepCount);
-        } else {
-            const uint32_t upstreamDepsDone_prev
-                = m_upstreamDepsDone.fetch_sub(1, std::memory_order_release);
-            assert(upstreamDepsDone_prev > 0);
-            return (upstreamDepsDone_prev == 1);
-        }
-    }
-    bool areUpstreamDepsDone(bool evenCycle) const {
-        const uint32_t target = evenCycle ? m_upstreamDepCount : 0;
-        return m_upstreamDepsDone.load(std::memory_order_acquire) == target;
-    }
-    void waitUntilUpstreamDone(bool evenCycle) const {
-        unsigned ct = 0;
-        while (VL_UNLIKELY(!areUpstreamDepsDone(evenCycle))) {
-            VL_CPU_RELAX();
-            ++ct;
-            if (VL_UNLIKELY(ct > VL_LOCK_SPINS)) {
-                ct = 0;
-                yieldThread();
-            }
-        }
-    }
-};
-
-class VlWorkerThread final {
-    friend class VlThreadPool;
-
-    // TYPES
-    struct ExecRec final {
-        VlExecFnp m_fnp = nullptr;  // Function to execute
-        VlSelfP m_selfp = nullptr;  // Symbol table to execute
-        bool m_evenCycle = false;  // Even/odd for flag alternation
-        ExecRec() = default;
-        ExecRec(VlExecFnp fnp, VlSelfP selfp, bool evenCycle)
-            : m_fnp{fnp}
-            , m_selfp{selfp}
-            , m_evenCycle{evenCycle} {}
-    };
-
-    // MEMBERS
-    mutable VerilatedMutex m_mutex;
-    std::condition_variable_any m_cv;
-    // Only notify the condition_variable if the worker is waiting
-    bool m_waiting VL_GUARDED_BY(m_mutex) = false;
-
-    // Why a vector? We expect the pending list to be very short, typically
-    // 0 or 1 or 2, so popping from the front shouldn't be
-    // expensive. Revisit if we ever have longer queues...
-    std::vector m_ready VL_GUARDED_BY(m_mutex);
-    // Store the size atomically, so we can spin wait
-    std::atomic m_ready_size;
-    // Thread context
-    VerilatedContext* const m_contextp;
-    // Underlying thread record
-#ifdef VL_USE_PTHREADS
-    pthread_t m_pthread{};
-#else
-    std::thread m_cthread{};
-#endif
-
-    // METHDOS
-    static void* start(void*);  // Static entry point, invokes 'main'
-    void main();  // 'main' loop of thread
-
-    VL_UNCOPYABLE(VlWorkerThread);
-
-public:
-    // CONSTRUCTORS
-    explicit VlWorkerThread(VerilatedContext* contextp);
-    ~VlWorkerThread();
-
-    // METHODS
-    template 
-    void dequeWork(ExecRec* workp) VL_MT_SAFE_EXCLUDES(m_mutex) {
-        // Spin for a while, waiting for new data
-        if VL_CONSTEXPR_CXX17 (N_SpinWait) {
-            for (unsigned i = 0; i < VL_LOCK_SPINS; ++i) {
-                if (VL_LIKELY(m_ready_size.load(std::memory_order_relaxed))) break;
-                VL_CPU_RELAX();
-            }
-        }
-        VerilatedLockGuard lock{m_mutex};
-        while (m_ready.empty()) {
-            m_waiting = true;
-            m_cv.wait(m_mutex);
-        }
-        m_waiting = false;
-        // As noted above this is inefficient if our ready list is ever
-        // long (but it shouldn't be)
-        *workp = m_ready.front();
-        m_ready.erase(m_ready.begin());
-        m_ready_size.fetch_sub(1, std::memory_order_relaxed);
-    }
-    void addTask(VlExecFnp fnp, VlSelfP selfp, bool evenCycle = false)
-        VL_MT_SAFE_EXCLUDES(m_mutex) {
-        bool notify;
-        {
-            const VerilatedLockGuard lock{m_mutex};
-            m_ready.emplace_back(fnp, selfp, evenCycle);
-            m_ready_size.fetch_add(1, std::memory_order_relaxed);
-            notify = m_waiting;
-        }
-        if (notify) m_cv.notify_one();
-    }
-
-    void shutdown();  // Finish current tasks, then terminate thread
-    void wait();  // Blocks calling thread until all tasks complete in this thread
-};
-
-class VlThreadPool final : public VerilatedVirtualBase {
-    // MEMBERS
-    std::vector m_workers;  // our workers
-
-    mutable VerilatedMutex m_mutex;  // Guards indexes of unassigned workers
-    // Indexes of unassigned workers
-    std::stack m_unassignedWorkers VL_GUARDED_BY(m_mutex);
-    // For sequentially generating task IDs to avoid shadowing
-    std::atomic m_assignedTasks{0};
-    std::string m_numaStatus;  // Status of NUMA assignment
-
-public:
-    // CONSTRUCTORS
-    // Construct a thread pool with 'nThreads' dedicated threads. The thread
-    // pool will create these threads and make them available to execute tasks
-    // via this->workerp(index)->addTask(...)
-    VlThreadPool(VerilatedContext* contextp, unsigned nThreads);
-    ~VlThreadPool() override;
-
-    // METHODS
-    size_t assignWorkerIndex() {
-        const VerilatedLockGuard lock{m_mutex};
-        assert(!m_unassignedWorkers.empty());
-        const size_t index = m_unassignedWorkers.top();
-        m_unassignedWorkers.pop();
-        return index;
-    }
-    void freeWorkerIndexes(std::vector& indexes) {
-        const VerilatedLockGuard lock{m_mutex};
-        for (size_t index : indexes) m_unassignedWorkers.push(index);
-        indexes.clear();
-    }
-    unsigned assignTaskIndex() { return m_assignedTasks++; }
-    int numThreads() const { return static_cast(m_workers.size()); }
-    std::string numaStatus() const { return m_numaStatus; }
-    VlWorkerThread* workerp(int index) {
-        assert(index >= 0);
-        assert(index < static_cast(m_workers.size()));
-        return m_workers[index];
-    }
-
-private:
-    VL_UNCOPYABLE(VlThreadPool);
-
-    std::string numaAssign(VerilatedContext* contextp);
-};
-
-#endif
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.cpp
deleted file mode 100644
index 51dd88b9a0a..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.cpp
+++ /dev/null
@@ -1,271 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=========================================================================
-///
-/// \file
-/// \brief Verilated timing implementation code
-///
-/// This file must be compiled and linked against all Verilated objects
-/// that use timing features.
-///
-/// See the internals documentation docs/internals.rst for details.
-///
-//=========================================================================
-
-#include "verilated_timing.h"
-
-//======================================================================
-// VlCoroutineHandle:: Methods
-
-void VlCoroutineHandle::resume() {
-    // Only null if we have a fork..join_any and one of the other child processes resumed the
-    // main process
-    if (VL_LIKELY(m_coro)) {
-        VL_DEBUG_IF(VL_DBG_MSGF("             Resuming: "); dump(););
-        if (m_process) {  // If process state is managed with std::process
-            if (m_process->state() == VlProcess::KILLED) {
-                m_coro.destroy();
-            } else {
-                m_process->state(VlProcess::RUNNING);
-                m_coro();
-            }
-        } else {
-            m_coro();
-        }
-        m_coro = nullptr;
-    }
-}
-
-#ifdef VL_DEBUG
-void VlCoroutineHandle::dump() const {
-    VL_PRINTF("Process waiting at %s:%d\n", m_fileline.filename(), m_fileline.lineno());
-}
-#endif
-
-//======================================================================
-// VlDelayScheduler:: Methods
-
-void VlDelayScheduler::resume() {
-#ifdef VL_DEBUG
-    VL_DEBUG_IF(dump(); VL_DBG_MSGF("         Resuming delayed processes\n"););
-#endif
-    bool resumed = false;
-
-    while (!m_queue.empty() && (m_queue.cbegin()->first == m_context.time())) {
-        VlCoroutineHandle handle = std::move(m_queue.begin()->second);
-        m_queue.erase(m_queue.begin());
-        handle.resume();
-        resumed = true;
-    }
-
-    if (!resumed) {
-        if (m_context.time() == 0) {
-            // Nothing was scheduled at time 0, but resume() got called due to --x-initial-edge
-            return;
-        }
-
-        VL_FATAL_MT(__FILE__, __LINE__, "",
-                    "%Error: Encountered process that should've been resumed at an "
-                    "earlier simulation time. Missed a time slot?\n");
-    }
-}
-
-void VlDelayScheduler::resumeZeroDelay() {
-    m_zeroDelayesSwap.swap(m_zeroDelayed);
-    for (VlCoroutineHandle& handle : m_zeroDelayesSwap) handle.resume();
-    m_zeroDelayesSwap.clear();
-}
-
-uint64_t VlDelayScheduler::nextTimeSlot() const {
-    if (!m_queue.empty()) return m_queue.cbegin()->first;
-    if (m_zeroDelayed.empty())
-        VL_FATAL_MT(__FILE__, __LINE__, "", "There is no next time slot scheduled");
-    return m_context.time();
-}
-
-#ifdef VL_DEBUG
-void VlDelayScheduler::dump() const {
-    if (m_queue.empty() && m_zeroDelayed.empty()) {
-        VL_DBG_MSGF("         No delayed processes:\n");
-    } else {
-        VL_DBG_MSGF("         Delayed processes:\n");
-        for (const auto& susp : m_zeroDelayed) {
-            VL_DBG_MSGF("             Awaiting #0-delayed resumption, "
-                        "time () %" PRIu64 ": ",
-                        m_context.time());
-            susp.dump();
-        }
-        for (const auto& susp : m_queue) {
-            VL_DBG_MSGF("             Awaiting time %" PRIu64 ": ", susp.first);
-            susp.second.dump();
-        }
-    }
-}
-#endif
-
-//======================================================================
-// VlTriggerScheduler:: Methods
-
-void VlTriggerScheduler::resume(const char* eventDescription) {
-#ifdef VL_DEBUG
-    VL_DEBUG_IF(dump(eventDescription);
-                VL_DBG_MSGF("         Resuming processes waiting for %s\n", eventDescription););
-#endif
-    for (VlCoroutineHandle& coro : m_toResume) coro.resume();
-    m_toResume.clear();
-}
-
-void VlTriggerScheduler::moveToResumeQueue(const char* eventDescription) {
-#ifdef VL_DEBUG
-    if (!m_fired.empty()) {
-        VL_DEBUG_IF(VL_DBG_MSGF("         Moving to resume queue processes waiting for %s:\n",
-                                eventDescription);
-                    for (const auto& susp
-                         : m_fired) {
-                        VL_DBG_MSGF("           - ");
-                        susp.dump();
-                    });
-    }
-#endif
-    std::swap(m_fired, m_toResume);
-}
-
-void VlTriggerScheduler::ready(const char* eventDescription) {
-#ifdef VL_DEBUG
-    if (!m_awaiting.empty()) {
-        VL_DEBUG_IF(
-            VL_DBG_MSGF("         Committing processes waiting for %s:\n", eventDescription);
-            for (const auto& susp
-                 : m_awaiting) {
-                VL_DBG_MSGF("           - ");
-                susp.dump();
-            });
-    }
-#endif
-    const size_t expectedSize = m_fired.size() + m_awaiting.size();
-    if (m_fired.capacity() < expectedSize) m_fired.reserve(expectedSize * 2);
-    m_fired.insert(m_fired.end(), std::make_move_iterator(m_awaiting.begin()),
-                   std::make_move_iterator(m_awaiting.end()));
-    m_awaiting.clear();
-}
-
-#ifdef VL_DEBUG
-void VlTriggerScheduler::dump(const char* eventDescription) const {
-    if (m_toResume.empty()) {
-        VL_DBG_MSGF("         No process to resume waiting for %s\n", eventDescription);
-    } else {
-        for (const auto& susp : m_toResume) {
-            VL_DBG_MSGF("         Processes to resume waiting for %s:\n", eventDescription);
-            VL_DBG_MSGF("           - ");
-            susp.dump();
-        }
-    }
-    if (!m_fired.empty()) {
-        VL_DBG_MSGF("         Triggered processes waiting for %s:\n", eventDescription);
-        for (const auto& susp : m_awaiting) {
-            VL_DBG_MSGF("           - ");
-            susp.dump();
-        }
-    }
-    if (!m_awaiting.empty()) {
-        VL_DBG_MSGF("         Not triggered processes waiting for %s:\n", eventDescription);
-        for (const auto& susp : m_awaiting) {
-            VL_DBG_MSGF("           - ");
-            susp.dump();
-        }
-    }
-}
-#endif
-
-//======================================================================
-// VlDynamicTriggerScheduler:: Methods
-
-bool VlDynamicTriggerScheduler::evaluate() {
-    m_anyTriggered = false;
-    VL_DEBUG_IF(dump(););
-    std::swap(m_suspended, m_evaluated);
-    for (auto& coro : m_evaluated) coro.resume();
-    m_evaluated.clear();
-    return m_anyTriggered;
-}
-
-void VlDynamicTriggerScheduler::doPostUpdates() {
-    VL_DEBUG_IF(if (!m_post.empty())
-                    VL_DBG_MSGF("         Doing post updates for processes:\n");  //
-                for (const auto& susp
-                     : m_post) {
-                    VL_DBG_MSGF("           - ");
-                    susp.dump();
-                });
-    for (auto& coro : m_post) coro.resume();
-    m_post.clear();
-}
-
-void VlDynamicTriggerScheduler::resume() {
-    VL_DEBUG_IF(if (!m_triggered.empty()) VL_DBG_MSGF("         Resuming processes:\n");  //
-                for (const auto& susp
-                     : m_triggered) {
-                    VL_DBG_MSGF("           - ");
-                    susp.dump();
-                });
-    for (auto& coro : m_triggered) coro.resume();
-    m_triggered.clear();
-}
-
-#ifdef VL_DEBUG
-void VlDynamicTriggerScheduler::dump() const {
-    if (m_suspended.empty()) {
-        VL_DBG_MSGF("         No suspended processes waiting for dynamic trigger evaluation\n");
-    } else {
-        for (const auto& susp : m_suspended) {
-            VL_DBG_MSGF("         Suspended processes waiting for dynamic trigger evaluation:\n");
-            VL_DBG_MSGF("           - ");
-            susp.dump();
-        }
-    }
-}
-#endif
-
-//======================================================================
-// VlForkSync:: Methods
-
-void VlForkSync::done(const char* filename, int lineno) {
-    VL_DEBUG_IF(VL_DBG_MSGF("             Process forked at %s:%d finished\n", filename, lineno););
-    if (m_join->m_counter > 0) m_join->m_counter--;
-    if (m_join->m_counter == 0) m_join->m_susp.resume();
-}
-
-//======================================================================
-// VlCoroutine:: Methods
-
-VlCoroutine::VlPromise::~VlPromise() {
-    // Indicate to the return object that the coroutine has finished or been destroyed
-    if (m_corop) m_corop->m_promisep = nullptr;
-    // If there is a continuation, destroy it
-    if (m_continuation) m_continuation.destroy();
-}
-
-std::suspend_never VlCoroutine::VlPromise::final_suspend() noexcept {
-    // Indicate to the return object that the coroutine has finished
-    if (m_corop) {
-        m_corop->m_promisep = nullptr;
-        // Forget the return value, we won't need it and it won't be able to let us know if
-        // it's destroyed
-        m_corop = nullptr;
-    }
-    // If there is a continuation, resume it
-    if (m_continuation) {
-        m_continuation();
-        m_continuation = nullptr;
-    }
-    return {};
-}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.h
deleted file mode 100644
index a657ac45886..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_timing.h
+++ /dev/null
@@ -1,482 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilated timing header
-///
-/// This file is included automatically by Verilator in some of the C++ files
-/// it generates if timing features are used.
-///
-/// This file is not part of the Verilated public-facing API.
-/// It is only for internal use.
-///
-/// See the internals documentation docs/internals.rst for details.
-///
-//*************************************************************************
-#ifndef VERILATOR_VERILATED_TIMING_H_
-#define VERILATOR_VERILATED_TIMING_H_
-
-#include "verilated.h"
-
-#include 
-#include 
-
-// clang-format off
-// Some preprocessor magic to support both Clang and GCC coroutines with both libc++ and libstdc++
-#if defined _LIBCPP_VERSION  // libc++
-# if defined(__has_include) && !__has_include() && __has_include()
-#  if __clang_major__ > 13  // Clang > 13 warns that coroutine types in std::experimental are deprecated
-#   pragma clang diagnostic push
-#   pragma clang diagnostic ignored "-Wdeprecated-experimental-coroutine"
-#  endif
-#  include 
-   namespace std {
-       using namespace experimental;  // Bring std::experimental into the std namespace
-   }
-# else
-#  include 
-# endif
-#else
-# if defined __clang__ && defined __GLIBCXX__ && !defined __cpp_impl_coroutine
-#  define __cpp_impl_coroutine 1  // Clang doesn't define this, but it's needed for libstdc++
-# endif
-# include 
-# if __clang_major__ < 14
-   namespace std {  // Bring coroutine library into std::experimental, as Clang < 14 expects it to be there
-       namespace experimental {
-           using namespace std;
-       }
-   }
-# endif
-#endif
-// clang-format on
-
-// Placeholder for compiling with --protect-ids
-#define VL_UNKNOWN ""
-
-//=============================================================================
-// VlFileLineDebug stores a SystemVerilog source code location. Used in VlCoroutineHandle for
-// debugging purposes.
-
-class VlFileLineDebug final {
-    // MEMBERS
-#ifdef VL_DEBUG
-    const char* m_filename = nullptr;
-    int m_lineno = 0;
-#endif
-
-public:
-    // CONSTRUCTORS
-    // Construct
-    VlFileLineDebug() = default;
-    VlFileLineDebug(const char* filename, int lineno)
-#ifdef VL_DEBUG
-        : m_filename{filename}
-        , m_lineno{lineno}
-#endif
-    {
-    }
-
-    // METHODS
-#ifdef VL_DEBUG
-    const char* filename() const { return m_filename; }
-    int lineno() const { return m_lineno; }
-#endif
-};
-
-//=============================================================================
-// VlCoroutineHandle is a non-copyable (but movable) coroutine handle. On resume, the handle is
-// cleared, as we assume that either the coroutine has finished and deleted itself, or, if it got
-// suspended, another VlCoroutineHandle was created to manage it.
-
-class VlCoroutineHandle final {
-    VL_UNCOPYABLE(VlCoroutineHandle);
-
-    // MEMBERS
-    std::coroutine_handle<> m_coro;  // The wrapped coroutine handle
-    VlProcessRef m_process;  // Data of the suspended process, null if not needed
-    VlFileLineDebug m_fileline;
-
-public:
-    // CONSTRUCTORS
-    // Construct
-    // non-explicit:
-    // cppcheck-suppress noExplicitConstructor
-    VlCoroutineHandle(VlProcessRef process)
-        : m_coro{nullptr}
-        , m_process{process} {
-        if (m_process) m_process->state(VlProcess::WAITING);
-    }
-    VlCoroutineHandle(std::coroutine_handle<> coro, VlProcessRef process, VlFileLineDebug fileline)
-        : m_coro{coro}
-        , m_process{process}
-        , m_fileline{fileline} {
-        if (m_process) m_process->state(VlProcess::WAITING);
-    }
-    // Move the handle, leaving a nullptr
-    // non-explicit:
-    // cppcheck-suppress noExplicitConstructor
-    VlCoroutineHandle(VlCoroutineHandle&& moved)
-        : m_coro{std::exchange(moved.m_coro, nullptr)}
-        , m_process{std::exchange(moved.m_process, nullptr)}
-        , m_fileline{moved.m_fileline} {}
-    // Destroy if the handle isn't null
-    ~VlCoroutineHandle() {
-        // Usually these coroutines should get resumed; we only need to clean up if we destroy a
-        // model with some coroutines suspended
-        if (VL_UNLIKELY(m_coro)) {
-            m_coro.destroy();
-            if (m_process && m_process->state() != VlProcess::KILLED) {
-                m_process->state(VlProcess::FINISHED);
-            }
-        }
-    }
-    // METHODS
-    // Move the handle, leaving a null handle
-    auto& operator=(VlCoroutineHandle&& moved) {
-        m_coro = std::exchange(moved.m_coro, nullptr);
-        m_process = std::exchange(moved.m_process, nullptr);
-        m_fileline = moved.m_fileline;
-        return *this;
-    }
-    // Resume the coroutine if the handle isn't null and the process isn't killed
-    void resume();
-#ifdef VL_DEBUG
-    void dump() const;
-#endif
-};
-
-enum class VlDelayPhase : bool { ACTIVE, INACTIVE };
-
-//=============================================================================
-// VlDelayScheduler stores coroutines to be resumed at a certain simulation time. If the current
-// time is equal to a coroutine's resume time, the coroutine gets resumed.
-
-class VlDelayScheduler final {
-    // TYPES
-    // Time-sorted queue of timestamps and handles
-    using VlDelayedCoroutineQueue = std::multimap;
-
-    // MEMBERS
-    VerilatedContext& m_context;
-    VlDelayedCoroutineQueue m_queue;  // Coroutines to be restored at a certain simulation time
-    std::vector m_zeroDelayed;  // Coroutines waiting for #0
-    // Coroutines that waited for #0 and are being resumed now. As member to avoid reallocations
-    std::vector m_zeroDelayesSwap;
-
-public:
-    // CONSTRUCTORS
-    explicit VlDelayScheduler(VerilatedContext& context)
-        : m_context{context} {}
-    // METHODS
-    // Resume coroutines waiting for the current simulation time
-    void resume();
-    // Resume coroutines waiting for #0
-    void resumeZeroDelay();
-    // Returns the simulation time of the next time slot (aborts if there are no delayed
-    // coroutines)
-    uint64_t nextTimeSlot() const;
-    // Are there no delayed coroutines awaiting?
-    bool empty() const { return m_queue.empty() && m_zeroDelayed.empty(); }
-    // Are there coroutines to resume at the current simulation time?
-    bool awaitingCurrentTime() const {
-        return (!m_queue.empty() && (m_queue.cbegin()->first <= m_context.time()));
-    }
-    // Are there coroutines to resume in the inactive region after a #0 delay?
-    bool awaitingZeroDelay() const { return !m_zeroDelayed.empty(); }
-#ifdef VL_DEBUG
-    void dump() const;
-#endif
-    // Used by coroutines for co_awaiting a certain simulation time
-    auto delay(uint64_t delay, VlProcessRef process, const char* filename = VL_UNKNOWN,
-               int lineno = 0) {
-        struct Awaitable final {
-            VlProcessRef process;  // Data of the suspended process, null if not needed
-            VlDelayedCoroutineQueue& queue;
-            std::vector& queueZeroDelay;
-            const uint64_t delay;
-            const VlDelayPhase phase;
-            const VlFileLineDebug fileline;
-
-            bool await_ready() const { return false; }  // Always suspend
-            void await_suspend(std::coroutine_handle<> coro) {
-                // Both active delays and fork..join_none #0 are resumed out of the time queue.
-                if (phase != VlDelayPhase::INACTIVE) {
-                    queue.emplace(delay, VlCoroutineHandle{coro, process, fileline});
-                } else {
-                    queueZeroDelay.emplace_back(VlCoroutineHandle{coro, process, fileline});
-                }
-            }
-            void await_resume() const {}
-        };
-
-        VlDelayPhase phase;
-        if (delay != 0) {
-            // UINT64_MAX is a sentinel for synthetic fork..join_none delays.
-            if (delay == std::numeric_limits::max()) delay = 0;
-            phase = VlDelayPhase::ACTIVE;
-        } else {
-            phase = VlDelayPhase::INACTIVE;
-        }
-        return Awaitable{process,       m_queue,
-                         m_zeroDelayed, m_context.time() + delay,
-                         phase,         VlFileLineDebug{filename, lineno}};
-    }
-};
-
-//=============================================================================
-// VlTriggerScheduler stores coroutines to be resumed by a trigger. It does not keep track of its
-// trigger, relying on calling code to resume when appropriate. Coroutines are kept in three stages
-// - 'awaiting', 'fired' and 'toResume'. Whenever a coroutine is suspended, it lands in the
-// 'awaiting' stage. Only when ready() is called, these coroutines get moved to the 'fired' stage.
-// When moveToResumeQueue() is begin called all coroutines from 'ready' are moved to 'toResume'.
-// That's when they can be resumed. This is done to avoid resuming processes before they start
-// waiting.
-
-class VlTriggerScheduler final {
-    // TYPES
-    using VlCoroutineVec = std::vector;
-
-    // MEMBERS
-    VlCoroutineVec m_awaiting;  // Coroutines suspended before ready() was called
-                                // (not resumable)
-    VlCoroutineVec m_fired;  // Coroutines that were triggered (all coros from m_awaiting are moved
-                             // here in ready())
-    VlCoroutineVec m_toResume;  // Coroutines to resume in next resumePrep()
-                                // - moved here in commit()
-
-public:
-    // METHODS
-    // Resumes all coroutines from the m_toResume
-    void resume(const char* eventDescription = VL_UNKNOWN);
-    // Moves all coroutines from m_fired to m_toResume
-    void moveToResumeQueue(const char* eventDescription = VL_UNKNOWN);
-    // Moves all coroutines from m_awaiting to m_fired
-    void ready(const char* eventDescription = VL_UNKNOWN);
-    // Are there no coroutines awaiting?
-    bool empty() const { return m_fired.empty() && m_awaiting.empty(); }
-#ifdef VL_DEBUG
-    void dump(const char* eventDescription) const;
-#endif
-    // Used by coroutines for co_awaiting a certain trigger
-    auto trigger(bool ready, VlProcessRef process, const char* eventDescription = VL_UNKNOWN,
-                 const char* filename = VL_UNKNOWN, int lineno = 0) {
-        VL_DEBUG_IF(VL_DBG_MSGF("         Suspending process waiting for %s at %s:%d\n",
-                                eventDescription, filename, lineno););
-        struct Awaitable final {
-            VlCoroutineVec& suspended;  // Coros waiting on trigger
-            VlProcessRef process;  // Data of the suspended process, null if not needed
-            VlFileLineDebug fileline;
-
-            bool await_ready() const { return false; }  // Always suspend
-            void await_suspend(std::coroutine_handle<> coro) {
-                suspended.emplace_back(coro, process, fileline);
-            }
-            void await_resume() const {}
-        };
-        return Awaitable{ready ? m_fired : m_awaiting, process, VlFileLineDebug{filename, lineno}};
-    }
-};
-
-//=============================================================================
-// VlDynamicTriggerScheduler is used for cases where triggers cannot be statically referenced and
-// evaluated. Coroutines that make use of this scheduler must adhere to a certain procedure:
-//     __Vtrigger = 0;
-//     
-//     while (!__Vtrigger) {
-//         co_await __VdynSched.evaluation();
-//         
;
-//         __Vtrigger = ;
-//         __VdynShed.anyTriggered(__Vtrigger);
-//         [optionally] co_await __VdynSched.postUpdate();
-//         ;
-//     }
-//    co_await __VdynSched.resumption();
-// The coroutines get resumed at trigger evaluation time, evaluate their local triggers, optionally
-// await the post update step, and if the trigger is set, await proper resumption in the 'act' eval
-// step.
-
-class VlDynamicTriggerScheduler final {
-    // TYPES
-    using VlCoroutineVec = std::vector;
-
-    // MEMBERS
-    bool m_anyTriggered = false;  // If true, at least one trigger was set
-    VlCoroutineVec m_suspended;  // Suspended coroutines awaiting trigger evaluation
-    VlCoroutineVec m_evaluated;  // Coroutines currently being evaluated (for evaluate())
-    VlCoroutineVec m_triggered;  // Coroutines whose triggers were set, and are awaiting resumption
-    VlCoroutineVec m_post;  // Coroutines awaiting the post update step (only relevant for triggers
-                            // with destructive post updates, e.g. named events)
-
-    // METHODS
-    auto awaitable(VlProcessRef process, VlCoroutineVec& queue, const char* filename, int lineno) {
-        struct Awaitable final {
-            VlProcessRef process;  // Data of the suspended process, null if not needed
-            VlCoroutineVec& suspended;  // Coros waiting on trigger
-            VlFileLineDebug fileline;
-
-            bool await_ready() const { return false; }  // Always suspend
-            void await_suspend(std::coroutine_handle<> coro) {
-                suspended.emplace_back(coro, process, fileline);
-            }
-            void await_resume() const {}
-        };
-        return Awaitable{process, queue, VlFileLineDebug{filename, lineno}};
-    }
-
-public:
-    // Evaluates all dynamic triggers (resumed coroutines that co_await evaluation())
-    bool evaluate();
-    // Called by coroutines that evaluate triggers to notify the scheduler if any triggers were set
-    void anyTriggered(bool triggered) { m_anyTriggered = m_anyTriggered || triggered; }
-    // Runs post updates for all dynamic triggers (resumes coroutines that co_await postUpdate())
-    void doPostUpdates();
-    // Resumes all coroutines whose triggers are set (those that co_await resumption())
-    void resume();
-#ifdef VL_DEBUG
-    void dump() const;
-#endif
-    // Used by coroutines for co_awaiting trigger evaluation
-    auto evaluation(VlProcessRef process, const char* eventDescription, const char* filename,
-                    int lineno) {
-        VL_DEBUG_IF(VL_DBG_MSGF("         Suspending process waiting for %s at %s:%d\n",
-                                eventDescription, filename, lineno););
-        return awaitable(process, m_suspended, filename, lineno);
-    }
-    // Used by coroutines for co_awaiting the trigger post update step
-    auto postUpdate(VlProcessRef process, const char* eventDescription, const char* filename,
-                    int lineno) {
-        VL_DEBUG_IF(
-            VL_DBG_MSGF("         Process waiting for %s at %s:%d awaiting the post update step\n",
-                        eventDescription, filename, lineno););
-        return awaitable(process, m_post, filename, lineno);
-    }
-    // Used by coroutines for co_awaiting the resumption step (in 'act' eval)
-    auto resumption(VlProcessRef process, const char* eventDescription, const char* filename,
-                    int lineno) {
-        VL_DEBUG_IF(VL_DBG_MSGF("         Process waiting for %s at %s:%d awaiting resumption\n",
-                                eventDescription, filename, lineno););
-        return awaitable(process, m_triggered, filename, lineno);
-    }
-};
-
-//=============================================================================
-// VlForever is a helper awaitable type for suspending coroutines forever. Used for constant
-// wait statements.
-
-struct VlForever final {
-    bool await_ready() const { return false; }  // Always suspend
-    void await_suspend(std::coroutine_handle<> coro) const { coro.destroy(); }
-    void await_resume() const {}
-};
-
-//=============================================================================
-// VlForkSync is used to manage fork..join and fork..join_any constructs.
-
-class VlForkSync final {
-    // VlJoin stores the handle of a suspended coroutine that did a fork..join or fork..join_any.
-    // If the counter reaches 0, the suspended coroutine shall be resumed.
-    struct VlJoin final {
-        size_t m_counter = 0;  // When reaches 0, resume suspended coroutine
-        VlCoroutineHandle m_susp;  // Coroutine to resume
-    };
-
-    // The join info is shared among all forked processes
-    std::shared_ptr m_join;
-
-public:
-    // Create the join object and set the counter to the specified number
-    void init(size_t count, VlProcessRef process) { m_join.reset(new VlJoin{count, {process}}); }
-    // Called whenever any of the forked processes finishes. If the join counter reaches 0, the
-    // main process gets resumed
-    void done(const char* filename = VL_UNKNOWN, int lineno = 0);
-    // Used by coroutines for co_awaiting a join
-    auto join(VlProcessRef process, const char* filename = VL_UNKNOWN, int lineno = 0) {
-        assert(m_join);
-        VL_DEBUG_IF(
-            VL_DBG_MSGF("             Awaiting join of fork at: %s:%d\n", filename, lineno););
-        struct Awaitable final {
-            VlProcessRef process;  // Data of the suspended process, null if not needed
-            const std::shared_ptr join;  // Join to await on
-            VlFileLineDebug fileline;
-
-            bool await_ready() { return join->m_counter == 0; }  // Suspend if join still exists
-            void await_suspend(std::coroutine_handle<> coro) {
-                join->m_susp = {coro, process, fileline};
-            }
-            void await_resume() const {}
-        };
-        return Awaitable{process, m_join, VlFileLineDebug{filename, lineno}};
-    }
-};
-
-//=============================================================================
-// VlCoroutine
-// Return value of a coroutine. Used for chaining coroutine suspension/resumption.
-
-class VlCoroutine final {
-private:
-    // TYPES
-    struct VlPromise final {
-        std::coroutine_handle<> m_continuation;  // Coroutine to resume after this one finishes
-        VlCoroutine* m_corop = nullptr;  // Pointer to the coroutine return object
-
-        ~VlPromise();
-
-        VlCoroutine get_return_object() { return {this}; }
-
-        // Never suspend at the start of the coroutine
-        std::suspend_never initial_suspend() const { return {}; }
-
-        // Never suspend at the end of the coroutine (thanks to this, the coroutine will clean up
-        // after itself)
-        std::suspend_never final_suspend() noexcept;
-
-        void unhandled_exception() const { std::abort(); }
-        void return_void() const {}
-    };
-
-    // MEMBERS
-    VlPromise* m_promisep;  // The promise created for this coroutine
-
-public:
-    // TYPES
-    using promise_type = VlPromise;  // promise_type has to be public
-
-    // CONSTRUCTORS
-    // Construct
-    // cppcheck-suppress noExplicitConstructor
-    VlCoroutine(VlPromise* promisep)
-        : m_promisep{promisep} {
-        m_promisep->m_corop = this;
-    }
-    // Move. Update the pointers each time the return object is moved
-    // cppcheck-suppress noExplicitConstructor
-    VlCoroutine(VlCoroutine&& other)
-        : m_promisep{std::exchange(other.m_promisep, nullptr)} {
-        if (m_promisep) m_promisep->m_corop = this;
-    }
-    ~VlCoroutine() {
-        // Indicate to the promise that the return object is gone
-        if (m_promisep) m_promisep->m_corop = nullptr;
-    }
-
-    // METHODS
-    // Suspend the awaiter if the coroutine is suspended (the promise exists)
-    bool await_ready() const noexcept { return !m_promisep; }
-    // Set the awaiting coroutine as the continuation of the current coroutine
-    void await_suspend(std::coroutine_handle<> coro) { m_promisep->m_continuation = coro; }
-    void await_resume() const noexcept {}
-};
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace.h
deleted file mode 100644
index 15e3cd9645d..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace.h
+++ /dev/null
@@ -1,648 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated internal common-tracing header
-///
-/// This file is not part of the Verilated public-facing API.
-/// It is only for internal use by Verilated tracing routines.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_TRACE_H_
-#define VERILATOR_VERILATED_TRACE_H_
-
-// clang-format off
-
-#include "verilated.h"
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-#include 
-#include 
-
-// clang-format on
-
-class VlThreadPool;
-template 
-class VerilatedTraceBuffer;
-template 
-class VerilatedTraceOffloadBuffer;
-
-//=============================================================================
-// Common enumerations
-
-enum class VerilatedTracePrefixType : uint8_t {
-    // Note: Entries must match VTracePrefixType (by name, not necessarily by value)
-    ARRAY_PACKED,
-    ARRAY_UNPACKED,
-    ROOTIO_WRAPPER,  // $rootio suppressed due to name()!=""
-    SCOPE_MODULE,
-    SCOPE_INTERFACE,
-    STRUCT_PACKED,
-    STRUCT_UNPACKED,
-    UNION_PACKED
-};
-
-// Direction attribute for ports
-enum class VerilatedTraceSigDirection : uint8_t {
-    NONE,
-    INPUT,
-    OUTPUT,
-    INOUT,
-};
-
-// Kind of signal. Similar to nettype but with a few more alternatives
-enum class VerilatedTraceSigKind : uint8_t {
-    PARAMETER,
-    SUPPLY0,
-    SUPPLY1,
-    TRI,
-    TRI0,
-    TRI1,
-    TRIAND,
-    TRIOR,
-    TRIREG,
-    WIRE,
-    VAR,
-};
-
-// Base data type of signal
-enum class VerilatedTraceSigType : uint8_t {
-    DOUBLE,
-    INTEGER,
-    BIT,
-    LOGIC,
-    INT,
-    SHORTINT,
-    LONGINT,
-    BYTE,
-    EVENT,
-    TIME,
-};
-
-//=============================================================================
-// Offloaded tracing
-
-// A simple synchronized first in first out queue
-template 
-class VerilatedThreadQueue final {  // LCOV_EXCL_LINE  // lcov bug
-private:
-    mutable VerilatedMutex m_mutex;  // Protects m_queue
-    std::condition_variable_any m_cv;
-    std::deque m_queue VL_GUARDED_BY(m_mutex);
-
-public:
-    // Put an element at the back of the queue
-    void put(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
-        const VerilatedLockGuard lock{m_mutex};
-        m_queue.push_back(value);
-        m_cv.notify_one();
-    }
-
-    // Put an element at the front of the queue
-    void put_front(T value) VL_MT_SAFE_EXCLUDES(m_mutex) {
-        const VerilatedLockGuard lock{m_mutex};
-        m_queue.push_front(value);
-        m_cv.notify_one();
-    }
-
-    // Get an element from the front of the queue. Blocks if none available
-    T get() VL_MT_SAFE_EXCLUDES(m_mutex) {
-        VerilatedLockGuard lock{m_mutex};
-        m_cv.wait(m_mutex, [this]() VL_REQUIRES(m_mutex) { return !m_queue.empty(); });
-        assert(!m_queue.empty());
-        T value = m_queue.front();
-        m_queue.pop_front();
-        return value;
-    }
-
-    // Non blocking get
-    bool tryGet(T& result) VL_MT_SAFE_EXCLUDES(m_mutex) {
-        const VerilatedLockGuard lockGuard{m_mutex};
-        if (m_queue.empty()) return false;
-        result = m_queue.front();
-        m_queue.pop_front();
-        return true;
-    }
-};
-
-// Commands used by thread tracing. Anonymous enum in class, as we want
-// it scoped, but we also want the automatic conversion to integer types.
-class VerilatedTraceOffloadCommand final {
-public:
-    // These must all fit in 4 bit at the moment, as the tracing routines
-    // pack parameters in the top bits.
-    enum : uint8_t {
-        CHG_BIT_0 = 0x0,
-        CHG_BIT_1 = 0x1,
-        CHG_CDATA = 0x2,
-        CHG_SDATA = 0x3,
-        CHG_IDATA = 0x4,
-        CHG_QDATA = 0x5,
-        CHG_WDATA = 0x6,
-        CHG_DOUBLE = 0x8,
-        CHG_EVENT = 0x9,
-        // TODO: full..
-        TIME_CHANGE = 0xc,
-        TRACE_BUFFER = 0xd,
-        END = 0xe,  // End of buffer
-        SHUTDOWN = 0xf  // Shutdown worker thread, also marks end of buffer
-    };
-};
-
-//=============================================================================
-// VerilatedTraceConfig
-
-// Simple data representing trace configuration required by generated models.
-class VerilatedTraceConfig final {
-public:
-    const bool m_useParallel;  // Use parallel tracing
-    const bool m_useOffloading;  // Offloading trace rendering
-    const bool m_useFstWriterThread;  // Use the separate FST writer thread
-
-    VerilatedTraceConfig(bool useParallel, bool useOffloading, bool useFstWriterThread)
-        : m_useParallel{useParallel}
-        , m_useOffloading{useOffloading}
-        , m_useFstWriterThread{useFstWriterThread} {}
-};
-
-//=============================================================================
-// VerilatedTraceBaseC - base class of all Verilated*C trace classes
-// Internal use only
-
-class VerilatedTraceBaseC VL_NOT_FINAL {
-    bool m_modelConnected = false;  // Model connected by calling Verilated::trace()
-public:
-    /// True if file currently open
-    virtual bool isOpen() const VL_MT_SAFE = 0;
-
-    // internal use only
-    bool modelConnected() const VL_MT_SAFE { return m_modelConnected; }
-    void modelConnected(bool flag) VL_MT_SAFE { m_modelConnected = flag; }
-};
-
-//=============================================================================
-// VerilatedTrace
-
-// T_Trace is the format-specific subclass of VerilatedTrace.
-// T_Buffer is the format-specific base class of VerilatedTraceBuffer.
-template 
-class VerilatedTrace VL_NOT_FINAL {
-public:
-    using Buffer = VerilatedTraceBuffer;
-    using OffloadBuffer = VerilatedTraceOffloadBuffer;
-
-    //=========================================================================
-    // Generic tracing internals
-
-    using initCb_t = void (*)(void*, T_Trace*, uint32_t);  // Type of init callbacks
-    using dumpCb_t = void (*)(void*, Buffer*);  // Type of dump callbacks
-    using dumpOffloadCb_t = void (*)(void*, OffloadBuffer*);  // Type of offload dump callbacks
-    using cleanupCb_t = void (*)(void*, T_Trace*);  // Type of cleanup callbacks
-
-private:
-    // Give the buffer (both base and derived) access to the private bits
-    friend T_Buffer;
-    friend Buffer;
-    friend OffloadBuffer;
-
-    struct CallbackRecord final {
-        union {  // The callback
-            const initCb_t m_initCb;
-            const dumpCb_t m_dumpCb;
-            const dumpOffloadCb_t m_dumpOffloadCb;
-            const cleanupCb_t m_cleanupCb;
-        };
-        const uint32_t m_fidx;  // The index of the tracing function
-        void* const m_userp;  // The user pointer to pass to the callback (the symbol table)
-        const bool m_isLibInstance;  // Whether the callback is for a --lib-create instance
-        const std::string m_name;  // The name of the instance callback is for
-        const uint32_t m_nTraceCodes;  // The number of trace codes used by callback
-        CallbackRecord(initCb_t cb, void* userp, bool isLibInstance, const std::string& name,
-                       uint32_t nTraceCodes)
-            : m_initCb{cb}
-            , m_fidx{0}
-            , m_userp{userp}
-            , m_isLibInstance{isLibInstance}
-            , m_name{name}
-            , m_nTraceCodes{nTraceCodes} {}
-        CallbackRecord(dumpCb_t cb, uint32_t fidx, void* userp)
-            : m_dumpCb{cb}
-            , m_fidx{fidx}
-            , m_userp{userp}
-            , m_isLibInstance{false}  // Don't care
-            , m_name{}  // Don't care
-            , m_nTraceCodes{0}  // Don't care
-        {}
-        CallbackRecord(dumpOffloadCb_t cb, uint32_t fidx, void* userp)
-            : m_dumpOffloadCb{cb}
-            , m_fidx{fidx}
-            , m_userp{userp}
-            , m_isLibInstance{false}  // Don't care
-            , m_name{}  // Don't care
-            , m_nTraceCodes{0}  // Don't care
-        {}
-        CallbackRecord(cleanupCb_t cb, void* userp)
-            : m_cleanupCb{cb}
-            , m_fidx{0}
-            , m_userp{userp}
-            , m_isLibInstance{false}  // Don't care
-            , m_name{}  // Don't care
-            , m_nTraceCodes{0}  // Don't care
-        {}
-    };
-
-    bool m_offload = false;  // Use the offload thread
-    bool m_parallel = false;  // Use parallel tracing
-
-    struct ParallelWorkerData final {
-        const dumpCb_t m_cb;  // The callback
-        void* const m_userp;  // The use pointer to pass to the callback
-        Buffer* const m_bufp;  // The buffer pointer to pass to the callback
-        std::atomic m_ready{false};  // The ready flag
-        mutable VerilatedMutex m_mutex;  // Mutex for suspension until ready
-        std::condition_variable_any m_cv;  // Condition variable for suspension
-        bool m_waiting VL_GUARDED_BY(m_mutex) = false;  // Whether a thread is suspended in wait()
-
-        void wait();
-
-        ParallelWorkerData(dumpCb_t cb, void* userp, Buffer* bufp)
-            : m_cb{cb}
-            , m_userp{userp}
-            , m_bufp{bufp} {}
-    };
-
-    // Passed a ParallelWorkerData*, second argument is ignored
-    static void parallelWorkerTask(void*, bool);
-
-protected:
-    uint32_t* m_sigs_oldvalp = nullptr;  // Previous value store
-    EData* m_sigs_enabledp = nullptr;  // Bit vector of enabled codes (nullptr = all on)
-private:
-    std::vector m_sigs_enabledVec;  // Staging for m_sigs_enabledp
-    std::vector m_initCbs;  // Routines to initialize tracing
-    std::vector m_constCbs;  // Routines to perform const dump
-    std::vector m_constOffloadCbs;  // Routines to perform offloaded const dump
-    std::vector m_fullCbs;  // Routines to perform full dump
-    std::vector m_fullOffloadCbs;  // Routines to perform offloaded full dump
-    std::vector m_chgCbs;  // Routines to perform incremental dump
-    std::vector m_chgOffloadCbs;  // Routines to perform offloaded incremental dump
-    std::vector m_cleanupCbs;  // Routines to call at the end of dump
-    bool m_constDump = true;  // Whether a const dump is required on the next call to 'dump'
-    bool m_fullDump = true;  // Whether a full dump is required on the next call to 'dump'
-    uint32_t m_nextCode = 0;  // Next code number to assign
-    uint32_t m_numSignals = 0;  // Number of distinct signals
-    uint32_t m_maxBits = 0;  // Number of bits in the widest signal
-    void* m_initUserp = nullptr;  // The callback userp of the instance currently being initialized
-    // TODO: Should keep this as a Trie, that is how it's accessed all the time.
-    std::vector> m_dumpvars;  // dumpvar() entries
-    double m_timeRes = 1e-9;  // Time resolution (ns/ms etc)
-    double m_timeUnit = 1e-0;  // Time units (ns/ms etc)
-    uint64_t m_timeLastDump = 0;  // Last time we did a dump
-    bool m_didSomeDump = false;  // Did at least one dump (i.e.: m_timeLastDump is valid)
-    VerilatedContext* m_contextp = nullptr;  // The context used by the traced models
-    std::set m_models;  // The collection of models being traced
-
-    void addCallbackRecord(std::vector& cbVec, CallbackRecord&& cbRec)
-        VL_MT_SAFE_EXCLUDES(m_mutex);
-
-    // Equivalent to 'this' but is of the sub-type 'T_Trace*'. Use 'self()->'
-    // to access duck-typed functions to avoid a virtual function call.
-    T_Trace* self() { return static_cast(this); }
-
-    void runCallbacks(const std::vector& cbVec);
-    void runOffloadedCallbacks(const std::vector& cbVec);
-
-    // Flush any remaining data for this file
-    static void onFlush(void* selfp) VL_MT_UNSAFE_ONE;
-    // Close the file on termination
-    static void onExit(void* selfp) VL_MT_UNSAFE_ONE;
-
-    // Number of total offload buffers that have been allocated
-    uint32_t m_numOffloadBuffers = 0;
-    // Size of offload buffers
-    size_t m_offloadBufferSize = 0;
-    // Buffers handed to worker for processing
-    VerilatedThreadQueue m_offloadBuffersToWorker;
-    // Buffers returned from worker after processing
-    VerilatedThreadQueue m_offloadBuffersFromWorker;
-
-protected:
-    // Write pointer into current buffer
-    uint32_t* m_offloadBufferWritep = nullptr;
-    // End of offload buffer
-    uint32_t* m_offloadBufferEndp = nullptr;
-
-private:
-    // The offload worker thread itself
-    std::unique_ptr m_workerThread;
-
-    // Get a new offload buffer that can be populated. May block if none available
-    uint32_t* getOffloadBuffer();
-
-    // The function executed by the offload worker thread
-    void offloadWorkerThreadMain();
-
-    // Wait until given offload buffer is placed in m_offloadBuffersFromWorker
-    void waitForOffloadBuffer(const uint32_t* bufferp);
-
-    // Shut down and join worker, if it's running, otherwise do nothing
-    void shutdownOffloadWorker();
-
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedTrace);
-
-protected:
-    //=========================================================================
-    // Internals available to format-specific implementations
-
-    mutable VerilatedMutex m_mutex;  // Ensure dump() etc only called from single thread
-
-    uint32_t nextCode() const { return m_nextCode; }
-    uint32_t numSignals() const { return m_numSignals; }
-    uint32_t maxBits() const { return m_maxBits; }
-    void* initUserp() const { return m_initUserp; }
-    void constDump(bool value) { m_constDump = value; }
-    void fullDump(bool value) { m_fullDump = value; }
-
-    double timeRes() const { return m_timeRes; }
-    double timeUnit() const { return m_timeUnit; }
-    std::string timeResStr() const;
-
-    void traceInit() VL_MT_UNSAFE;
-
-    // Declare new signal and return true if enabled
-    bool declCode(uint32_t code, const std::string& declName, uint32_t bits);
-
-    void closeBase();
-    void flushBase();
-
-    bool offload() const { return m_offload; }
-    bool parallel() const { return m_parallel; }
-
-    // Return last ' ' separated word. Assumes string does not end in ' '.
-    static std::string lastWord(const std::string& str) {
-        const size_t idx = str.rfind(' ');
-        if (idx == std::string::npos) return str;
-        return str.substr(idx + 1);
-    }
-
-    //=========================================================================
-    // Virtual functions to be provided by the format-specific implementation
-
-    // Called when the trace moves forward to a new time point
-    virtual void emitTimeChange(uint64_t timeui) = 0;
-
-    // These hooks are called before a full or change based dump is produced.
-    // The return value indicates whether to proceed with the dump.
-    virtual bool preFullDump() = 0;
-    virtual bool preChangeDump() = 0;
-
-    // Trace buffer management
-    virtual Buffer* getTraceBuffer(uint32_t fidx) = 0;
-    virtual void commitTraceBuffer(Buffer*) = 0;
-
-    // Configure sub-class
-    virtual void configure(const VerilatedTraceConfig&) = 0;
-
-public:
-    //=========================================================================
-    // External interface to client code
-
-    explicit VerilatedTrace();
-    ~VerilatedTrace();
-
-    // Set time units (s/ms, defaults to ns)
-    void set_time_unit(const char* unitp) VL_MT_SAFE;
-    void set_time_unit(const std::string& unit) VL_MT_SAFE;
-    // Set time resolution (s/ms, defaults to ns)
-    void set_time_resolution(const char* unitp) VL_MT_SAFE;
-    void set_time_resolution(const std::string& unit) VL_MT_SAFE;
-    // Set variables to dump, using $dumpvars format
-    // If level = 0, dump everything and hier is then ignored
-    void dumpvars(int level, const std::string& hier) VL_MT_SAFE;
-
-    // Call
-    void dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex);
-
-    //=========================================================================
-    // Internal interface to Verilator generated code
-
-    //=========================================================================
-    // Non-hot path internal interface to Verilator generated code
-
-    void addModel(VerilatedModel*) VL_MT_SAFE_EXCLUDES(m_mutex);
-    void addInitCb(initCb_t cb, void* userp, const std::string& name, bool isLibInstance,
-                   uint32_t nTraceCodes) VL_MT_SAFE;
-    void addConstCb(dumpCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
-    void addConstCb(dumpOffloadCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
-    void addFullCb(dumpCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
-    void addFullCb(dumpOffloadCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
-    void addChgCb(dumpCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
-    void addChgCb(dumpOffloadCb_t cb, uint32_t fidx, void* userp) VL_MT_SAFE;
-    void addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE;
-    void initLib(const std::string& name) VL_MT_UNSAFE;
-};
-
-//=============================================================================
-// VerilatedTraceBuffer
-
-// T_Buffer is the format-specific base class of VerilatedTraceBuffer.
-// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
-template 
-class VerilatedTraceBuffer VL_NOT_FINAL : public T_Buffer {
-protected:
-    // Type of the owner trace file
-    using Trace = typename std::remove_cv<
-        typename std::remove_reference::type>::type;
-
-    static_assert(std::has_virtual_destructor::value, "");
-    static_assert(std::is_base_of, Trace>::value, "");
-
-    friend Trace;  // Give the trace file access to the private bits
-    friend std::default_delete>;
-
-    uint32_t* const m_sigs_oldvalp;  // Previous value store
-    EData* const m_sigs_enabledp;  // Bit vector of enabled codes (nullptr = all on)
-
-    explicit VerilatedTraceBuffer(Trace& owner);
-    ~VerilatedTraceBuffer() override = default;
-
-public:
-    //=========================================================================
-    // Hot path internal interface to Verilator generated code
-
-    // Implementation note: We rely on the following duck-typed implementations
-    // in the derived class T_Derived. These emit* functions record a format-
-    // specific trace entry. Normally one would use pure virtual functions for
-    // these here, but we cannot afford dynamic dispatch for calling these as
-    // this is very hot code during tracing.
-
-    // duck-typed void emitBit(uint32_t code, CData newval) = 0;
-    // duck-typed void emitCData(uint32_t code, CData newval, int bits) = 0;
-    // duck-typed void emitSData(uint32_t code, SData newval, int bits) = 0;
-    // duck-typed void emitIData(uint32_t code, IData newval, int bits) = 0;
-    // duck-typed void emitQData(uint32_t code, QData newval, int bits) = 0;
-    // duck-typed void emitWData(uint32_t code, const WData* newvalp, int bits) = 0;
-    // duck-typed void emitDouble(uint32_t code, double newval) = 0;
-
-    VL_ATTR_ALWINLINE uint32_t* oldp(uint32_t code) { return m_sigs_oldvalp + code; }
-
-    // Write to previous value buffer value and emit trace entry.
-    void fullBit(uint32_t* oldp, CData newval);
-    void fullCData(uint32_t* oldp, CData newval, int bits);
-    void fullSData(uint32_t* oldp, SData newval, int bits);
-    void fullIData(uint32_t* oldp, IData newval, int bits);
-    void fullQData(uint32_t* oldp, QData newval, int bits);
-    void fullWData(uint32_t* oldp, const WData* newvalp, int bits);
-    void fullDouble(uint32_t* oldp, double newval);
-    void fullEvent(uint32_t* oldp, const VlEventBase* newvalp);
-    void fullEventTriggered(uint32_t* oldp);
-
-    // In non-offload mode, these are called directly by the trace callbacks,
-    // and are called chg*. In offload mode, they are called by the worker
-    // thread and are called chg*Impl
-
-    // Check previous dumped value of signal. If changed, then emit trace entry
-    VL_ATTR_ALWINLINE void chgBit(uint32_t* oldp, CData newval) {
-        const uint32_t diff = *oldp ^ newval;
-        if (VL_UNLIKELY(diff)) fullBit(oldp, newval);
-    }
-    VL_ATTR_ALWINLINE void chgCData(uint32_t* oldp, CData newval, int bits) {
-        const uint32_t diff = *oldp ^ newval;
-        if (VL_UNLIKELY(diff)) fullCData(oldp, newval, bits);
-    }
-    VL_ATTR_ALWINLINE void chgSData(uint32_t* oldp, SData newval, int bits) {
-        const uint32_t diff = *oldp ^ newval;
-        if (VL_UNLIKELY(diff)) fullSData(oldp, newval, bits);
-    }
-    VL_ATTR_ALWINLINE void chgIData(uint32_t* oldp, IData newval, int bits) {
-        const uint32_t diff = *oldp ^ newval;
-        if (VL_UNLIKELY(diff)) fullIData(oldp, newval, bits);
-    }
-    VL_ATTR_ALWINLINE void chgQData(uint32_t* oldp, QData newval, int bits) {
-        QData old;
-        std::memcpy(&old, oldp, sizeof(old));
-        const uint64_t diff = old ^ newval;
-        if (VL_UNLIKELY(diff)) fullQData(oldp, newval, bits);
-    }
-    VL_ATTR_ALWINLINE void chgWData(uint32_t* oldp, const WData* newvalp, int bits) {
-        for (int i = 0; i < (bits + 31) / 32; ++i) {
-            if (VL_UNLIKELY(oldp[i] ^ newvalp[i])) {
-                fullWData(oldp, newvalp, bits);
-                return;
-            }
-        }
-    }
-    VL_ATTR_ALWINLINE void chgEvent(uint32_t* oldp, const VlEventBase* newvalp) {
-        if (newvalp->isTriggered()) fullEvent(oldp, newvalp);
-    }
-    VL_ATTR_ALWINLINE void chgEventTriggered(uint32_t* oldp) { fullEventTriggered(oldp); }
-    VL_ATTR_ALWINLINE void chgDouble(uint32_t* oldp, double newval) {
-        double old;  // LCOV_EXCL_LINE  // lcov bug
-        std::memcpy(&old, oldp, sizeof(old));
-        if (VL_UNLIKELY(old != newval)) fullDouble(oldp, newval);
-    }
-};
-
-//=============================================================================
-// VerilatedTraceOffloadBuffer
-
-// T_Buffer is the format-specific base class of VerilatedTraceBuffer.
-// The format-specific hot-path methods use duck-typing via T_Buffer for performance.
-template 
-class VerilatedTraceOffloadBuffer final : public VerilatedTraceBuffer {
-    using typename VerilatedTraceBuffer::Trace;
-
-    friend Trace;  // Give the trace file access to the private bits
-
-    uint32_t* m_offloadBufferWritep;  // Write pointer into current buffer
-    uint32_t* const m_offloadBufferEndp;  // End of offload buffer
-
-    explicit VerilatedTraceOffloadBuffer(Trace& owner);
-    ~VerilatedTraceOffloadBuffer() override = default;
-
-public:
-    //=========================================================================
-    // Hot path internal interface to Verilator generated code
-
-    // Offloaded tracing. Just dump everything in the offload buffer
-    void chgBit(uint32_t code, CData newval) {
-        m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_BIT_0 | newval;
-        m_offloadBufferWritep[1] = code;
-        m_offloadBufferWritep += 2;
-        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
-    }
-    void chgCData(uint32_t code, CData newval, int bits) {
-        m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_CDATA;
-        m_offloadBufferWritep[1] = code;
-        m_offloadBufferWritep[2] = newval;
-        m_offloadBufferWritep += 3;
-        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
-    }
-    void chgSData(uint32_t code, SData newval, int bits) {
-        m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_SDATA;
-        m_offloadBufferWritep[1] = code;
-        m_offloadBufferWritep[2] = newval;
-        m_offloadBufferWritep += 3;
-        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
-    }
-    void chgIData(uint32_t code, IData newval, int bits) {
-        m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_IDATA;
-        m_offloadBufferWritep[1] = code;
-        m_offloadBufferWritep[2] = newval;
-        m_offloadBufferWritep += 3;
-        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
-    }
-    void chgQData(uint32_t code, QData newval, int bits) {
-        m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_QDATA;
-        m_offloadBufferWritep[1] = code;
-        *reinterpret_cast(m_offloadBufferWritep + 2) = newval;
-        m_offloadBufferWritep += 4;
-        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
-    }
-    void chgWData(uint32_t code, const WData* newvalp, int bits) {
-        m_offloadBufferWritep[0] = (bits << 4) | VerilatedTraceOffloadCommand::CHG_WDATA;
-        m_offloadBufferWritep[1] = code;
-        m_offloadBufferWritep += 2;
-        for (int i = 0; i < (bits + 31) / 32; ++i) *m_offloadBufferWritep++ = newvalp[i];
-        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
-    }
-    void chgDouble(uint32_t code, double newval) {
-        m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_DOUBLE;
-        m_offloadBufferWritep[1] = code;
-        // cppcheck-suppress invalidPointerCast
-        *reinterpret_cast(m_offloadBufferWritep + 2) = newval;
-        m_offloadBufferWritep += 4;
-        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
-    }
-    void chgEvent(uint32_t code, const VlEventBase* newvalp) {
-        if (newvalp->isTriggered()) chgEventTriggered(code);
-    }
-    void chgEventTriggered(uint32_t code) {
-        m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::CHG_EVENT;
-        m_offloadBufferWritep[1] = code;
-        m_offloadBufferWritep += 2;
-        VL_DEBUG_IF(assert(m_offloadBufferWritep <= m_offloadBufferEndp););
-    }
-};
-
-#endif  // guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace_imp.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace_imp.h
deleted file mode 100644
index 635e3e897ce..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_trace_imp.h
+++ /dev/null
@@ -1,936 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-//
-// Verilated tracing implementation code template common to all formats.
-// This file is included by the format-specific implementations and
-// should not be used otherwise.
-//
-//=============================================================================
-
-// clang-format off
-
-#ifndef VL_CPPCHECK
-#if !defined(VL_SUB_T) || !defined(VL_BUF_T)
-# error "This file should be included in trace format implementations"
-#endif
-
-#include "verilated_intrinsics.h"
-#include "verilated_trace.h"
-#include "verilated_threads.h"
-#include 
-
-#if 0
-# include 
-# define VL_TRACE_OFFLOAD_DEBUG(msg) std::cout << "TRACE OFFLOAD THREAD: " << msg << "\n"
-#else
-# define VL_TRACE_OFFLOAD_DEBUG(msg)
-#endif
-
-// clang-format on
-
-//=============================================================================
-// Static utility functions
-
-static double timescaleToDouble(const char* unitp) VL_PURE {
-    char* endp = nullptr;
-    double value = std::strtod(unitp, &endp);
-    // On error so we allow just "ns" to return 1e-9.
-    if (value == 0.0 && endp == unitp) value = 1;
-    unitp = endp;
-    for (; *unitp && std::isspace(*unitp); ++unitp) {}
-    switch (*unitp) {
-    case 's': value *= 1e0; break;
-    case 'm': value *= 1e-3; break;
-    case 'u': value *= 1e-6; break;
-    case 'n': value *= 1e-9; break;
-    case 'p': value *= 1e-12; break;
-    case 'f': value *= 1e-15; break;
-    case 'a': value *= 1e-18; break;
-    }
-    return value;
-}
-
-//=========================================================================
-// Buffer management
-
-template <>
-uint32_t* VerilatedTrace::getOffloadBuffer() {
-    uint32_t* bufferp;
-    // Some jitter is expected, so some number of alternative offload buffers are
-    // required, but don't allocate more than 8 buffers.
-    if (m_numOffloadBuffers < 8) {
-        // Allocate a new buffer if none is available
-        if (!m_offloadBuffersFromWorker.tryGet(bufferp)) {
-            ++m_numOffloadBuffers;
-            // Note: over allocate a bit so pointer comparison is well defined
-            // if we overflow only by a small amount
-            bufferp = new uint32_t[m_offloadBufferSize + 16];
-        }
-    } else {
-        // Block until a buffer becomes available
-        bufferp = m_offloadBuffersFromWorker.get();
-    }
-    return bufferp;
-}
-
-template <>
-void VerilatedTrace::waitForOffloadBuffer(const uint32_t* buffp) {
-    // Slow path code only called on flush/shutdown, so use a simple algorithm.
-    // Collect buffers from worker and stash them until we get the one we want.
-    std::deque stash;
-    do { stash.push_back(m_offloadBuffersFromWorker.get()); } while (stash.back() != buffp);
-    // Now put them back in the queue, in the original order.
-    while (!stash.empty()) {
-        m_offloadBuffersFromWorker.put_front(stash.back());
-        stash.pop_back();
-    }
-}
-
-//=========================================================================
-// Worker thread
-
-template <>
-void VerilatedTrace::offloadWorkerThreadMain() {
-    bool shutdown = false;
-
-    do {
-        uint32_t* const bufferp = m_offloadBuffersToWorker.get();
-
-        VL_TRACE_OFFLOAD_DEBUG("");
-        VL_TRACE_OFFLOAD_DEBUG("Got buffer: " << bufferp);
-
-        const uint32_t* readp = bufferp;
-
-        std::unique_ptr traceBufp;  // We own the passed tracebuffer
-
-        while (true) {
-            const uint32_t cmd = readp[0];
-            const uint32_t top = cmd >> 4;
-            // Always set this up, as it is almost always needed
-            uint32_t* const oldp = m_sigs_oldvalp + readp[1];
-            // Note this increment needs to be undone on commands which do not
-            // actually contain a code, but those are the rare cases.
-            readp += 2;
-
-            switch (cmd & 0xF) {
-                //===
-                // CHG_* commands
-            case VerilatedTraceOffloadCommand::CHG_BIT_0:
-                VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_0 " << top);
-                traceBufp->chgBit(oldp, 0);
-                continue;
-            case VerilatedTraceOffloadCommand::CHG_BIT_1:
-                VL_TRACE_OFFLOAD_DEBUG("Command CHG_BIT_1 " << top);
-                traceBufp->chgBit(oldp, 1);
-                continue;
-            case VerilatedTraceOffloadCommand::CHG_CDATA:
-                VL_TRACE_OFFLOAD_DEBUG("Command CHG_CDATA " << top);
-                // Bits stored in bottom byte of command
-                traceBufp->chgCData(oldp, *readp, top);
-                readp += 1;
-                continue;
-            case VerilatedTraceOffloadCommand::CHG_SDATA:
-                VL_TRACE_OFFLOAD_DEBUG("Command CHG_SDATA " << top);
-                // Bits stored in bottom byte of command
-                traceBufp->chgSData(oldp, *readp, top);
-                readp += 1;
-                continue;
-            case VerilatedTraceOffloadCommand::CHG_IDATA:
-                VL_TRACE_OFFLOAD_DEBUG("Command CHG_IDATA " << top);
-                // Bits stored in bottom byte of command
-                traceBufp->chgIData(oldp, *readp, top);
-                readp += 1;
-                continue;
-            case VerilatedTraceOffloadCommand::CHG_QDATA:
-                VL_TRACE_OFFLOAD_DEBUG("Command CHG_QDATA " << top);
-                // Bits stored in bottom byte of command
-                traceBufp->chgQData(oldp, *reinterpret_cast(readp), top);
-                readp += 2;
-                continue;
-            case VerilatedTraceOffloadCommand::CHG_WDATA:
-                VL_TRACE_OFFLOAD_DEBUG("Command CHG_WDATA " << top);
-                traceBufp->chgWData(oldp, readp, top);
-                readp += VL_WORDS_I(top);
-                continue;
-            case VerilatedTraceOffloadCommand::CHG_DOUBLE:
-                VL_TRACE_OFFLOAD_DEBUG("Command CHG_DOUBLE " << top);
-                traceBufp->chgDouble(oldp, *reinterpret_cast(readp));
-                readp += 2;
-                continue;
-            case VerilatedTraceOffloadCommand::CHG_EVENT:
-                VL_TRACE_OFFLOAD_DEBUG("Command CHG_EVENT " << top);
-                traceBufp->chgEventTriggered(oldp);
-                continue;
-
-                //===
-                // Rare commands
-            case VerilatedTraceOffloadCommand::TIME_CHANGE: {
-                VL_TRACE_OFFLOAD_DEBUG("Command TIME_CHANGE " << top);
-                readp -= 1;  // No code in this command, undo increment
-                const uint64_t timeui
-                    = static_cast(*reinterpret_cast(readp)) << 32ULL
-                      | static_cast(*reinterpret_cast(readp + 1));
-                emitTimeChange(timeui);
-                readp += 2;
-                continue;
-            }
-            case VerilatedTraceOffloadCommand::TRACE_BUFFER:
-                VL_TRACE_OFFLOAD_DEBUG("Command TRACE_BUFFER " << top);
-                readp -= 1;  // No code in this command, undo increment
-                traceBufp.reset(*reinterpret_cast(readp));
-                readp += 2;
-                continue;
-
-                //===
-                // Commands ending this buffer
-            case VerilatedTraceOffloadCommand::END:  //
-                VL_TRACE_OFFLOAD_DEBUG("Command END");
-                break;
-            case VerilatedTraceOffloadCommand::SHUTDOWN:
-                VL_TRACE_OFFLOAD_DEBUG("Command SHUTDOWN");
-                shutdown = true;
-                break;
-
-            //===
-            // Unknown command
-            default: {  // LCOV_EXCL_START
-                VL_TRACE_OFFLOAD_DEBUG("Command UNKNOWN " << cmd);
-                VL_FATAL_MT(__FILE__, __LINE__, "", "Unknown trace command");
-                break;
-            }  // LCOV_EXCL_STOP
-            }
-
-            // The above switch will execute 'continue' when necessary,
-            // so if we ever reach here, we are done with the buffer.
-            break;
-        }
-
-        VL_TRACE_OFFLOAD_DEBUG("Returning buffer");
-
-        // Return buffer
-        m_offloadBuffersFromWorker.put(bufferp);
-    } while (VL_LIKELY(!shutdown));
-}
-
-template <>
-void VerilatedTrace::shutdownOffloadWorker() {
-    // If the worker thread is not running, done..
-    if (!m_workerThread) return;
-
-    // Hand an buffer with a shutdown command to the worker thread
-    uint32_t* const bufferp = getOffloadBuffer();
-    bufferp[0] = VerilatedTraceOffloadCommand::SHUTDOWN;
-    m_offloadBuffersToWorker.put(bufferp);
-    // Wait for it to return
-    waitForOffloadBuffer(bufferp);
-    // Join the thread and delete it
-    m_workerThread->join();
-    m_workerThread.reset(nullptr);
-}
-
-//=============================================================================
-// Life cycle
-
-template <>
-void VerilatedTrace::closeBase() {
-    if (offload()) {
-        shutdownOffloadWorker();
-        while (m_numOffloadBuffers) {
-            delete[] m_offloadBuffersFromWorker.get();
-            --m_numOffloadBuffers;
-        }
-    }
-}
-
-template <>
-void VerilatedTrace::flushBase() {
-    if (offload()) {
-        // Hand an empty buffer to the worker thread
-        uint32_t* const bufferp = getOffloadBuffer();
-        *bufferp = VerilatedTraceOffloadCommand::END;
-        m_offloadBuffersToWorker.put(bufferp);
-        // Wait for it to be returned. As the processing is in-order,
-        // this ensures all previous buffers have been processed.
-        waitForOffloadBuffer(bufferp);
-    }
-}
-
-//=============================================================================
-// Callbacks to run on global events
-
-template <>
-void VerilatedTrace::onFlush(void* selfp) {
-    // This calls 'flush' on the derived class (which must then get any mutex)
-    reinterpret_cast(selfp)->flush();
-}
-
-template <>
-void VerilatedTrace::onExit(void* selfp) {
-    // This calls 'close' on the derived class (which must then get any mutex)
-    reinterpret_cast(selfp)->close();
-}
-
-//=============================================================================
-// VerilatedTrace
-
-template <>
-VerilatedTrace::VerilatedTrace() {
-    set_time_unit(Verilated::threadContextp()->timeunitString());
-    set_time_resolution(Verilated::threadContextp()->timeprecisionString());
-}
-
-template <>
-VerilatedTrace::~VerilatedTrace() {
-    if (m_sigs_oldvalp) VL_DO_CLEAR(delete[] m_sigs_oldvalp, m_sigs_oldvalp = nullptr);
-    if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
-    Verilated::removeFlushCb(VerilatedTrace::onFlush, this);
-    Verilated::removeExitCb(VerilatedTrace::onExit, this);
-    if (offload()) closeBase();
-}
-
-//=========================================================================
-// Internals available to format-specific implementations
-
-template <>
-void VerilatedTrace::traceInit() VL_MT_UNSAFE {
-    // Note: It is possible to re-open a trace file (VCD in particular),
-    // so we must reset the next code here, but it must have the same number
-    // of codes on re-open
-    const uint32_t expectedCodes = nextCode();
-    m_nextCode = 1;
-    m_numSignals = 0;
-    m_maxBits = 0;
-    m_sigs_enabledVec.clear();
-
-    // Call all initialize callbacks for root (non-library) instances, which will:
-    // - Call decl* for each signal (these eventually call ::declCode)
-    // - Call the initialize callbacks of library instances underneath
-    // - Store the base code
-    for (const CallbackRecord& cbr : m_initCbs) {
-        if (cbr.m_isLibInstance) continue;  // Will be called from parent callback
-        const uint32_t baseCode = nextCode();
-        m_nextCode += cbr.m_nTraceCodes;
-        m_initUserp = cbr.m_userp;
-        cbr.m_initCb(cbr.m_userp, self(), baseCode);
-    }
-
-    if (expectedCodes && nextCode() != expectedCodes) {
-        VL_FATAL_MT(__FILE__, __LINE__, "",
-                    "Reopening trace file with different number of signals");
-    }
-
-    // Now that we know the number of codes, allocate space for the buffer
-    // holding previous signal values.
-    if (!m_sigs_oldvalp) m_sigs_oldvalp = new uint32_t[nextCode()];
-
-    // Apply enables
-    if (m_sigs_enabledp) VL_DO_CLEAR(delete[] m_sigs_enabledp, m_sigs_enabledp = nullptr);
-    if (!m_sigs_enabledVec.empty()) {
-        // Else if was empty, m_sigs_enabledp = nullptr to short circuit tests
-        // But it isn't, so alloc one bit for each code to indicate enablement
-        // We don't want to still use m_signs_enabledVec as std::vector is not
-        // guaranteed to be fast
-        m_sigs_enabledp = new uint32_t[1 + VL_WORDS_I(nextCode())]{0};
-        m_sigs_enabledVec.reserve(nextCode());
-        for (size_t code = 0; code < nextCode(); ++code) {
-            if (m_sigs_enabledVec[code]) {
-                m_sigs_enabledp[VL_BITWORD_I(code)] |= 1U << VL_BITBIT_I(code);
-            }
-        }
-        m_sigs_enabledVec.clear();
-    }
-
-    // Set callback so flush/abort will flush this file
-    Verilated::addFlushCb(VerilatedTrace::onFlush, this);
-    Verilated::addExitCb(VerilatedTrace::onExit, this);
-
-    if (offload()) {
-        // Compute offload buffer size. we need to be able to store a new value for
-        // each signal, which is 'nextCode()' entries after the init callbacks
-        // above have been run, plus up to 2 more words of metadata per signal,
-        // plus fixed overhead of 1 for a termination flag and 3 for a time stamp
-        // update and 2 for the buffer address.
-        m_offloadBufferSize = nextCode() + numSignals() * 2 + 6;
-
-        // Start the worker thread
-        m_workerThread.reset(
-            new std::thread{&VerilatedTrace::offloadWorkerThreadMain, this});
-    }
-}
-
-template <>
-bool VerilatedTrace::declCode(uint32_t code, const std::string& declName,
-                                                  uint32_t bits) {
-    if (VL_UNCOVERABLE(!code)) {
-        VL_FATAL_MT(__FILE__, __LINE__, "", "Internal: internal trace problem, code 0 is illegal");
-    }
-    // To keep it simple, this is O(enables * signals), but we expect few enables
-    bool enabled = false;
-    if (m_dumpvars.empty()) enabled = true;
-    for (const auto& item : m_dumpvars) {
-        const int dumpvarsLevel = item.first;
-        const char* dvp = item.second.c_str();
-        const char* np = declName.c_str();
-        while (*dvp && *dvp == *np) {
-            ++dvp;
-            ++np;
-        }
-        if (*dvp) continue;  // Didn't match dumpvar item
-        if (*np && *np != ' ') continue;  // e.g. "t" isn't a match for "top"
-        int levels = 0;
-        while (*np) {
-            if (*np++ == ' ') ++levels;
-        }
-        if (levels > dumpvarsLevel) continue;  // Too deep
-        // We only need to set first code word if it's a multicode signal
-        // as that's all we'll check for later
-        if (m_sigs_enabledVec.size() <= code) m_sigs_enabledVec.resize((code + 1024) * 2);
-        m_sigs_enabledVec[code] = true;
-        enabled = true;
-        break;
-    }
-
-    ++m_numSignals;
-    m_maxBits = std::max(m_maxBits, bits);
-    return enabled;
-}
-
-//=========================================================================
-// Internals available to format-specific implementations
-
-template <>
-std::string VerilatedTrace::timeResStr() const {
-    return vl_timescaled_double(m_timeRes);
-}
-
-//=========================================================================
-// External interface to client code
-
-template <>
-void VerilatedTrace::set_time_unit(const char* unitp) VL_MT_SAFE {
-    m_timeUnit = timescaleToDouble(unitp);
-}
-template <>
-void VerilatedTrace::set_time_unit(const std::string& unit) VL_MT_SAFE {
-    set_time_unit(unit.c_str());
-}
-template <>
-void VerilatedTrace::set_time_resolution(const char* unitp) VL_MT_SAFE {
-    m_timeRes = timescaleToDouble(unitp);
-}
-template <>
-void VerilatedTrace::set_time_resolution(const std::string& unit) VL_MT_SAFE {
-    set_time_resolution(unit.c_str());
-}
-template <>
-void VerilatedTrace::dumpvars(int level, const std::string& hier) VL_MT_SAFE {
-    if (level == 0) {
-        m_dumpvars.clear();  // empty = everything on
-    } else {
-        // Convert Verilog . separators to trace space separators
-        std::string hierSpaced = hier;
-        for (auto& i : hierSpaced) {
-            if (i == '.') i = ' ';
-        }
-        m_dumpvars.emplace_back(level, hierSpaced);
-    }
-}
-
-template <>
-void VerilatedTrace::parallelWorkerTask(void* datap, bool) {
-    ParallelWorkerData* const wdp = reinterpret_cast(datap);
-    // Run the task
-    wdp->m_cb(wdp->m_userp, wdp->m_bufp);
-    // Mark buffer as ready
-    const VerilatedLockGuard lock{wdp->m_mutex};
-    wdp->m_ready.store(true);
-    if (wdp->m_waiting) wdp->m_cv.notify_one();
-}
-
-template <>
-VL_ATTR_NOINLINE void VerilatedTrace::ParallelWorkerData::wait() {
-    // Spin for a while, waiting for the buffer to become ready
-    for (int i = 0; i < VL_LOCK_SPINS; ++i) {
-        if (VL_LIKELY(m_ready.load(std::memory_order_relaxed))) return;
-        VL_CPU_RELAX();
-    }
-    // We have been spinning for a while, so yield the thread
-    VerilatedLockGuard lock{m_mutex};
-    m_waiting = true;
-    m_cv.wait(m_mutex, [this] { return m_ready.load(std::memory_order_relaxed); });
-    m_waiting = false;
-}
-
-template <>
-void VerilatedTrace::runCallbacks(const std::vector& cbVec) {
-    if (parallel()) {
-        // If tracing in parallel, dispatch to the thread pool
-        VlThreadPool* threadPoolp = static_cast(m_contextp->threadPoolp());
-        // List of work items for thread (std::list, as ParallelWorkerData is not movable)
-        std::list workerData;
-        // We use the whole pool + the main thread
-        const unsigned threads = threadPoolp->numThreads() + 1;
-        // Main thread executes all jobs with index % threads == 0
-        std::vector mainThreadWorkerData;
-        // Enqueue all the jobs
-        for (const CallbackRecord& cbr : cbVec) {
-            // Always get the trace buffer on the main thread
-            Buffer* const bufp = getTraceBuffer(cbr.m_fidx);
-            // Create new work item
-            workerData.emplace_back(cbr.m_dumpCb, cbr.m_userp, bufp);
-            // Grab the new work item
-            ParallelWorkerData* const itemp = &workerData.back();
-            // Enqueue task to thread pool, or main thread
-            if (unsigned rem = cbr.m_fidx % threads) {
-                threadPoolp->workerp(rem - 1)->addTask(parallelWorkerTask, itemp);
-            } else {
-                mainThreadWorkerData.push_back(itemp);
-            }
-        }
-        // Execute main thread jobs
-        for (ParallelWorkerData* const itemp : mainThreadWorkerData) {
-            parallelWorkerTask(itemp, false);
-        }
-        // Commit all trace buffers in order
-        for (ParallelWorkerData& item : workerData) {
-            // Wait until ready
-            item.wait();
-            // Commit the buffer
-            commitTraceBuffer(item.m_bufp);
-        }
-
-        // Done
-        return;
-    }
-    // Fall back on sequential execution
-    for (const CallbackRecord& cbr : cbVec) {
-        Buffer* const traceBufferp = getTraceBuffer(cbr.m_fidx);
-        cbr.m_dumpCb(cbr.m_userp, traceBufferp);
-        commitTraceBuffer(traceBufferp);
-    }
-}
-
-template <>
-void VerilatedTrace::runOffloadedCallbacks(
-    const std::vector& cbVec) {
-    // Fall back on sequential execution
-    for (const CallbackRecord& cbr : cbVec) {
-        Buffer* traceBufferp = getTraceBuffer(cbr.m_fidx);
-        cbr.m_dumpOffloadCb(cbr.m_userp, static_cast(traceBufferp));
-        commitTraceBuffer(traceBufferp);
-    }
-}
-
-template <>
-void VerilatedTrace::dump(uint64_t timeui) VL_MT_SAFE_EXCLUDES(m_mutex) {
-    // Not really VL_MT_SAFE but more VL_MT_UNSAFE_ONE.
-    // This does get the mutex, but if multiple threads are trying to dump
-    // chances are the data being dumped will have other problems
-    const VerilatedLockGuard lock{m_mutex};
-    if (VL_UNCOVERABLE(m_didSomeDump && timeui <= m_timeLastDump)) {  // LCOV_EXCL_START
-        VL_PRINTF_MT("%%Warning: previous dump at t=%" PRIu64 ", requesting t=%" PRIu64
-                     ", dump call ignored\n",
-                     m_timeLastDump, timeui);
-        return;
-    }  // LCOV_EXCL_STOP
-    m_timeLastDump = timeui;
-    m_didSomeDump = true;
-
-    Verilated::quiesce();
-
-    // Call hook for format-specific behaviour
-    if (VL_UNLIKELY(m_fullDump)) {
-        if (!preFullDump()) return;
-    } else {
-        if (!preChangeDump()) return;
-    }
-
-    uint32_t* bufferp = nullptr;
-    if (offload()) {
-        // Currently only incremental dumps run on the worker thread
-        if (VL_LIKELY(!m_fullDump)) {
-            // Get the offload buffer we are about to fill
-            bufferp = getOffloadBuffer();
-            m_offloadBufferWritep = bufferp;
-            m_offloadBufferEndp = bufferp + m_offloadBufferSize;
-
-            // Tell worker to update time point
-            m_offloadBufferWritep[0] = VerilatedTraceOffloadCommand::TIME_CHANGE;
-            *reinterpret_cast(m_offloadBufferWritep + 1)
-                = static_cast(timeui >> 32ULL);
-            *reinterpret_cast(m_offloadBufferWritep + 2)
-                = static_cast(timeui);
-            m_offloadBufferWritep += 3;
-        } else {
-            // Update time point
-            flushBase();
-            emitTimeChange(timeui);
-        }
-    } else {
-        // Update time point
-        emitTimeChange(timeui);
-    }
-
-    // Run the callbacks
-    if (VL_UNLIKELY(m_fullDump)) {
-        m_fullDump = false;  // No more need for next dump to be full
-        if (offload()) {
-            runOffloadedCallbacks(m_fullOffloadCbs);
-        } else {
-            runCallbacks(m_fullCbs);
-        }
-    } else {
-        if (offload()) {
-            runOffloadedCallbacks(m_chgOffloadCbs);
-        } else {
-            runCallbacks(m_chgCbs);
-        }
-    }
-
-    if (VL_UNLIKELY(m_constDump)) {
-        m_constDump = false;
-        if (offload()) {
-            runOffloadedCallbacks(m_constOffloadCbs);
-        } else {
-            runCallbacks(m_constCbs);
-        }
-    }
-
-    for (const CallbackRecord& cbr : m_cleanupCbs) cbr.m_cleanupCb(cbr.m_userp, self());
-
-    if (offload() && VL_LIKELY(bufferp)) {
-        // Mark end of the offload buffer we just filled
-        *m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::END;
-
-        // Assert no buffer overflow
-        assert(static_cast(m_offloadBufferWritep - bufferp) <= m_offloadBufferSize);
-
-        // Reset our pointers as we are giving up the buffer
-        m_offloadBufferWritep = nullptr;
-        m_offloadBufferEndp = nullptr;
-
-        // Pass it to the worker thread
-        m_offloadBuffersToWorker.put(bufferp);
-    }
-}
-
-//=============================================================================
-// Non-hot path internal interface to Verilator generated code
-
-template <>
-void VerilatedTrace::addModel(VerilatedModel* modelp)
-    VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-
-    const bool firstModel = m_models.empty();
-    const bool newModel = m_models.insert(modelp).second;
-    VerilatedContext* const contextp = modelp->contextp();
-
-    // Validate
-    if (!newModel) {  // LCOV_EXCL_START
-        VL_FATAL_MT(
-            __FILE__, __LINE__, "",
-            "The same model has already been added to this trace file or VerilatedContext");
-    }
-    if (VL_UNCOVERABLE(m_contextp && contextp != m_contextp)) {
-        VL_FATAL_MT(__FILE__, __LINE__, "",
-                    "A trace file instance can only handle models from the same VerilatedContext");
-    }
-    if (VL_UNCOVERABLE(m_didSomeDump)) {
-        VL_FATAL_MT(__FILE__, __LINE__, "",
-                    "Cannot add models to a trace file if 'dump' has already been called");
-    }  // LCOV_EXCL_STOP
-
-    // Keep hold of the context
-    m_contextp = contextp;
-
-    // Get the desired trace config from the model
-    const std::unique_ptr configp = modelp->traceConfig();
-
-    // Configure trace base class
-    if (!firstModel) {
-        if (m_offload != configp->m_useOffloading) {
-            VL_FATAL_MT(__FILE__, __LINE__, "",
-                        "Either all or no models using the same trace file must use offloading");
-        }
-    }
-    m_offload = configp->m_useOffloading;
-    // If at least one model requests parallel tracing, then use it
-    m_parallel |= configp->m_useParallel;
-
-    if (VL_UNCOVERABLE(m_parallel && m_offload)) {  // LCOV_EXCL_START
-        VL_FATAL_MT(__FILE__, __LINE__, "", "Cannot use parallel tracing with offloading");
-    }  // LCOV_EXCL_STOP
-
-    // Configure format-specific sub class
-    configure(*(configp.get()));
-}
-
-template <>
-void VerilatedTrace::addCallbackRecord(std::vector& cbVec,
-                                                           CallbackRecord&& cbRec)
-    VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    cbVec.push_back(cbRec);
-}
-
-template <>
-void VerilatedTrace::addInitCb(initCb_t cb, void* userp,
-                                                   const std::string& name, bool isLibInstance,
-                                                   uint32_t nTraceCodes) VL_MT_SAFE {
-    addCallbackRecord(m_initCbs, CallbackRecord{cb, userp, isLibInstance, name, nTraceCodes});
-}
-template <>
-void VerilatedTrace::addConstCb(dumpCb_t cb, uint32_t fidx,
-                                                    void* userp) VL_MT_SAFE {
-    addCallbackRecord(m_constCbs, CallbackRecord{cb, fidx, userp});
-}
-template <>
-void VerilatedTrace::addConstCb(dumpOffloadCb_t cb, uint32_t fidx,
-                                                    void* userp) VL_MT_SAFE {
-    addCallbackRecord(m_constOffloadCbs, CallbackRecord{cb, fidx, userp});
-}
-template <>
-void VerilatedTrace::addFullCb(dumpCb_t cb, uint32_t fidx,
-                                                   void* userp) VL_MT_SAFE {
-    addCallbackRecord(m_fullCbs, CallbackRecord{cb, fidx, userp});
-}
-template <>
-void VerilatedTrace::addFullCb(dumpOffloadCb_t cb, uint32_t fidx,
-                                                   void* userp) VL_MT_SAFE {
-    addCallbackRecord(m_fullOffloadCbs, CallbackRecord{cb, fidx, userp});
-}
-template <>
-void VerilatedTrace::addChgCb(dumpCb_t cb, uint32_t fidx,
-                                                  void* userp) VL_MT_SAFE {
-    addCallbackRecord(m_chgCbs, CallbackRecord{cb, fidx, userp});
-}
-template <>
-void VerilatedTrace::addChgCb(dumpOffloadCb_t cb, uint32_t fidx,
-                                                  void* userp) VL_MT_SAFE {
-    addCallbackRecord(m_chgOffloadCbs, CallbackRecord{cb, fidx, userp});
-}
-template <>
-void VerilatedTrace::addCleanupCb(cleanupCb_t cb, void* userp) VL_MT_SAFE {
-    addCallbackRecord(m_cleanupCbs, CallbackRecord{cb, userp});
-}
-
-template <>
-void VerilatedTrace::initLib(const std::string& name) VL_MT_SAFE {
-    // Note it's possible the instance doesn't exist if the lib was compiled without tracing
-    void* const prevInitUserp = m_initUserp;
-    for (const CallbackRecord& cbr : m_initCbs) {
-        if (cbr.m_name != name) continue;
-        const uint32_t baseCode = nextCode();
-        m_nextCode += cbr.m_nTraceCodes;
-        m_initUserp = cbr.m_userp;
-        cbr.m_initCb(cbr.m_userp, self(), baseCode);
-        m_initUserp = prevInitUserp;
-    }
-}
-
-//=========================================================================
-// Primitives converting binary values to strings...
-
-// All of these take a destination pointer where the string will be emitted,
-// and a value to convert. There are a couple of variants for efficiency.
-
-static inline void cvtCDataToStr(char* dstp, CData value) {
-#ifdef VL_HAVE_SSE2
-    // Similar to cvtSDataToStr but only the bottom 8 byte lanes are used
-    const __m128i a = _mm_cvtsi32_si128(value);
-    const __m128i b = _mm_unpacklo_epi8(a, a);
-    const __m128i c = _mm_shufflelo_epi16(b, 0);
-    const __m128i m = _mm_set1_epi64x(0x0102040810204080);
-    const __m128i d = _mm_cmpeq_epi8(_mm_and_si128(c, m), m);
-    const __m128i result = _mm_sub_epi8(_mm_set1_epi8('0'), d);
-    _mm_storel_epi64(reinterpret_cast<__m128i*>(dstp), result);
-#else
-    dstp[0] = '0' | static_cast((value >> 7) & 1);
-    dstp[1] = '0' | static_cast((value >> 6) & 1);
-    dstp[2] = '0' | static_cast((value >> 5) & 1);
-    dstp[3] = '0' | static_cast((value >> 4) & 1);
-    dstp[4] = '0' | static_cast((value >> 3) & 1);
-    dstp[5] = '0' | static_cast((value >> 2) & 1);
-    dstp[6] = '0' | static_cast((value >> 1) & 1);
-    dstp[7] = '0' | static_cast(value & 1);
-#endif
-}
-
-static inline void cvtSDataToStr(char* dstp, SData value) {
-#ifdef VL_HAVE_SSE2
-    // We want each bit in the 16-bit input value to end up in a byte lane
-    // within the 128-bit XMM register. Note that x86 is little-endian and we
-    // want the MSB of the input at the low address, so we will bit-reverse
-    // at the same time.
-
-    // Put value in bottom of 128-bit register a[15:0] = value
-    const __m128i a = _mm_cvtsi32_si128(value);
-    // Interleave bytes with themselves
-    // b[15: 0] = {2{a[ 7:0]}} == {2{value[ 7:0]}}
-    // b[31:16] = {2{a[15:8]}} == {2{value[15:8]}}
-    const __m128i b = _mm_unpacklo_epi8(a, a);
-    // Shuffle bottom 64 bits, note swapping high bytes with low bytes
-    // c[31: 0] = {2{b[31:16]}} == {4{value[15:8}}
-    // c[63:32] = {2{b[15: 0]}} == {4{value[ 7:0}}
-    const __m128i c = _mm_shufflelo_epi16(b, 0x05);
-    // Shuffle whole register
-    // d[ 63: 0] = {2{c[31: 0]}} == {8{value[15:8}}
-    // d[126:54] = {2{c[63:32]}} == {8{value[ 7:0}}
-    const __m128i d = _mm_shuffle_epi32(c, 0x50);
-    // Test each bit within the bytes, this sets each byte lane to 0
-    // if the bit for that lane is 0 and to 0xff if the bit is 1.
-    const __m128i m = _mm_set1_epi64x(0x0102040810204080);
-    const __m128i e = _mm_cmpeq_epi8(_mm_and_si128(d, m), m);
-    // Convert to ASCII by subtracting the masks from ASCII '0':
-    // '0' - 0 is '0', '0' - -1 is '1'
-    const __m128i result = _mm_sub_epi8(_mm_set1_epi8('0'), e);
-    // Store the 16 characters to the un-aligned buffer
-    _mm_storeu_si128(reinterpret_cast<__m128i*>(dstp), result);
-#else
-    cvtCDataToStr(dstp, value >> 8);
-    cvtCDataToStr(dstp + 8, value);
-#endif
-}
-
-static inline void cvtIDataToStr(char* dstp, IData value) {
-#ifdef VL_HAVE_AVX2
-    // Similar to cvtSDataToStr but the bottom 16-bits are processed in the
-    // top half of the YMM registers
-    const __m256i a = _mm256_insert_epi32(_mm256_undefined_si256(), value, 0);
-    const __m256i b = _mm256_permute4x64_epi64(a, 0);
-    const __m256i s = _mm256_set_epi8(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2,
-                                      2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3);
-    const __m256i c = _mm256_shuffle_epi8(b, s);
-    const __m256i m = _mm256_set1_epi64x(0x0102040810204080);
-    const __m256i d = _mm256_cmpeq_epi8(_mm256_and_si256(c, m), m);
-    const __m256i result = _mm256_sub_epi8(_mm256_set1_epi8('0'), d);
-    _mm256_storeu_si256(reinterpret_cast<__m256i*>(dstp), result);
-#else
-    cvtSDataToStr(dstp, value >> 16);
-    cvtSDataToStr(dstp + 16, value);
-#endif
-}
-
-static inline void cvtQDataToStr(char* dstp, QData value) {
-    cvtIDataToStr(dstp, value >> 32);
-    cvtIDataToStr(dstp + 32, value);
-}
-
-#define cvtEDataToStr cvtIDataToStr
-
-//=========================================================================
-// VerilatedTraceBuffer
-
-template <>
-VerilatedTraceBuffer::VerilatedTraceBuffer(Trace& owner)
-    : VL_BUF_T{owner}
-    , m_sigs_oldvalp{owner.m_sigs_oldvalp}
-    , m_sigs_enabledp{owner.m_sigs_enabledp} {}
-
-// These functions must write the new value back into the old value store,
-// and subsequently call the format-specific emit* implementations. Note
-// that this file must be included in the format-specific implementation, so
-// the emit* functions can be inlined for performance.
-
-template <>
-void VerilatedTraceBuffer::fullBit(uint32_t* oldp, CData newval) {
-    const uint32_t code = oldp - m_sigs_oldvalp;
-    *oldp = newval;  // Still copy even if not tracing so chg doesn't call full
-    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
-    emitBit(code, newval);
-}
-
-template <>
-void VerilatedTraceBuffer::fullEvent(uint32_t* oldp, const VlEventBase* newvalp) {
-    const uint32_t code = oldp - m_sigs_oldvalp;
-    // No need to update *oldp
-    if (newvalp->isTriggered()) emitEvent(code);
-}
-
-template <>
-void VerilatedTraceBuffer::fullEventTriggered(uint32_t* oldp) {
-    const uint32_t code = oldp - m_sigs_oldvalp;
-    // No need to update *oldp
-    emitEvent(code);
-}
-
-template <>
-void VerilatedTraceBuffer::fullCData(uint32_t* oldp, CData newval, int bits) {
-    const uint32_t code = oldp - m_sigs_oldvalp;
-    *oldp = newval;  // Still copy even if not tracing so chg doesn't call full
-    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
-    emitCData(code, newval, bits);
-}
-
-template <>
-void VerilatedTraceBuffer::fullSData(uint32_t* oldp, SData newval, int bits) {
-    const uint32_t code = oldp - m_sigs_oldvalp;
-    *oldp = newval;  // Still copy even if not tracing so chg doesn't call full
-    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
-    emitSData(code, newval, bits);
-}
-
-template <>
-void VerilatedTraceBuffer::fullIData(uint32_t* oldp, IData newval, int bits) {
-    const uint32_t code = oldp - m_sigs_oldvalp;
-    *oldp = newval;  // Still copy even if not tracing so chg doesn't call full
-    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
-    emitIData(code, newval, bits);
-}
-
-template <>
-void VerilatedTraceBuffer::fullQData(uint32_t* oldp, QData newval, int bits) {
-    const uint32_t code = oldp - m_sigs_oldvalp;
-    std::memcpy(oldp, &newval, sizeof(newval));
-    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
-    emitQData(code, newval, bits);
-}
-
-template <>
-void VerilatedTraceBuffer::fullWData(uint32_t* oldp, const WData* newvalp, int bits) {
-    const uint32_t code = oldp - m_sigs_oldvalp;
-    for (int i = 0; i < VL_WORDS_I(bits); ++i) oldp[i] = newvalp[i];
-    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
-    emitWData(code, newvalp, bits);
-}
-
-template <>
-void VerilatedTraceBuffer::fullDouble(uint32_t* oldp, double newval) {
-    const uint32_t code = oldp - m_sigs_oldvalp;
-    std::memcpy(oldp, &newval, sizeof(newval));
-    if (VL_UNLIKELY(m_sigs_enabledp && !(VL_BITISSET_W(m_sigs_enabledp, code)))) return;
-    // cppcheck-suppress invalidPointerCast
-    emitDouble(code, newval);
-}
-
-//=========================================================================
-// VerilatedTraceOffloadBuffer
-
-template <>
-VerilatedTraceOffloadBuffer::VerilatedTraceOffloadBuffer(VL_SUB_T& owner)
-    : VerilatedTraceBuffer{owner}
-    , m_offloadBufferWritep{owner.m_offloadBufferWritep}
-    , m_offloadBufferEndp{owner.m_offloadBufferEndp} {
-    if (m_offloadBufferWritep) {
-        using This = VerilatedTraceBuffer*;
-        // Tack on the buffer address
-        static_assert(2 * sizeof(uint32_t) >= sizeof(This),
-                      "This should be enough on all platforms");
-        *m_offloadBufferWritep++ = VerilatedTraceOffloadCommand::TRACE_BUFFER;
-        *reinterpret_cast(m_offloadBufferWritep) = static_cast(this);
-        m_offloadBufferWritep += 2;
-    }
-}
-
-#endif  // VL_CPPCHECK
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_types.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_types.h
deleted file mode 100644
index da8c94977a0..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_types.h
+++ /dev/null
@@ -1,2139 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilated common data type containers
-///
-/// verilated.h should be included instead of this file.
-///
-/// Those macro/function/variable starting or ending in _ are internal,
-/// however many of the other function/macros here are also internal.
-///
-//*************************************************************************
-
-#ifndef VERILATOR_VERILATED_TYPES_H_
-#define VERILATOR_VERILATED_TYPES_H_
-
-#ifndef VERILATOR_VERILATED_H_INTERNAL_
-#error "verilated_types.h should only be included by verilated.h"
-#endif
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-class VlProcess;
-template 
-class VlUnpacked;
-
-//=========================================================================
-// Debug functions
-
-#ifdef VL_DEBUG
-/// Evaluate statement if VL_DEBUG defined
-#define VL_DEBUG_IFDEF(stmt) \
-    do { stmt } while (false)
-/// Evaluate statement if VL_DEBUG defined and Verilated::debug() enabled
-#define VL_DEBUG_IF(stmt) \
-    do { \
-        if (VL_UNLIKELY(Verilated::debug())) { stmt } \
-    } while (false)
-#else
-// We intentionally do not compile the stmt to improve compile speed
-#define VL_DEBUG_IFDEF(stmt) \
-    do { \
-    } while (false)
-#define VL_DEBUG_IF(stmt) \
-    do { \
-    } while (false)
-#endif
-
-//===================================================================
-// String formatters (required by below containers)
-
-extern std::string VL_TO_STRING(CData lhs);
-extern std::string VL_TO_STRING(SData lhs);
-extern std::string VL_TO_STRING(IData lhs);
-extern std::string VL_TO_STRING(QData lhs);
-extern std::string VL_TO_STRING(double lhs);
-inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
-extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
-
-//=========================================================================
-// Declare net data types
-
-#define VL_SIG8(name, msb, lsb) CData name  ///< Declare signal, 1-8 bits
-#define VL_SIG16(name, msb, lsb) SData name  ///< Declare signal, 9-16 bits
-#define VL_SIG64(name, msb, lsb) QData name  ///< Declare signal, 33-64 bits
-#define VL_SIG(name, msb, lsb) IData name  ///< Declare signal, 17-32 bits
-#define VL_SIGW(name, msb, lsb, words) VlWide name  ///< Declare signal, 65+ bits
-#define VL_IN8(name, msb, lsb) CData name  ///< Declare input signal, 1-8 bits
-#define VL_IN16(name, msb, lsb) SData name  ///< Declare input signal, 9-16 bits
-#define VL_IN64(name, msb, lsb) QData name  ///< Declare input signal, 33-64 bits
-#define VL_IN(name, msb, lsb) IData name  ///< Declare input signal, 17-32 bits
-#define VL_INW(name, msb, lsb, words) VlWide name  ///< Declare input signal, 65+ bits
-#define VL_INOUT8(name, msb, lsb) CData name  ///< Declare bidir signal, 1-8 bits
-#define VL_INOUT16(name, msb, lsb) SData name  ///< Declare bidir signal, 9-16 bits
-#define VL_INOUT64(name, msb, lsb) QData name  ///< Declare bidir signal, 33-64 bits
-#define VL_INOUT(name, msb, lsb) IData name  ///< Declare bidir signal, 17-32 bits
-#define VL_INOUTW(name, msb, lsb, words) VlWide name  ///< Declare bidir signal, 65+ bits
-#define VL_OUT8(name, msb, lsb) CData name  ///< Declare output signal, 1-8 bits
-#define VL_OUT16(name, msb, lsb) SData name  ///< Declare output signal, 9-16 bits
-#define VL_OUT64(name, msb, lsb) QData name  ///< Declare output signal, 33-64 bits
-#define VL_OUT(name, msb, lsb) IData name  ///< Declare output signal, 17-32 bits
-#define VL_OUTW(name, msb, lsb, words) VlWide name  ///< Declare output signal, 65+ bits
-
-//===================================================================
-// Functions needed here
-
-constexpr IData VL_CLOG2_CE_Q(QData lhs) VL_PURE {
-    // constexpr usage only! Recuses to meet C++11 constexpr func limitations
-    return lhs <= 1 ? 0 : VL_CLOG2_CE_Q((lhs + 1) >> 1ULL) + 1;
-}
-
-// Metadata of processes
-using VlProcessRef = std::shared_ptr;
-
-class VlProcess final {
-    // MEMBERS
-    int m_state;  // Current state of the process
-    VlProcessRef m_parentp = nullptr;  // Parent process, if exists
-    std::set m_children;  // Active child processes
-
-public:
-    // TYPES
-    enum : int {  // Type int for compatibility with $c
-        FINISHED = 0,
-        RUNNING = 1,
-        WAITING = 2,
-        SUSPENDED = 3,
-        KILLED = 4,
-    };
-
-    // CONSTRUCTORS
-    // Construct independent process
-    VlProcess()
-        : m_state{RUNNING} {}
-    // Construct child process of parent
-    explicit VlProcess(VlProcessRef parentp)
-        : m_state{RUNNING}
-        , m_parentp{parentp} {
-        m_parentp->attach(this);
-    }
-
-    ~VlProcess() {
-        if (m_parentp) m_parentp->detach(this);
-    }
-
-    void attach(VlProcess* childp) { m_children.insert(childp); }
-    void detach(VlProcess* childp) { m_children.erase(childp); }
-
-    int state() const { return m_state; }
-    void state(int s) { m_state = s; }
-    void disable() {
-        state(KILLED);
-        disableFork();
-    }
-    void disableFork() {
-        for (VlProcess* childp : m_children) childp->disable();
-    }
-    bool completed() const { return state() == FINISHED || state() == KILLED; }
-    bool completedFork() const {
-        for (const VlProcess* const childp : m_children)
-            if (!childp->completed()) return false;
-        return true;
-    }
-
-    std::string randstate() const VL_MT_UNSAFE;
-    void randstate(const std::string& state) VL_MT_UNSAFE;
-};
-
-inline std::string VL_TO_STRING(const VlProcessRef& p) { return std::string("process"); }
-
-//===================================================================
-// SystemVerilog event type
-
-class VlEventBase VL_NOT_FINAL {
-public:
-    virtual ~VlEventBase() = default;
-
-    virtual void fire() = 0;
-    virtual bool isFired() const = 0;
-    virtual bool isTriggered() const = 0;
-    virtual void clearFired() = 0;
-    virtual void clearTriggered() = 0;
-};
-
-class VlEvent final : public VlEventBase {
-    // MEMBERS
-    bool m_fired = false;  // Fired on this scheduling iteration
-    bool m_triggered = false;  // Triggered state of event persisting until next time step
-
-public:
-    // CONSTRUCTOR
-    VlEvent() = default;
-    ~VlEvent() override = default;
-
-    friend std::string VL_TO_STRING(const VlEvent& e);
-    friend class VlAssignableEvent;
-    // METHODS
-    void fire() override { m_fired = m_triggered = true; }
-    bool isFired() const override { return m_fired; }
-    bool isTriggered() const override { return m_triggered; }
-    void clearFired() override { m_fired = false; }
-    void clearTriggered() override { m_triggered = false; }
-};
-
-class VlAssignableEvent final : public std::shared_ptr, public VlEventBase {
-public:
-    // Constructor
-    VlAssignableEvent()
-        : std::shared_ptr(new VlEvent) {}
-    ~VlAssignableEvent() override = default;
-
-    // METHODS
-    void fire() override { (*this)->m_fired = (*this)->m_triggered = true; }
-    bool isFired() const override { return (*this)->m_fired; }
-    bool isTriggered() const override { return (*this)->m_triggered; }
-    void clearFired() override { (*this)->m_fired = false; }
-    void clearTriggered() override { (*this)->m_triggered = false; }
-};
-
-inline std::string VL_TO_STRING(const VlEventBase& e);
-
-inline std::string VL_TO_STRING(const VlEvent& e) {
-    return "triggered="s + (e.isTriggered() ? "true" : "false");
-}
-
-inline std::string VL_TO_STRING(const VlAssignableEvent& e) {
-    return "&{ " + VL_TO_STRING(*e) + " }";
-}
-
-inline std::string VL_TO_STRING(const VlEventBase& e) {
-    if (const VlAssignableEvent& assignable = dynamic_cast(e)) {
-        return VL_TO_STRING(assignable);
-    }
-    return "triggered="s + (e.isTriggered() ? "true" : "false");
-}
-
-//===================================================================
-// Random
-
-// Random Number Generator with internal state
-class VlRNG final {
-    std::array m_state;
-
-public:
-    // The default constructor simply sets state, to avoid vl_rand64()
-    // having to check for construction at each call
-    // Alternative: seed with zero and check on rand64() call
-    VlRNG() VL_MT_SAFE;
-    explicit VlRNG(uint64_t seed) VL_PURE;
-    void srandom(uint64_t n) VL_MT_UNSAFE;
-    std::string get_randstate() const VL_MT_UNSAFE;
-    void set_randstate(const std::string& state) VL_MT_UNSAFE;
-    uint64_t rand64() VL_MT_UNSAFE;
-    // Threadsafe, but requires use on vl_thread_rng
-    static uint64_t vl_thread_rng_rand64() VL_MT_SAFE;
-    static VlRNG& vl_thread_rng() VL_MT_SAFE;
-};
-
-inline uint64_t vl_rand64() VL_MT_SAFE { return VlRNG::vl_thread_rng_rand64(); }
-
-// RNG for shuffle()
-class VlURNG final {
-public:
-    using result_type = size_t;
-    static constexpr size_t min() { return 0; }
-    static constexpr size_t max() { return 1ULL << 31; }
-    size_t operator()() { return VL_MASK_I(31) & vl_rand64(); }
-};
-
-template 
-class VlRandC final {
-    T_Value m_remaining = 0;  // Number of values to pull before re-randomize
-    T_Value m_lfsr = 1;  // LFSR state
-
-public:
-    // CONSTRUCTORS
-    VlRandC() {
-        static_assert(N_NumValues >= 1, "");
-        static_assert(sizeof(T_Value) == 8 || (N_NumValues < (1ULL << (8 * sizeof(T_Value)))), "");
-    }
-    // METHODS
-    T_Value randomize(VlRNG& rngr) {
-        if (VL_UNLIKELY(!m_remaining)) reseed(rngr);
-        // Polynomials are first listed at https://users.ece.cmu.edu/~koopman/lfsr/
-        static constexpr uint64_t s_polynomials[] = {
-            0x0ULL,  // 0 never used (constant, no randomization)
-            0x0ULL,  // 1
-            0x3ULL,        0x5ULL,       0x9ULL,        0x12ULL,       0x21ULL,
-            0x41ULL,       0x8eULL,      0x108ULL,      0x204ULL,      0x402ULL,
-            0x829ULL,      0x100dULL,    0x2015ULL,     0x4001ULL,
-            0x8016ULL,  // 16
-            0x10004ULL,    0x20040ULL,   0x40013ULL,    0x80004ULL,    0x100002ULL,
-            0x200001ULL,   0x400010ULL,  0x80000dULL,   0x1000004ULL,  0x2000023ULL,
-            0x4000013ULL,  0x8000004ULL, 0x10000002ULL, 0x20000029ULL, 0x40000004ULL,
-            0x80000057ULL,  // 32
-            0x100000029ULL  // 33
-        };
-        constexpr uint32_t clogWidth = VL_CLOG2_CE_Q(N_NumValues) + 1;
-        constexpr uint32_t lfsrWidth = (clogWidth < 2) ? 2 : clogWidth;
-        constexpr T_Value polynomial = static_cast(s_polynomials[lfsrWidth]);
-        // printf("  numV=%ld w=%d poly=%x\n", N_NumValues, lfsrWidth, polynomial);
-        //  Loop until get reasonable value. Because we picked a LFSR of at most one
-        //  extra bit in width, this will only require at most on average 1.5 loops
-        do {
-            m_lfsr = (m_lfsr & 1ULL) ? ((m_lfsr >> 1ULL) ^ polynomial) : (m_lfsr >> 1ULL);
-        } while (m_lfsr > N_NumValues);  // Note if == then output value 0
-        --m_remaining;
-        T_Value result = (m_lfsr == N_NumValues) ? 0 : m_lfsr;
-        // printf("    result=%x  (numv=%ld, rem=%d)\n", result, N_NumValues, m_remaining);
-        return result;
-    }
-    void reseed(VlRNG& rngr) {
-        constexpr uint32_t lfsrWidth = VL_CLOG2_CE_Q(N_NumValues) + 1;
-        m_remaining = N_NumValues;
-        do {
-            m_lfsr = rngr.rand64() & VL_MASK_Q(lfsrWidth);
-            // printf("    lfsr.reseed=%x\n", m_lfsr);
-        } while (!m_lfsr);  // 0 not a legal seed
-    }
-};
-
-// These require the class object to have the thread safety lock
-inline IData VL_RANDOM_RNG_I(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); }
-inline QData VL_RANDOM_RNG_Q(VlRNG& rngr) VL_MT_UNSAFE { return rngr.rand64(); }
-extern double VL_RANDOM_RNG_D(VlRNG& rngr) VL_MT_UNSAFE;
-extern WDataOutP VL_RANDOM_RNG_W(VlRNG& rngr, int obits, WDataOutP outwp) VL_MT_UNSAFE;
-
-//===================================================================
-// Readmem/Writemem operation classes
-
-class VlReadMem final {
-    const bool m_hex;  // Hex format
-    const int m_bits;  // Bit width of values
-    const std::string& m_filename;  // Filename
-    const QData m_end;  // End address (as specified by user)
-    FILE* m_fp = nullptr;  // File handle for filename
-    QData m_addr = 0;  // Next address to read
-    int m_linenum = 0;  // Line number last read from file
-    bool m_anyAddr = false;  // Had address directive in the file
-public:
-    VlReadMem(bool hex, int bits, const std::string& filename, QData start, QData end);
-    ~VlReadMem();
-    bool isOpen() const { return m_fp != nullptr; }
-    int linenum() const { return m_linenum; }
-    bool get(QData& addrr, std::string& valuer);
-    void setData(void* valuep, const std::string& rhs);
-};
-
-class VlWriteMem final {
-    const bool m_hex;  // Hex format
-    const int m_bits;  // Bit width of values
-    FILE* m_fp = nullptr;  // File handle for filename
-    QData m_addr = 0;  // Next address to write
-public:
-    VlWriteMem(bool hex, int bits, const std::string& filename, QData start, QData end);
-    ~VlWriteMem();
-    bool isOpen() const { return m_fp != nullptr; }
-    void print(QData addr, bool addrstamp, const void* valuep);
-};
-
-//===================================================================
-/// Verilog wide packed bit container.
-/// Similar to std::array, but lighter weight, only methods needed
-/// by Verilator, to help compile time.
-///
-/// A 'struct' as we want this to be an aggregate type that allows
-/// static aggregate initialization. Consider data members private.
-///
-/// For example a Verilog "bit [94:0]" will become a VlWide<3> because 3*32
-/// bits are needed to hold the 95 bits. The MSB (bit 96) must always be
-/// zero in memory, but during intermediate operations in the Verilated
-/// internals is unpredictable.
-
-static int _vl_cmp_w(int words, WDataInP const lwp, WDataInP const rwp) VL_PURE;
-
-template 
-struct VlWide;
-
-// Type trait to check if a type is VlWide
-template 
-struct VlIsVlWide : public std::false_type {};
-
-template 
-struct VlIsVlWide> : public std::true_type {};
-
-template 
-struct VlWide final {
-    static constexpr size_t Words = N_Words;
-
-    // MEMBERS
-    // This should be the only data member, otherwise generated static initializers need updating
-    EData m_storage[N_Words];  // Contents of the packed array
-
-    // CONSTRUCTORS
-    // Default constructors and destructor are used. Note however that C++20 requires that
-    // aggregate types do not have a user declared constructor, not even an explicitly defaulted
-    // one.
-
-    // OPERATOR METHODS
-    // Default copy assignment operators are used.
-    operator WDataOutP() VL_PURE { return &m_storage[0]; }  // This also allows []
-    operator WDataInP() const VL_PURE { return &m_storage[0]; }  // This also allows []
-    bool operator!=(const VlWide& that) const VL_PURE {
-        for (size_t i = 0; i < N_Words; ++i) {
-            if (m_storage[i] != that.m_storage[i]) return true;
-        }
-        return false;
-    }
-
-    // METHODS
-    const EData& at(size_t index) const { return m_storage[index]; }
-    EData& at(size_t index) { return m_storage[index]; }
-    size_t size() const { return N_Words; }
-    WData* data() { return &m_storage[0]; }
-    const WData* data() const { return &m_storage[0]; }
-    bool operator<(const VlWide& rhs) const {
-        return _vl_cmp_w(N_Words, data(), rhs.data()) < 0;
-    }
-};
-
-// Convert a C array to std::array reference by pointer magic, without copy.
-// Data type (second argument) is so the function template can automatically generate.
-template 
-VlWide& VL_CVT_W_A(const WDataInP inp, const VlWide&) {
-    return *((VlWide*)inp);
-}
-
-template 
-std::string VL_TO_STRING(const VlWide& obj) {
-    return VL_TO_STRING_W(N_Words, obj.data());
-}
-
-//===================================================================
-// Verilog queue and dynamic array container
-// There are no multithreaded locks on this; the base variable must
-// be protected by other means
-//
-// Bound here is the maximum size() allowed, e.g. 1 + SystemVerilog bound
-// For dynamic arrays it is always zero
-template 
-class VlQueue final {
-private:
-    // TYPES
-    using Deque = std::deque;
-
-public:
-    using const_iterator = typename Deque::const_iterator;
-    template 
-    using WithFuncReturnType = decltype(std::declval()(0, std::declval()));
-
-private:
-    // MEMBERS
-    Deque m_deque;  // State of the assoc array
-    T_Value m_defaultValue{};  // Default value
-
-public:
-    // CONSTRUCTORS
-    // cppcheck-suppress uninitMemberVar // m_defaultValue isn't defaulted, caller must
-    VlQueue() = default;
-    ~VlQueue() = default;
-    VlQueue(const VlQueue&) = default;
-    VlQueue(VlQueue&&) = default;
-    VlQueue& operator=(const VlQueue&) = default;
-    VlQueue& operator=(VlQueue&&) = default;
-    bool operator==(const VlQueue& rhs) const { return m_deque == rhs.m_deque; }
-    bool operator!=(const VlQueue& rhs) const { return m_deque != rhs.m_deque; }
-    bool operator<(const VlQueue& rhs) const {
-        for (int index = 0; index < m_deque.size(); ++index) {
-            if (m_deque[index] < rhs.m_deque[index]) return true;
-        }
-        return false;
-    }
-
-    // Standard copy constructor works. Verilog: assoca = assocb
-    // Also must allow conversion from a different N_MaxSize queue
-    template 
-    VlQueue operator=(const VlQueue& rhs) {
-        m_deque = rhs.privateDeque();
-        if (VL_UNLIKELY(N_MaxSize && N_MaxSize < m_deque.size())) m_deque.resize(N_MaxSize - 1);
-        return *this;
-    }
-
-    // Construct new object from _V_alue and/or _C_ontainer child objects
-    static VlQueue consV(const T_Value& lhs) {
-        VlQueue out;
-        out.push_back(lhs);
-        return out;
-    }
-    static VlQueue consVV(const T_Value& lhs, const T_Value& rhs) {
-        VlQueue out;
-        out.push_back(rhs);
-        out.push_back(lhs);
-        return out;
-    }
-    static VlQueue consCV(const VlQueue& lhs, const T_Value& rhs) {
-        VlQueue out = lhs;
-        out.push_front(rhs);
-        return out;
-    }
-    static VlQueue consVC(const T_Value& lhs, const VlQueue& rhs) {
-        VlQueue out = rhs;
-        out.push_back(lhs);
-        return out;
-    }
-    static VlQueue consCC(const VlQueue& lhs, const VlQueue& rhs) {
-        VlQueue out = rhs;
-        for (const auto& i : lhs.m_deque) out.push_back(i);
-        return out;
-    }
-
-    // METHODS
-    T_Value& atDefault() { return m_defaultValue; }
-    const T_Value& atDefault() const { return m_defaultValue; }
-    const Deque& privateDeque() const { return m_deque; }
-
-    // Size. Verilog: function int size(), or int num()
-    int size() const { return m_deque.size(); }
-    // Clear array. Verilog: function void delete([input index])
-    void clear() { m_deque.clear(); }
-    void erase(int32_t index) {
-        if (VL_LIKELY(index >= 0 && index < m_deque.size()))
-            m_deque.erase(m_deque.begin() + index);
-    }
-
-    // Dynamic array new[] becomes a renew()
-    void renew(size_t size) {
-        clear();
-        m_deque.resize(size, atDefault());
-    }
-    // Dynamic array new[]() becomes a renew_copy()
-    void renew_copy(size_t size, const VlQueue& rhs) {
-        if (size == 0) {
-            clear();
-        } else {
-            *this = rhs;
-            m_deque.resize(size, atDefault());
-        }
-    }
-    // Unpacked array new[]() becomes a renew_copy()
-    template 
-    void renew_copy(size_t size, const VlUnpacked& rhs);
-
-    void resize(size_t size) { m_deque.resize(size, atDefault()); }
-
-    // function void q.push_front(value)
-    void push_front(const T_Value& value) {
-        m_deque.push_front(value);
-        if (VL_UNLIKELY(N_MaxSize != 0 && m_deque.size() > N_MaxSize)) m_deque.pop_back();
-    }
-    // function void q.push_back(value)
-    void push_back(const T_Value& value) {
-        if (VL_LIKELY(N_MaxSize == 0 || m_deque.size() < N_MaxSize)) m_deque.push_back(value);
-    }
-    // function value_t q.pop_front();
-    T_Value pop_front() {
-        if (m_deque.empty()) return m_defaultValue;
-        T_Value v = m_deque.front();
-        m_deque.pop_front();
-        return v;
-    }
-    // function value_t q.pop_back();
-    T_Value pop_back() {
-        if (m_deque.empty()) return m_defaultValue;
-        T_Value v = m_deque.back();
-        m_deque.pop_back();
-        return v;
-    }
-
-    // Setting. Verilog: assoc[index] = v (should only be used by dynamic arrays)
-    T_Value& atWrite(int32_t index) {
-        // cppcheck-suppress variableScope
-        static thread_local T_Value t_throwAway;
-        // Needs to work for dynamic arrays, so does not use N_MaxSize
-        if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
-            t_throwAway = atDefault();
-            return t_throwAway;
-        }
-        return m_deque[index];
-    }
-    // Setting. Verilog: assoc[index] = v (should only be used by queues)
-    T_Value& atWriteAppend(int32_t index) {
-        // cppcheck-suppress variableScope
-        static thread_local T_Value t_throwAway;
-        if (index == m_deque.size()) push_back(atDefault());
-        if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
-            t_throwAway = atDefault();
-            return t_throwAway;
-        }
-        return m_deque[index];
-    }
-    // Accessing. Verilog: v = assoc[index]
-    const T_Value& at(int32_t index) const {
-        // Needs to work for dynamic arrays, so does not use N_MaxSize
-        if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
-            return atDefault();
-        } else {
-            return m_deque[index];
-        }
-    }
-    // Access with an index counted from end (e.g. q[$])
-    T_Value& atWriteAppendBack(int32_t index) { return atWriteAppend(m_deque.size() - 1 - index); }
-    const T_Value& atBack(int32_t index) const { return at(m_deque.size() - 1 - index); }
-
-    // function void q.insert(index, value);
-    void insert(int32_t index, const T_Value& value) {
-        if (VL_UNLIKELY(index < 0 || index > m_deque.size())) return;
-        m_deque.insert(m_deque.begin() + index, value);
-    }
-
-    // inside (set membership operator)
-    bool inside(const T_Value& value) const {
-        return std::find(m_deque.cbegin(), m_deque.cend(), value) != m_deque.cend();
-    }
-
-    // Return slice q[lsb:msb]
-    VlQueue slice(int32_t lsb, int32_t msb) const {
-        VlQueue out;
-        if (VL_UNLIKELY(lsb < 0)) lsb = 0;
-        if (VL_UNLIKELY(lsb >= m_deque.size())) lsb = m_deque.size() - 1;
-        if (VL_UNLIKELY(msb >= m_deque.size())) msb = m_deque.size() - 1;
-        for (int32_t i = lsb; i <= msb; ++i) out.push_back(m_deque[i]);
-        return out;
-    }
-    VlQueue sliceFrontBack(int32_t lsb, int32_t msb) const {
-        return slice(lsb, m_deque.size() - 1 - msb);
-    }
-    VlQueue sliceBackBack(int32_t lsb, int32_t msb) const {
-        return slice(m_deque.size() - 1 - lsb, m_deque.size() - 1 - msb);
-    }
-
-    // For save/restore
-    const_iterator begin() const { return m_deque.begin(); }
-    const_iterator end() const { return m_deque.end(); }
-
-    // Methods
-    void sort() { std::sort(m_deque.begin(), m_deque.end()); }
-    template 
-    void sort(T_Func with_func) {
-        // with_func returns arbitrary type to use for the sort comparison
-        std::sort(m_deque.begin(), m_deque.end(), [=](const T_Value& a, const T_Value& b) {
-            // index number is meaningless with sort, as it changes
-            return with_func(0, a) < with_func(0, b);
-        });
-    }
-    void rsort() { std::sort(m_deque.rbegin(), m_deque.rend()); }
-    template 
-    void rsort(T_Func with_func) {
-        // with_func returns arbitrary type to use for the sort comparison
-        std::sort(m_deque.rbegin(), m_deque.rend(), [=](const T_Value& a, const T_Value& b) {
-            // index number is meaningless with sort, as it changes
-            return with_func(0, a) < with_func(0, b);
-        });
-    }
-    void reverse() { std::reverse(m_deque.begin(), m_deque.end()); }
-    void shuffle() { std::shuffle(m_deque.begin(), m_deque.end(), VlURNG{}); }
-    VlQueue unique() const {
-        VlQueue out;
-        std::set saw;
-        for (const auto& i : m_deque) {
-            const auto it = saw.find(i);
-            if (it == saw.end()) {
-                saw.insert(it, i);
-                out.push_back(i);
-            }
-        }
-        return out;
-    }
-    template 
-    VlQueue unique(T_Func with_func) const {
-        VlQueue out;
-        std::set saw;
-        for (const auto& i : m_deque) {
-            const auto i_mapped = with_func(0, i);
-            const auto it = saw.find(i_mapped);
-            if (it == saw.end()) {
-                saw.insert(it, i_mapped);
-                out.push_back(i);
-            }
-        }
-        return out;
-    }
-    VlQueue unique_index() const {
-        VlQueue out;
-        IData index = 0;
-        std::set saw;
-        for (const auto& i : m_deque) {
-            const auto it = saw.find(i);
-            if (it == saw.end()) {
-                saw.insert(it, i);
-                out.push_back(index);
-            }
-            ++index;
-        }
-        return out;
-    }
-    template 
-    VlQueue unique_index(T_Func with_func) const {
-        VlQueue out;
-        IData index = 0;
-        std::set saw;
-        for (const auto& i : m_deque) {
-            const auto i_mapped = with_func(index, i);
-            auto it = saw.find(i_mapped);
-            if (it == saw.end()) {
-                saw.insert(it, i_mapped);
-                out.push_back(index);
-            }
-            ++index;
-        }
-        return out;
-    }
-    template 
-    VlQueue find(T_Func with_func) const {
-        VlQueue out;
-        IData index = 0;
-        for (const auto& i : m_deque) {
-            if (with_func(index, i)) out.push_back(i);
-            ++index;
-        }
-        return out;
-    }
-    template 
-    VlQueue find_index(T_Func with_func) const {
-        VlQueue out;
-        IData index = 0;
-        for (const auto& i : m_deque) {
-            if (with_func(index, i)) out.push_back(index);
-            ++index;
-        }
-        return out;
-    }
-    template 
-    VlQueue find_first(T_Func with_func) const {
-        // Can't use std::find_if as need index number
-        IData index = 0;
-        for (const auto& i : m_deque) {
-            if (with_func(index, i)) return VlQueue::consV(i);
-            ++index;
-        }
-        return VlQueue{};
-    }
-    template 
-    VlQueue find_first_index(T_Func with_func) const {
-        IData index = 0;
-        for (const auto& i : m_deque) {
-            if (with_func(index, i)) return VlQueue::consV(index);
-            ++index;
-        }
-        return VlQueue{};
-    }
-    template 
-    VlQueue find_last(T_Func with_func) const {
-        IData index = m_deque.size() - 1;
-        for (auto& item : vlstd::reverse_view(m_deque)) {
-            if (with_func(index, item)) return VlQueue::consV(item);
-            --index;
-        }
-        return VlQueue{};
-    }
-    template 
-    VlQueue find_last_index(T_Func with_func) const {
-        IData index = m_deque.size() - 1;
-        for (auto& item : vlstd::reverse_view(m_deque)) {
-            if (with_func(index, item)) return VlQueue::consV(index);
-            --index;
-        }
-        return VlQueue{};
-    }
-
-    // Reduction operators
-    VlQueue min() const {
-        if (m_deque.empty()) return VlQueue{};
-        const auto it = std::min_element(m_deque.cbegin(), m_deque.cend());
-        return VlQueue::consV(*it);
-    }
-    template 
-    VlQueue min(T_Func with_func) const {
-        if (m_deque.empty()) return VlQueue{};
-        const auto it = std::min_element(m_deque.cbegin(), m_deque.cend(),
-                                         [&with_func](const IData& a, const IData& b) {
-                                             return with_func(0, a) < with_func(0, b);
-                                         });
-        return VlQueue::consV(*it);
-    }
-    VlQueue max() const {
-        if (m_deque.empty()) return VlQueue{};
-        const auto it = std::max_element(m_deque.cbegin(), m_deque.cend());
-        return VlQueue::consV(*it);
-    }
-    template 
-    VlQueue max(T_Func with_func) const {
-        if (m_deque.empty()) return VlQueue{};
-        const auto it = std::max_element(m_deque.cbegin(), m_deque.cend(),
-                                         [&with_func](const IData& a, const IData& b) {
-                                             return with_func(0, a) < with_func(0, b);
-                                         });
-        return VlQueue::consV(*it);
-    }
-
-    T_Value r_sum() const {
-        T_Value out(0);  // Type must have assignment operator
-        for (const auto& i : m_deque) out += i;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_sum(T_Func with_func) const {
-        WithFuncReturnType out = WithFuncReturnType(0);
-        IData index = 0;
-        for (const auto& i : m_deque) out += with_func(index++, i);
-        return out;
-    }
-    T_Value r_product() const {
-        if (m_deque.empty()) return T_Value(0);  // The big three do it this way
-        T_Value out = T_Value(1);
-        for (const auto& i : m_deque) out *= i;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_product(T_Func with_func) const {
-        if (m_deque.empty()) return WithFuncReturnType(0);  // The big three do it this way
-        WithFuncReturnType out = WithFuncReturnType(1);
-        IData index = 0;
-        for (const auto& i : m_deque) out *= with_func(index++, i);
-        return out;
-    }
-    T_Value r_and() const {
-        if (m_deque.empty()) return T_Value(0);  // The big three do it this way
-        T_Value out = ~T_Value(0);
-        for (const auto& i : m_deque) out &= i;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_and(T_Func with_func) const {
-        if (m_deque.empty()) return WithFuncReturnType(0);  // The big three do it this way
-        IData index = 0;
-        WithFuncReturnType out = ~WithFuncReturnType(0);
-        for (const auto& i : m_deque) out &= with_func(index++, i);
-        return out;
-    }
-    T_Value r_or() const {
-        T_Value out = T_Value(0);
-        for (const auto& i : m_deque) out |= i;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_or(T_Func with_func) const {
-        WithFuncReturnType out = WithFuncReturnType(0);
-        IData index = 0;
-        for (const auto& i : m_deque) out |= with_func(index++, i);
-        return out;
-    }
-    T_Value r_xor() const {
-#ifdef VERILATOR_BIG3_NULLARY_ARITHMETICS_QUIRKS
-        if (m_deque.empty()) return T_Value(0);
-#endif
-        T_Value out = T_Value(0);
-        for (const auto& i : m_deque) out ^= i;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_xor(T_Func with_func) const {
-        WithFuncReturnType out = WithFuncReturnType(0);
-        IData index = 0;
-        for (const auto& i : m_deque) out ^= with_func(index++, i);
-        return out;
-    }
-
-    // Dumping. Verilog: str = $sformatf("%p", assoc)
-    std::string to_string() const {
-        if (m_deque.empty()) return "'{}";  // No trailing space
-        std::string out = "'{";
-        std::string comma;
-        for (const auto& i : m_deque) {
-            out += comma + VL_TO_STRING(i);
-            comma = ", ";
-        }
-        return out + "}";
-    }
-};
-
-template 
-std::string VL_TO_STRING(const VlQueue& obj) {
-    return obj.to_string();
-}
-
-template 
-struct VlContainsCustomStruct> : VlContainsCustomStruct {};
-
-//===================================================================
-// Verilog associative array container
-// There are no multithreaded locks on this; the base variable must
-// be protected by other means
-//
-template 
-class VlAssocArray final {
-private:
-    // TYPES
-    using Map = std::map;
-
-public:
-    using const_iterator = typename Map::const_iterator;
-    template 
-    using WithFuncReturnType
-        = decltype(std::declval()(std::declval(), std::declval()));
-
-private:
-    // MEMBERS
-    Map m_map;  // State of the assoc array
-    T_Value m_defaultValue;  // Default value
-
-public:
-    // CONSTRUCTORS
-    // m_defaultValue isn't defaulted. Caller's constructor must do it.
-    VlAssocArray() = default;
-    ~VlAssocArray() = default;
-    VlAssocArray(const VlAssocArray&) = default;
-    VlAssocArray(VlAssocArray&&) = default;
-    VlAssocArray& operator=(const VlAssocArray&) = default;
-    VlAssocArray& operator=(VlAssocArray&&) = default;
-    bool operator==(const VlAssocArray& rhs) const { return m_map == rhs.m_map; }
-    bool operator!=(const VlAssocArray& rhs) const { return m_map != rhs.m_map; }
-    bool operator<(const VlAssocArray& rhs) const { return m_map < rhs.m_map; }
-    // METHODS
-    T_Value& atDefault() { return m_defaultValue; }
-    const T_Value& atDefault() const { return m_defaultValue; }
-
-    // Size of array. Verilog: function int size(), or int num()
-    int size() const { return m_map.size(); }
-    bool empty() const { return m_map.empty(); }
-    // Clear array. Verilog: function void delete([input index])
-    void clear() { m_map.clear(); }
-    void erase(const T_Key& index) { m_map.erase(index); }
-    // Return 0/1 if element exists. Verilog: function int exists(input index)
-    int exists(const T_Key& index) const { return m_map.find(index) != m_map.end(); }
-    // Return first element.  Verilog: function int first(ref index);
-    int first(T_Key& indexr) const {
-        const auto it = m_map.cbegin();
-        if (it == m_map.end()) return 0;
-        indexr = it->first;
-        return 1;
-    }
-    // Return last element.  Verilog: function int last(ref index)
-    int last(T_Key& indexr) const {
-        const auto it = m_map.crbegin();
-        if (it == m_map.crend()) return 0;
-        indexr = it->first;
-        return 1;
-    }
-    // Return next element. Verilog: function int next(ref index)
-    int next(T_Key& indexr) const {
-        auto it = m_map.find(indexr);
-        if (VL_UNLIKELY(it == m_map.end())) return 0;
-        ++it;
-        if (VL_UNLIKELY(it == m_map.end())) return 0;
-        indexr = it->first;
-        return 1;
-    }
-    // Return prev element. Verilog: function int prev(ref index)
-    int prev(T_Key& indexr) const {
-        auto it = m_map.find(indexr);
-        if (VL_UNLIKELY(it == m_map.end())) return 0;
-        if (VL_UNLIKELY(it == m_map.begin())) return 0;
-        --it;
-        indexr = it->first;
-        return 1;
-    }
-    // Setting. Verilog: assoc[index] = v
-    // Can't just overload operator[] or provide a "at" reference to set,
-    // because we need to be able to insert only when the value is set
-    T_Value& at(const T_Key& index) {
-        const auto it = m_map.find(index);
-        if (it == m_map.end()) {
-            std::pair pit = m_map.emplace(index, m_defaultValue);
-            return pit.first->second;
-        }
-        return it->second;
-    }
-    // Accessing. Verilog: v = assoc[index]
-    const T_Value& at(const T_Key& index) const {
-        const auto it = m_map.find(index);
-        if (it == m_map.end()) {
-            return m_defaultValue;
-        } else {
-            return it->second;
-        }
-    }
-    // Setting as a chained operation
-    VlAssocArray& set(const T_Key& index, const T_Value& value) {
-        at(index) = value;
-        return *this;
-    }
-    VlAssocArray& setDefault(const T_Value& value) {
-        atDefault() = value;
-        return *this;
-    }
-
-    // For save/restore
-    const_iterator begin() const { return m_map.begin(); }
-    const_iterator end() const { return m_map.end(); }
-
-    // Methods
-    VlQueue unique() const {
-        VlQueue out;
-        std::set saw;
-        for (const auto& i : m_map) {
-            auto it = saw.find(i.second);
-            if (it == saw.end()) {
-                saw.insert(it, i.second);
-                out.push_back(i.second);
-            }
-        }
-        return out;
-    }
-    template 
-    VlQueue unique(T_Func with_func) const {
-        VlQueue out;
-        T_Key default_key;
-        using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
-        std::set saw;
-        for (const auto& i : m_map) {
-            const auto i_mapped = with_func(default_key, i.second);
-            const auto it = saw.find(i_mapped);
-            if (it == saw.end()) {
-                saw.insert(it, i_mapped);
-                out.push_back(i.second);
-            }
-        }
-        return out;
-    }
-    VlQueue unique_index() const {
-        VlQueue out;
-        std::set saw;
-        for (const auto& i : m_map) {
-            auto it = saw.find(i.second);
-            if (it == saw.end()) {
-                saw.insert(it, i.second);
-                out.push_back(i.first);
-            }
-        }
-        return out;
-    }
-    template 
-    VlQueue unique_index(T_Func with_func) const {
-        VlQueue out;
-        using WithType = decltype(with_func(m_map.begin()->first, m_map.begin()->second));
-        std::set saw;
-        for (const auto& i : m_map) {
-            const auto i_mapped = with_func(i.first, i.second);
-            auto it = saw.find(i_mapped);
-            if (it == saw.end()) {
-                saw.insert(it, i_mapped);
-                out.push_back(i.first);
-            }
-        }
-        return out;
-    }
-    template 
-    VlQueue find(T_Func with_func) const {
-        VlQueue out;
-        for (const auto& i : m_map)
-            if (with_func(i.first, i.second)) out.push_back(i.second);
-        return out;
-    }
-    template 
-    VlQueue find_index(T_Func with_func) const {
-        VlQueue out;
-        for (const auto& i : m_map)
-            if (with_func(i.first, i.second)) out.push_back(i.first);
-        return out;
-    }
-    template 
-    VlQueue find_first(T_Func with_func) const {
-        const auto it
-            = std::find_if(m_map.cbegin(), m_map.cend(), [=](const std::pair& i) {
-                  return with_func(i.first, i.second);
-              });
-        if (it == m_map.end()) return VlQueue{};
-        return VlQueue::consV(it->second);
-    }
-    template 
-    VlQueue find_first_index(T_Func with_func) const {
-        const auto it
-            = std::find_if(m_map.cbegin(), m_map.cend(), [=](const std::pair& i) {
-                  return with_func(i.first, i.second);
-              });
-        if (it == m_map.end()) return VlQueue{};
-        return VlQueue::consV(it->first);
-    }
-    template 
-    VlQueue find_last(T_Func with_func) const {
-        const auto it = std::find_if(
-            m_map.crbegin(), m_map.crend(),
-            [=](const std::pair& i) { return with_func(i.first, i.second); });
-        if (it == m_map.rend()) return VlQueue{};
-        return VlQueue::consV(it->second);
-    }
-    template 
-    VlQueue find_last_index(T_Func with_func) const {
-        const auto it = std::find_if(
-            m_map.crbegin(), m_map.crend(),
-            [=](const std::pair& i) { return with_func(i.first, i.second); });
-        if (it == m_map.rend()) return VlQueue{};
-        return VlQueue::consV(it->first);
-    }
-
-    // Reduction operators
-    VlQueue min() const {
-        if (m_map.empty()) return VlQueue();
-        const auto it = std::min_element(
-            m_map.cbegin(), m_map.cend(),
-            [](const std::pair& a, const std::pair& b) {
-                return a.second < b.second;
-            });
-        return VlQueue::consV(it->second);
-    }
-    template 
-    VlQueue min(T_Func with_func) const {
-        if (m_map.empty()) return VlQueue();
-        const auto it = std::min_element(
-            m_map.cbegin(), m_map.cend(),
-            [&with_func](const std::pair& a, const std::pair& b) {
-                return with_func(a.first, a.second) < with_func(b.first, b.second);
-            });
-        return VlQueue::consV(it->second);
-    }
-    VlQueue max() const {
-        if (m_map.empty()) return VlQueue();
-        const auto it = std::max_element(
-            m_map.cbegin(), m_map.cend(),
-            [](const std::pair& a, const std::pair& b) {
-                return a.second < b.second;
-            });
-        return VlQueue::consV(it->second);
-    }
-    template 
-    VlQueue max(T_Func with_func) const {
-        if (m_map.empty()) return VlQueue();
-        const auto it = std::max_element(
-            m_map.cbegin(), m_map.cend(),
-            [&with_func](const std::pair& a, const std::pair& b) {
-                return with_func(a.first, a.second) < with_func(b.first, b.second);
-            });
-        return VlQueue::consV(it->second);
-    }
-
-    T_Value r_sum() const {
-        T_Value out(0);  // Type must have assignment operator
-        for (const auto& i : m_map) out += i.second;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_sum(T_Func with_func) const {
-        WithFuncReturnType out = WithFuncReturnType(0);
-        for (const auto& i : m_map) out += with_func(i.first, i.second);
-        return out;
-    }
-    T_Value r_product() const {
-        if (m_map.empty()) return T_Value(0);  // The big three do it this way
-        T_Value out = T_Value(1);
-        for (const auto& i : m_map) out *= i.second;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_product(T_Func with_func) const {
-        if (m_map.empty()) return WithFuncReturnType(0);  // The big three do it this way
-        WithFuncReturnType out = WithFuncReturnType(1);
-        for (const auto& i : m_map) out *= with_func(i.first, i.second);
-        return out;
-    }
-    T_Value r_and() const {
-        if (m_map.empty()) return T_Value(0);  // The big three do it this way
-        T_Value out = ~T_Value(0);
-        for (const auto& i : m_map) out &= i.second;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_and(T_Func with_func) const {
-        if (m_map.empty()) return WithFuncReturnType(0);  // The big three do it this way
-        WithFuncReturnType out = ~WithFuncReturnType(0);
-        for (const auto& i : m_map) out &= with_func(i.first, i.second);
-        return out;
-    }
-    T_Value r_or() const {
-        T_Value out = T_Value(0);
-        for (const auto& i : m_map) out |= i.second;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_or(T_Func with_func) const {
-        WithFuncReturnType out = WithFuncReturnType(0);
-        for (const auto& i : m_map) out |= with_func(i.first, i.second);
-        return out;
-    }
-    T_Value r_xor() const {
-        T_Value out = T_Value(0);
-        for (const auto& i : m_map) out ^= i.second;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_xor(T_Func with_func) const {
-        WithFuncReturnType out = WithFuncReturnType(0);
-        for (const auto& i : m_map) out ^= with_func(i.first, i.second);
-        return out;
-    }
-
-    // Dumping. Verilog: str = $sformatf("%p", assoc)
-    std::string to_string() const {
-        if (m_map.empty()) return "'{}";  // No trailing space
-        std::string out = "'{";
-        std::string comma;
-        for (const auto& i : m_map) {
-            out += comma + VL_TO_STRING(i.first) + ":" + VL_TO_STRING(i.second);
-            comma = ", ";
-        }
-        // Default not printed - maybe random init data
-        return out + "}";
-    }
-};
-
-template 
-std::string VL_TO_STRING(const VlAssocArray& obj) {
-    return obj.to_string();
-}
-
-template 
-struct VlContainsCustomStruct> : VlContainsCustomStruct {};
-
-template 
-void VL_READMEM_N(bool hex, int bits, const std::string& filename,
-                  VlAssocArray& obj, QData start, QData end) VL_MT_SAFE {
-    VlReadMem rmem{hex, bits, filename, start, end};
-    if (VL_UNLIKELY(!rmem.isOpen())) return;
-    while (true) {
-        QData addr;
-        std::string data;
-        if (rmem.get(addr /*ref*/, data /*ref*/)) {
-            rmem.setData(&(obj.at(addr)), data);
-        } else {
-            break;
-        }
-    }
-}
-
-template 
-void VL_WRITEMEM_N(bool hex, int bits, const std::string& filename,
-                   const VlAssocArray& obj, QData start, QData end) VL_MT_SAFE {
-    VlWriteMem wmem{hex, bits, filename, start, end};
-    if (VL_UNLIKELY(!wmem.isOpen())) return;
-    for (const auto& i : obj) {
-        const QData addr = i.first;
-        if (addr >= start && addr <= end) wmem.print(addr, true, &(i.second));
-    }
-}
-
-//===================================================================
-/// Verilog unpacked array container
-/// For when a standard C++[] array is not sufficient, e.g. an
-/// array under a queue, or methods operating on the array.
-///
-/// A 'struct' as we want this to be an aggregate type that allows
-/// static aggregate initialization. Consider data members private.
-///
-/// This class may get exposed to a Verilated Model's top I/O, if the top
-/// IO has an unpacked array.
-
-template 
-class VlUnpacked final {
-    // TYPES
-    using T_Key = IData;  // Index type, for uniformity with other containers
-    using Unpacked = T_Value[N_Depth];
-
-public:
-    template 
-    using WithFuncReturnType = decltype(std::declval()(0, std::declval()));
-
-    // MEMBERS
-    // This should be the only data member, otherwise generated static initializers need updating
-    Unpacked m_storage;  // Contents of the unpacked array
-
-    // CONSTRUCTORS
-    // Default constructors and destructor are used. Note however that C++20 requires that
-    // aggregate types do not have a user declared constructor, not even an explicitly defaulted
-    // one.
-
-    // OPERATOR METHODS
-    // Default copy assignment operators are used.
-
-    // METHODS
-public:
-    // Raw access
-    WData* data() { return &m_storage[0]; }
-    const WData* data() const { return &m_storage[0]; }
-
-    constexpr std::size_t size() const { return N_Depth; }
-
-    void fill(const T_Value& value) {
-        std::fill(std::begin(m_storage), std::end(m_storage), value);
-    }
-
-    // To fit C++14
-    template 
-    int find_length(int dimension, std::false_type) const {
-        return size();
-    }
-
-    template 
-    int find_length(int dimension, std::true_type) const {
-        if (dimension == N_CurrentDimension) {
-            return size();
-        } else {
-            return m_storage[0].template find_length(dimension);
-        }
-    }
-
-    template 
-    int find_length(int dimension) const {
-        return find_length(dimension, std::is_class{});
-    }
-
-    template 
-    auto& find_element(const std::vector& indices, std::false_type) {
-        return m_storage[indices[N_CurrentDimension]];
-    }
-
-    template 
-    auto& find_element(const std::vector& indices, std::true_type) {
-        return m_storage[indices[N_CurrentDimension]]
-            .template find_element(indices);
-    }
-
-    template 
-    auto& find_element(const std::vector& indices) {
-        return find_element(indices, std::is_class{});
-    }
-
-    T_Value& operator[](size_t index) { return m_storage[index]; }
-    constexpr const T_Value& operator[](size_t index) const { return m_storage[index]; }
-
-    // *this != that, which might be used for change detection/trigger computation, but avoid
-    // operator overloading in VlUnpacked for safety in other contexts.
-    bool neq(const VlUnpacked& that) const { return neq(*this, that); }
-    // Similar to 'neq' above, *this = that used for change detection
-    void assign(const VlUnpacked& that) { *this = that; }
-    bool operator==(const VlUnpacked& that) const { return !neq(that); }
-    bool operator!=(const VlUnpacked& that) const { return neq(that); }
-    // interface to C style arrays (used in ports), see issue #5125
-    bool neq(const T_Value that[N_Depth]) const { return neq(*this, that); }
-    void assign(const T_Value that[N_Depth]) { std::copy_n(that, N_Depth, m_storage); }
-    void operator=(const T_Value that[N_Depth]) { assign(that); }
-    bool operator<(const VlUnpacked& that) const {
-        for (int index = 0; index < N_Depth; ++index) {
-            if (m_storage[index] < that.m_storage[index]) return true;
-        }
-        return false;
-    }
-
-    // inside (set membership operator)
-    bool inside(const T_Value& value) const {
-        return std::find(std::begin(m_storage), std::end(m_storage), value) != std::end(m_storage);
-    }
-
-    void sort() { std::sort(std::begin(m_storage), std::end(m_storage)); }
-    template 
-    void sort(T_Func with_func) {
-        // with_func returns arbitrary type to use for the sort comparison
-        std::sort(std::begin(m_storage), std::end(m_storage),
-                  [=](const T_Value& a, const T_Value& b) {
-                      // index number is meaningless with sort, as it changes
-                      return with_func(0, a) < with_func(0, b);
-                  });
-    }
-    // std::rbegin/std::rend not available until C++14
-    void rsort() {
-        std::sort(std::begin(m_storage), std::end(m_storage), std::greater());
-    }
-    template 
-    void rsort(T_Func with_func) {
-        // with_func returns arbitrary type to use for the sort comparison
-        // std::rbegin/std::rend not available until C++14, so using > below
-        std::sort(std::begin(m_storage), std::end(m_storage),
-                  [=](const T_Value& a, const T_Value& b) {
-                      // index number is meaningless with sort, as it changes
-                      return with_func(0, a) > with_func(0, b);
-                  });
-    }
-    void reverse() { std::reverse(std::begin(m_storage), std::end(m_storage)); }
-    void shuffle() { std::shuffle(std::begin(m_storage), std::end(m_storage), VlURNG{}); }
-    VlQueue unique() const {
-        VlQueue out;
-        std::set saw;
-        for (const auto& i : m_storage) {
-            const auto it = saw.find(i);
-            if (it == saw.end()) {
-                saw.insert(it, i);
-                out.push_back(i);
-            }
-        }
-        return out;
-    }
-    template 
-    VlQueue unique(T_Func with_func) const {
-        VlQueue out;
-        std::set saw;
-        for (const auto& i : m_storage) {
-            const auto i_mapped = with_func(0, i);
-            const auto it = saw.find(i_mapped);
-            if (it == saw.end()) {
-                saw.insert(it, i_mapped);
-                out.push_back(i);
-            }
-        }
-        return out;
-    }
-    VlQueue unique_index() const {
-        VlQueue out;
-        IData index = 0;
-        std::set saw;
-        for (const auto& i : m_storage) {
-            const auto it = saw.find(i);
-            if (it == saw.end()) {
-                saw.insert(it, i);
-                out.push_back(index);
-            }
-            ++index;
-        }
-        return out;
-    }
-    template 
-    VlQueue unique_index(T_Func with_func) const {
-        VlQueue out;
-        IData index = 0;
-        std::set saw;
-        for (const auto& i : m_storage) {
-            const auto i_mapped = with_func(index, i);
-            auto it = saw.find(i_mapped);
-            if (it == saw.end()) {
-                saw.insert(it, i_mapped);
-                out.push_back(index);
-            }
-            ++index;
-        }
-        return out;
-    }
-    template 
-    VlQueue find(T_Func with_func) const {
-        VlQueue out;
-        IData index = 0;
-        for (const auto& i : m_storage) {
-            if (with_func(index, i)) out.push_back(i);
-            ++index;
-        }
-        return out;
-    }
-    template 
-    VlQueue find_index(T_Func with_func) const {
-        VlQueue out;
-        IData index = 0;
-        for (const auto& i : m_storage) {
-            if (with_func(index, i)) out.push_back(index);
-            ++index;
-        }
-        return out;
-    }
-    template 
-    VlQueue find_first(T_Func with_func) const {
-        // Can't use std::find_if as need index number
-        IData index = 0;
-        for (const auto& i : m_storage) {
-            if (with_func(index, i)) return VlQueue::consV(i);
-            ++index;
-        }
-        return VlQueue{};
-    }
-    template 
-    VlQueue find_first_index(T_Func with_func) const {
-        IData index = 0;
-        for (const auto& i : m_storage) {
-            if (with_func(index, i)) return VlQueue::consV(index);
-            ++index;
-        }
-        return VlQueue{};
-    }
-    template 
-    VlQueue find_last(T_Func with_func) const {
-        for (int i = N_Depth - 1; i >= 0; i--) {
-            if (with_func(i, m_storage[i])) return VlQueue::consV(m_storage[i]);
-        }
-        return VlQueue{};
-    }
-    template 
-    VlQueue find_last_index(T_Func with_func) const {
-        for (int i = N_Depth - 1; i >= 0; i--) {
-            if (with_func(i, m_storage[i])) return VlQueue::consV(i);
-        }
-        return VlQueue{};
-    }
-
-    // Reduction operators
-    VlQueue min() const {
-        const auto it = std::min_element(std::begin(m_storage), std::end(m_storage));
-        return VlQueue::consV(*it);
-    }
-    template 
-    VlQueue min(T_Func with_func) const {
-        const auto it = std::min_element(std::begin(m_storage), std::end(m_storage),
-                                         [&with_func](const IData& a, const IData& b) {
-                                             return with_func(0, a) < with_func(0, b);
-                                         });
-        return VlQueue::consV(*it);
-    }
-    VlQueue max() const {
-        const auto it = std::max_element(std::begin(m_storage), std::end(m_storage));
-        return VlQueue::consV(*it);
-    }
-    template 
-    VlQueue max(T_Func with_func) const {
-        const auto it = std::max_element(std::begin(m_storage), std::end(m_storage),
-                                         [&with_func](const IData& a, const IData& b) {
-                                             return with_func(0, a) < with_func(0, b);
-                                         });
-        return VlQueue::consV(*it);
-    }
-
-    T_Value r_sum() const {
-        T_Value out(0);  // Type must have assignment operator
-        for (const auto& i : m_storage) out += i;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_sum(T_Func with_func) const {
-        WithFuncReturnType out
-            = WithFuncReturnType(0);  // Type must have assignment operator
-        for (const auto& i : m_storage) out += with_func(0, i);
-        return out;
-    }
-    T_Value r_product() const {
-        T_Value out = T_Value(1);
-        for (const auto& i : m_storage) out *= i;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_product(T_Func with_func) const {
-        WithFuncReturnType out = WithFuncReturnType(1);
-        for (const auto& i : m_storage) out *= with_func(0, i);
-        return out;
-    }
-    T_Value r_and() const {
-        if (m_storage.empty()) return T_Value(0);  // The big three do it this way
-        T_Value out = ~T_Value(0);
-        for (const auto& i : m_storage) out &= i;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_and(T_Func with_func) const {
-        WithFuncReturnType out = ~WithFuncReturnType(0);
-        for (const auto& i : m_storage) out &= with_func(0, i);
-        return out;
-    }
-    T_Value r_or() const {
-        T_Value out = T_Value(0);
-        for (const auto& i : m_storage) out |= i;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_or(T_Func with_func) const {
-        WithFuncReturnType out = WithFuncReturnType(0);
-        for (const auto& i : m_storage) out |= with_func(0, i);
-        return out;
-    }
-    T_Value r_xor() const {
-        T_Value out = T_Value(0);
-        for (const auto& i : m_storage) out ^= i;
-        return out;
-    }
-    template 
-    WithFuncReturnType r_xor(T_Func with_func) const {
-        WithFuncReturnType out = WithFuncReturnType(0);
-        for (const auto& i : m_storage) out ^= with_func(0, i);
-        return out;
-    }
-
-    // Dumping. Verilog: str = $sformatf("%p", assoc)
-    std::string to_string() const {
-        std::string out = "'{";
-        std::string comma;
-        for (int i = 0; i < N_Depth; ++i) {
-            out += comma + VL_TO_STRING(m_storage[i]);
-            comma = ", ";
-        }
-        return out + "}";
-    }
-
-private:
-    template 
-    static bool neq(const VlUnpacked& a, const VlUnpacked& b) {
-        for (size_t i = 0; i < N_Dep; ++i) {
-            // Recursive 'neq', in case T_Val is also a VlUnpacked<_, _>
-            if (neq(a.m_storage[i], b.m_storage[i])) return true;
-        }
-        return false;
-    }
-
-    template 
-    static bool neq(const VlUnpacked& a, const T_Val b[N_Dep]) {
-        for (size_t i = 0; i < N_Dep; ++i) {
-            // Recursive 'neq', in case T_Val is also a VlUnpacked<_, _>
-            if (neq(a.m_storage[i], b[i])) return true;
-        }
-        return false;
-    }
-
-    template   //
-    static bool neq(const T_Other& a, const T_Other& b) {
-        // Base case (T_Other is not VlUnpacked<_, _>), fall back on !=
-        return a != b;
-    }
-};
-// Trait to detect VlUnpacked types
-template 
-struct IsVlUnpacked : std::false_type {};
-template 
-struct IsVlUnpacked> : std::true_type {};
-
-template 
-std::string VL_TO_STRING(const VlUnpacked& obj) {
-    return obj.to_string();
-}
-
-template 
-struct VlContainsCustomStruct> : VlContainsCustomStruct {};
-
-template 
-template 
-void VlQueue::renew_copy(
-    size_t size, const VlUnpacked& rhs) {
-    clear();
-    if (size == 0) return;
-    m_deque.resize(size, atDefault());
-    for (size_t i = 0; i < std::min(size, N_UnpackedDepth); ++i) { m_deque[i] = rhs.m_storage[i]; }
-}
-
-//===================================================================
-// Helper to apply the given indices to a target expression
-
-template 
-struct VlApplyIndices final {
-    VL_ATTR_ALWINLINE
-    static auto& apply(T_Target& target, const size_t* indicesp) {
-        return VlApplyIndices::apply(
-            target[indicesp[N_Curr]], indicesp);
-    }
-};
-
-template 
-struct VlApplyIndices final {
-    VL_ATTR_ALWINLINE
-    static T_Target& apply(T_Target& target, const size_t*) { return target; }
-};
-
-//===================================================================
-// Commit queue for NBAs - currently only for unpacked arrays
-//
-// This data-structure is used to handle non-blocking assignments
-// that might execute a variable number of times in a single
-// evaluation. It has 2 operations:
-// - 'enqueue' will add an update to the queue
-// - 'commit' will apply all enqueued updates to the target variable,
-//   in the order they were enqueued. This ensures the last NBA
-//   takes effect as it is expected.
-// There are 2 specializations of this class below:
-// - A version when a partial element update is not required,
-//   e.g, to handle:
-//      logic [31:0] array[N];
-//      for (int i = 0 ; i < N ; ++i) array[i] <= x;
-//   Here 'enqueue' takes the RHS ('x'), and the array indices ('i')
-//   as arguments.
-// - A different version when a partial element update is required,
-//   e.g. for:
-//      logic [31:0] array[N];
-//      for (int i = 0 ; i < N ; ++i) array[i][3:1] <= y;
-//   Here 'enqueue' takes one additional argument, which is a bitmask
-//   derived from the bit selects (_[3:1]), which masks the bits that
-//   need to be updated, and additionally the RHS is widened to a full
-//   element size, with the bits inserted into the masked region.
-template 
-class VlNBACommitQueue;
-
-// Specialization for whole element updates only
-template 
-class VlNBACommitQueue final {
-    // TYPES
-    struct Entry final {
-        T_Element value;
-        size_t indices[N_Rank];
-    };
-
-    // STATE
-    std::vector m_pending;  // Pending updates, in program order
-
-public:
-    // CONSTRUCTOR
-    VlNBACommitQueue() = default;
-    VL_UNCOPYABLE(VlNBACommitQueue);
-
-    // METHODS
-    template 
-    void enqueue(const T_Element& value, T_Args... indices) {
-        m_pending.emplace_back(Entry{value, {indices...}});
-    }
-
-    // Note: T_Commit might be different from T_Target. Specifically, when the signal is a
-    // top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
-    template 
-    void commit(T_Commit& target) {
-        if (m_pending.empty()) return;
-        for (const Entry& entry : m_pending) {
-            VlApplyIndices<0, N_Rank, T_Commit>::apply(target, entry.indices) = entry.value;
-        }
-        m_pending.clear();
-    }
-};
-
-// With partial element updates
-template 
-class VlNBACommitQueue final {
-    // TYPES
-    struct Entry final {
-        T_Element value;
-        T_Element mask;
-        size_t indices[N_Rank];
-    };
-
-    // STATE
-    std::vector m_pending;  // Pending updates, in program order
-
-    // STATIC METHODS
-
-    // Binary & | ~ for elements to use for masking in partial updates. Sorry for the templates.
-    template 
-    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type
-    bAnd(const T& a, const T& b) {
-        return a & b;
-    }
-
-    template 
-    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type
-    bAnd(const T& a, const T& b) {
-        T result;
-        for (size_t i = 0; i < T::Words; ++i) {
-            result.m_storage[i] = a.m_storage[i] & b.m_storage[i];
-        }
-        return result;
-    }
-
-    template 
-    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type
-    bOr(const T& a, const T& b) {
-        return a | b;
-    }
-
-    template 
-    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type  //
-    bOr(const T& a, const T& b) {
-        T result;
-        for (size_t i = 0; i < T::Words; ++i) {
-            result.m_storage[i] = a.m_storage[i] | b.m_storage[i];
-        }
-        return result;
-    }
-
-    template 
-    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type
-    bNot(const T& a) {
-        return ~a;
-    }
-
-    template 
-    VL_ATTR_ALWINLINE static typename std::enable_if::value, T>::type
-    bNot(const T& a) {
-        T result;
-        for (size_t i = 0; i < T::Words; ++i) result.m_storage[i] = ~a.m_storage[i];
-        return result;
-    }
-
-public:
-    // CONSTRUCTOR
-    VlNBACommitQueue() = default;
-    VL_UNCOPYABLE(VlNBACommitQueue);
-
-    // METHODS
-    template 
-    void enqueue(const T_Element& value, const T_Element& mask, T_Args... indices) {
-        m_pending.emplace_back(Entry{value, mask, {indices...}});
-    }
-
-    // Note: T_Commit might be different from T_Target. Specifically, when the signal is a
-    // top-level IO port, T_Commit will be a native C array, while T_Target, will be a VlUnpacked
-    template 
-    void commit(T_Commit& target) {
-        if (m_pending.empty()) return;
-        for (const Entry& entry : m_pending) {  //
-            auto& ref = VlApplyIndices<0, N_Rank, T_Commit>::apply(target, entry.indices);
-            // Maybe inefficient, but it works for now ...
-            const auto oldValue = ref;
-            ref = bOr(bAnd(entry.value, entry.mask), bAnd(oldValue, bNot(entry.mask)));
-        }
-        m_pending.clear();
-    }
-};
-
-//===================================================================
-// Object that VlDeleter is capable of deleting
-
-class VlDeletable VL_NOT_FINAL {
-public:
-    VlDeletable() = default;
-    virtual ~VlDeletable() = default;
-};
-
-//===================================================================
-// Class providing delayed deletion of garbage objects. Objects get deleted only when 'deleteAll()'
-// is called, or the deleter itself is destroyed.
-
-class VlDeleter final {
-    // MEMBERS
-    // Queue of new objects that should be deleted
-    std::vector m_newGarbage VL_GUARDED_BY(m_mutex);
-    // Queue of objects currently being deleted (only for deleteAll())
-    std::vector m_deleteNow VL_GUARDED_BY(m_deleteMutex);
-    mutable VerilatedMutex m_mutex;  // Mutex protecting the 'new garbage' queue
-    mutable VerilatedMutex m_deleteMutex;  // Mutex protecting the delete queue
-
-public:
-    // CONSTRUCTOR
-    VlDeleter() = default;
-    ~VlDeleter() { deleteAll(); }
-
-private:
-    VL_UNCOPYABLE(VlDeleter);
-
-public:
-    // METHODS
-    // Adds a new object to the 'new garbage' queue.
-    void put(VlDeletable* const objp) VL_MT_SAFE {
-        const VerilatedLockGuard lock{m_mutex};
-        m_newGarbage.push_back(objp);
-    }
-
-    // Deletes all queued garbage objects.
-    void deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_MT_SAFE;
-};
-
-//===================================================================
-// Base class for all verilated classes. Includes a reference counter, and a pointer to the deleter
-// object that should destroy it after the counter reaches 0. This allows for easy construction of
-// VlClassRefs from 'this'.
-
-class VlClass VL_NOT_FINAL : public VlDeletable {
-    // TYPES
-    template 
-    friend class VlClassRef;  // Needed for access to the ref counter and deleter
-
-    // MEMBERS
-    std::atomic m_counter{1};  // Reference count for this object
-    VlDeleter* m_deleterp = nullptr;  // The deleter that will delete this object
-
-    // METHODS
-    // Atomically increments the reference counter
-    void refCountInc() VL_MT_SAFE {
-        VL_DEBUG_IFDEF(assert(m_counter););  // If zero, we might have already deleted
-        ++m_counter;
-    }
-    // Atomically decrements the reference counter. Assuming VlClassRef semantics are sound, it
-    // should never get called at m_counter == 0.
-    void refCountDec() VL_MT_SAFE {
-        if (!--m_counter) m_deleterp->put(this);
-    }
-
-public:
-    // CONSTRUCTORS
-    VlClass() {}
-    VlClass(const VlClass& copied) {}
-    ~VlClass() override = default;
-    // Polymorphic shallow clone. Overridden in each generated concrete class.
-    virtual VlClass* clone() const { return nullptr; }
-    // METHODS
-    virtual const char* typeName() const { return "VlClass"; }
-    virtual std::string to_string() const { return ""; }
-};
-
-//===================================================================
-// Represents the null pointer. Used for:
-// * setting VlClassRef to null instead of via nullptr_t, to prevent the implicit conversion of 0
-//   to nullptr,
-// * comparing interface pointers to null.
-
-struct VlNull final {
-    operator bool() const { return false; }
-    bool operator==(const void* ptr) const { return !ptr; }
-};
-inline bool operator==(const void* ptr, VlNull) { return !ptr; }
-
-//===================================================================
-// Verilog class reference container
-// There are no multithreaded locks on this; the base variable must
-// be protected by other means
-
-template 
-class VlClassRef final {
-private:
-    // TYPES
-    template 
-    friend class VlClassRef;  // Needed for template copy/move assignments
-
-    // MEMBERS
-    T_Class* m_objp = nullptr;  // Object pointed to
-
-    // METHODS
-    // Increase reference counter with null check
-    void refCountInc() const VL_MT_SAFE {
-        if (m_objp) m_objp->refCountInc();
-    }
-    // Decrease reference counter with null check
-    void refCountDec() const VL_MT_SAFE {
-        if (m_objp) m_objp->refCountDec();
-    }
-
-public:
-    // CONSTRUCTORS
-    VlClassRef() = default;
-    // Init with nullptr
-    // cppcheck-suppress noExplicitConstructor
-    VlClassRef(VlNull){};
-    template 
-    VlClassRef(VlDeleter& deleter, T_Args&&... args)
-        // () required here to avoid narrowing conversion warnings,
-        // when a new() has an e.g. CData type and passed a 1U.
-        : m_objp{new T_Class(std::forward(args)...)} {
-        // refCountInc was moved to the constructor of T_Class
-        // to fix self references in constructor.
-        m_objp->m_deleterp = &deleter;
-    }
-    // Explicit to avoid implicit conversion from 0
-    explicit VlClassRef(T_Class* objp)
-        : m_objp{objp} {
-        refCountInc();
-    }
-    // cppcheck-suppress noExplicitConstructor
-    VlClassRef(const VlClassRef& copied)
-        : m_objp{copied.m_objp} {
-        refCountInc();
-    }
-    // cppcheck-suppress noExplicitConstructor
-    VlClassRef(VlClassRef&& moved)
-        : m_objp{std::exchange(moved.m_objp, nullptr)} {}
-    // cppcheck-suppress noExplicitConstructor
-    template 
-    VlClassRef(const VlClassRef& copied)
-        : m_objp{copied.m_objp} {
-        refCountInc();
-    }
-    // cppcheck-suppress noExplicitConstructor
-    template 
-    VlClassRef(VlClassRef&& moved)
-        : m_objp{std::exchange(moved.m_objp, nullptr)} {}
-    ~VlClassRef() { refCountDec(); }
-
-    // METHODS
-    // Copy and move assignments
-    VlClassRef& operator=(const VlClassRef& copied) {
-        if (m_objp == copied.m_objp) return *this;
-        refCountDec();
-        m_objp = copied.m_objp;
-        refCountInc();
-        return *this;
-    }
-    VlClassRef& operator=(VlClassRef&& moved) {
-        if (m_objp == moved.m_objp) return *this;
-        refCountDec();
-        m_objp = std::exchange(moved.m_objp, nullptr);
-        return *this;
-    }
-    template 
-    VlClassRef& operator=(const VlClassRef& copied) {
-        if (m_objp == copied.m_objp) return *this;
-        refCountDec();
-        m_objp = copied.m_objp;
-        refCountInc();
-        return *this;
-    }
-    template 
-    VlClassRef& operator=(VlClassRef&& moved) {
-        if (m_objp == moved.m_objp) return *this;
-        refCountDec();
-        m_objp = std::exchange(moved.m_objp, nullptr);
-        return *this;
-    }
-    // Assign with nullptr
-    VlClassRef& operator=(VlNull) {
-        refCountDec();
-        m_objp = nullptr;
-        return *this;
-    }
-    // Dynamic caster
-    template 
-    VlClassRef dynamicCast() const {
-        return VlClassRef{dynamic_cast(m_objp)};
-    }
-    // Polymorphic shallow clone (IEEE 1800-2017 8.7: new  preserves runtime type)
-    VlClassRef clone(VlDeleter& deleter) const {
-        VlClass* clonedp = m_objp->clone();
-        if (VL_UNLIKELY(!clonedp)) return {};
-        clonedp->m_deleterp = &deleter;
-        VlClassRef result;
-        result.m_objp = dynamic_cast(clonedp);
-        return result;
-    }
-    // Dereference operators
-    T_Class& operator*() const { return *m_objp; }
-    T_Class* operator->() const { return m_objp; }
-    // For 'if (ptr)...'
-    operator bool() const { return m_objp; }
-    // In SV A == B iff both are handles to the same object (IEEE 1800-2023 8.4)
-    template 
-    bool operator==(const VlClassRef& rhs) const {
-        return m_objp == rhs.m_objp;
-    };
-    template 
-    bool operator!=(const VlClassRef& rhs) const {
-        return m_objp != rhs.m_objp;
-    };
-    template 
-    bool operator<(const VlClassRef& rhs) const {
-        return m_objp < rhs.m_objp;
-    };
-};
-
-template 
-static inline bool VL_CAST_DYNAMIC(VlClassRef in, VlClassRef& outr) {
-    if (!in) {
-        outr = VlNull{};
-        return true;
-    }
-    VlClassRef casted = in.template dynamicCast();
-    if (VL_LIKELY(casted)) {
-        outr = casted;
-        return true;
-    } else {
-        return false;
-    }
-}
-
-template 
-static inline bool VL_CAST_DYNAMIC(VlNull in, VlClassRef& outr) {
-    outr = VlNull{};
-    return true;
-}
-
-// For printing class references under a container, several choices:
-// 1. Dump recursively the pointed-to object.  Can be huge.  Might be circular.
-// 2. Print object type and pointer as C pointer.  Astable when rerun.
-// 3. Print object type and pointer as an incrementing number.  Needs num storage.
-// 4. Print object type alone.  Avoids above issues.
-template 
-inline std::string VL_TO_STRING(const VlClassRef& obj) {
-    return obj ? obj->typeName() : "null";
-}
-// Entry point for string conversion (called from not under a container);
-// dereference VlClassRef objects to print members
-template   // Default if no specialization
-inline std::string VL_TO_STRING_DEREF(T_Lhs obj) {
-    return VL_TO_STRING(obj);
-}
-template   // Specialization
-inline std::string VL_TO_STRING_DEREF(const VlClassRef& obj) {
-    return obj ? obj->to_string() : "null";
-}
-template   // Specialization
-inline std::string VL_TO_STRING_DEREF(VlClassRef& obj) {
-    return obj ? obj->to_string() : "null";
-}
-
-//=============================================================================
-// VlSampleQueue stores samples for input clockvars in clocking blocks. At a clocking event,
-// samples from this queue should be written to the correct input clockvar.
-
-template 
-class VlSampleQueue final {
-    // TYPES
-    // Type representing a single value sample at a point in time
-    struct VlSample final {
-        uint64_t m_timestamp;  // Timestamp at which the value was sampled
-        T_Sampled m_value;  // The sampled value
-    };
-
-    // MEMBERS
-    std::deque m_queue;  // Queue of samples with timestamps
-
-public:
-    // METHODS
-    // Push a new sample with the given timestamp to the end of the queue
-    void push(uint64_t time, const T_Sampled& value) { m_queue.push_back({time, value}); }
-    // Get the latest sample with its timestamp less than or equal to the given skew
-    void pop(uint64_t time, uint64_t skew, T_Sampled& value) {
-        if (time < skew) return;
-        // Find the last element not greater than (time - skew). Do a binary search, as the queue
-        // should be ordered.
-        auto it = std::lower_bound(m_queue.rbegin(), m_queue.rend(), VlSample{time - skew, {}},
-                                   [](const VlSample& sample, const VlSample& skewed) {
-                                       return sample.m_timestamp > skewed.m_timestamp;
-                                   });
-        if (it != m_queue.rend()) {
-            value = it->m_value;
-            m_queue.erase(m_queue.begin(), it.base());
-        }
-    }
-};
-
-//======================================================================
-
-#define VL_NEW(Class, ...) \
-    VlClassRef { vlSymsp->__Vm_deleter, __VA_ARGS__ }
-
-#define VL_KEEP_THIS \
-    VlClassRef::type> __Vthisref { this }
-
-template   // T typically of type VlClassRef
-inline T VL_NULL_CHECK(T t, const char* filename, int linenum) {
-    if (VL_UNLIKELY(!t)) Verilated::nullPointerError(filename, linenum);
-    return t;
-}
-
-//======================================================================
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.cpp
deleted file mode 100644
index 7ceb04fc3cc..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.cpp
+++ /dev/null
@@ -1,685 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated C++ tracing in VCD format implementation code
-///
-/// This file must be compiled and linked against all Verilated objects
-/// that use --trace-vcd.
-///
-/// Use "verilator --trace-vcd" to add this to the Makefile for the linker.
-///
-//=============================================================================
-
-// clang-format off
-
-#include "verilatedos.h"
-#include "verilated.h"
-#include "verilated_vcd_c.h"
-
-#include 
-#include 
-#include 
-
-#if defined(_WIN32) && !defined(__MINGW32__) && !defined(__CYGWIN__)
-# include 
-#else
-# include 
-#endif
-
-#ifndef O_LARGEFILE  // WIN32 headers omit this
-# define O_LARGEFILE 0
-#endif
-#ifndef O_NONBLOCK  // WIN32 headers omit this
-# define O_NONBLOCK 0
-#endif
-#ifndef O_CLOEXEC  // WIN32 headers omit this
-# define O_CLOEXEC 0
-#endif
-
-// clang-format on
-
-// This size comes form VCD allowing use of printable ASCII characters between
-// '!' and '~' inclusive, which are a total of 94 different values. Encoding a
-// 32 bit code hence needs a maximum of std::ceil(log94(2**32-1)) == 5 bytes.
-constexpr unsigned VL_TRACE_MAX_VCD_CODE_SIZE = 5;  // Maximum length of a VCD string code
-
-// We use 8 bytes per code in a suffix buffer array.
-// 1 byte optional separator + VL_TRACE_MAX_VCD_CODE_SIZE bytes for code
-// + 1 byte '\n' + 1 byte suffix size. This luckily comes out to a power of 2,
-// meaning the array can be aligned such that entries never straddle multiple
-// cache-lines.
-constexpr unsigned VL_TRACE_SUFFIX_ENTRY_SIZE = 8;  // Size of a suffix entry
-
-//=============================================================================
-// Specialization of the generics for this trace format
-
-#define VL_SUB_T VerilatedVcd
-#define VL_BUF_T VerilatedVcdBuffer
-#include "verilated_trace_imp.h"
-#undef VL_SUB_T
-#undef VL_BUF_T
-
-//=============================================================================
-//=============================================================================
-//=============================================================================
-// VerilatedVcdFile
-
-bool VerilatedVcdFile::open(const std::string& name) VL_MT_UNSAFE {
-    m_fd = ::open(name.c_str(),
-                  O_CREAT | O_WRONLY | O_TRUNC | O_LARGEFILE | O_NONBLOCK | O_CLOEXEC, 0666);
-    return m_fd >= 0;
-}
-
-void VerilatedVcdFile::close() VL_MT_UNSAFE { ::close(m_fd); }
-
-ssize_t VerilatedVcdFile::write(const char* bufp, ssize_t len) VL_MT_UNSAFE {
-    return ::write(m_fd, bufp, len);
-}
-
-//=============================================================================
-//=============================================================================
-//=============================================================================
-// Opening/Closing
-
-VerilatedVcd::VerilatedVcd(VerilatedVcdFile* filep) {
-    // Not in header to avoid link issue if header is included without this .cpp file
-    m_fileNewed = (filep == nullptr);
-    m_filep = m_fileNewed ? new VerilatedVcdFile : filep;
-    m_wrChunkSize = 8 * 1024;
-    m_wrBufp = new char[m_wrChunkSize * 8];
-    m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
-    m_writep = m_wrBufp;
-    m_wrTimeBeginp = nullptr;
-    m_wrTimeEndp = nullptr;
-}
-
-void VerilatedVcd::open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    if (isOpen()) return;
-
-    // Set member variables
-    m_filename = filename;  // "" is ok, as someone may overload open
-
-    openNextImp(m_rolloverSize != 0);
-    if (!isOpen()) return;
-
-    printStr("$version Generated by VerilatedVcd $end\n");
-    printStr("$timescale ");
-    printStr(timeResStr().c_str());  // lintok-begin-on-ref
-    printStr(" $end\n");
-
-    // Scope and signal definitions
-    assert(m_indent >= 0);
-    ++m_indent;
-    Super::traceInit();
-    --m_indent;
-    assert(m_indent >= 0);
-
-    printStr("$enddefinitions $end\n\n\n");
-
-    // When using rollover, the first chunk contains the header only.
-    if (m_rolloverSize) openNextImp(true);
-}
-
-void VerilatedVcd::openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex) {
-    // Open next filename in concat sequence, mangle filename if
-    // incFilename is true.
-    const VerilatedLockGuard lock{m_mutex};
-    openNextImp(incFilename);
-}
-
-void VerilatedVcd::openNextImp(bool incFilename) {
-    closePrev();  // Close existing
-    if (incFilename) {
-        // Find _0000.{ext} in filename
-        std::string name = m_filename;
-        const size_t pos = name.rfind('.');
-        if (pos > 8 && 0 == std::strncmp("_cat", name.c_str() + pos - 8, 4)
-            && std::isdigit(name.c_str()[pos - 4]) && std::isdigit(name.c_str()[pos - 3])
-            && std::isdigit(name.c_str()[pos - 2]) && std::isdigit(name.c_str()[pos - 1])) {
-            // Increment code.
-            if ((++(name[pos - 1])) > '9') {
-                name[pos - 1] = '0';
-                if ((++(name[pos - 2])) > '9') {
-                    name[pos - 2] = '0';
-                    if ((++(name[pos - 3])) > '9') {
-                        name[pos - 3] = '0';
-                        if ((++(name[pos - 4])) > '9') {  //
-                            name[pos - 4] = '0';
-                        }
-                    }
-                }
-            }
-        } else {
-            // Append _cat0000
-            name.insert(pos, "_cat0000");
-        }
-        m_filename = name;
-    }
-    if (VL_UNCOVERABLE(m_filename[0] == '|')) {
-        assert(0);  // LCOV_EXCL_LINE // Not supported yet.
-    } else {
-        // cppcheck-suppress duplicateExpression
-        if (!m_filep->open(m_filename)) {
-            // User code can check isOpen()
-            m_isOpen = false;
-            return;
-        }
-    }
-    m_isOpen = true;
-    constDump(true);  // First dump must contain the const signals
-    fullDump(true);  // First dump must be full
-    m_wroteBytes = 0;
-}
-
-bool VerilatedVcd::preChangeDump() {
-    if (VL_UNLIKELY(m_rolloverSize && m_wroteBytes > m_rolloverSize)) openNextImp(true);
-    return isOpen();
-}
-
-void VerilatedVcd::emitTimeChange(uint64_t timeui) {
-    // Remember pointers when last emitted time stamp; if last output was
-    // timestamp backup and overwrite it.
-    // This is faster then checking on every signal change if time needs to
-    // be emitted.  Note buffer flushes may still emit a rare duplicate.
-    if (m_wrTimeBeginp && m_wrTimeEndp == m_writep) m_writep = m_wrTimeBeginp;
-    m_wrTimeBeginp = m_writep;
-    {
-        printStr("#");
-        const std::string str = std::to_string(timeui);
-        printStr(str.c_str());
-        printStr("\n");
-    }
-    m_wrTimeEndp = m_writep;
-}
-
-VerilatedVcd::~VerilatedVcd() {
-    close();
-    if (m_wrBufp) VL_DO_CLEAR(delete[] m_wrBufp, m_wrBufp = nullptr);
-    if (m_filep && m_fileNewed) VL_DO_CLEAR(delete m_filep, m_filep = nullptr);
-    if (parallel()) {
-        assert(m_numBuffers == m_freeBuffers.size());
-        for (auto& pair : m_freeBuffers) VL_DO_CLEAR(delete[] pair.first, pair.first = nullptr);
-    }
-}
-
-void VerilatedVcd::closePrev() {
-    // This function is on the flush() call path
-    if (!isOpen()) return;
-
-    Super::flushBase();
-    bufferFlush();
-    m_isOpen = false;
-    m_filep->close();
-}
-
-void VerilatedVcd::closeErr() {
-    // This function is on the flush() call path
-    // Close due to an error.  We might abort before even getting here,
-    // depending on the definition of vl_fatal.
-    if (!isOpen()) return;
-
-    // No buffer flush, just fclose
-    m_isOpen = false;
-    m_filep->close();  // May get error, just ignore it
-}
-
-void VerilatedVcd::close() VL_MT_SAFE_EXCLUDES(m_mutex) {
-    // This function is on the flush() call path
-    const VerilatedLockGuard lock{m_mutex};
-    if (!isOpen()) return;
-    closePrev();
-    // closePrev() called Super::flush(), so we just
-    // need to shut down the tracing thread here.
-    Super::closeBase();
-}
-
-void VerilatedVcd::flush() VL_MT_SAFE_EXCLUDES(m_mutex) {
-    const VerilatedLockGuard lock{m_mutex};
-    Super::flushBase();
-    bufferFlush();
-}
-
-void VerilatedVcd::printStr(const char* str) {
-    // Not fast...
-    while (*str) {
-        *m_writep++ = *str++;
-        bufferCheck();
-    }
-}
-
-void VerilatedVcd::bufferResize(size_t minsize) {
-    // minsize is size of largest write.  We buffer at least 8 times as much data,
-    // writing when we are 3/4 full (with thus 2*minsize remaining free)
-    if (VL_UNLIKELY(minsize > m_wrChunkSize)) {
-        const char* oldbufp = m_wrBufp;
-        m_wrChunkSize = vlstd::roundUpToMultipleOf<1024>(minsize * 2);
-        m_wrBufp = new char[m_wrChunkSize * 8];
-        std::memcpy(m_wrBufp, oldbufp, m_writep - oldbufp);
-        m_writep = m_wrBufp + (m_writep - oldbufp);
-        if (m_wrTimeBeginp) {
-            m_wrTimeBeginp = m_wrBufp + (m_wrTimeBeginp - oldbufp);
-            m_wrTimeEndp = m_wrBufp + (m_wrTimeEndp - oldbufp);
-        }
-        m_wrFlushp = m_wrBufp + m_wrChunkSize * 6;
-        VL_DO_CLEAR(delete[] oldbufp, oldbufp = nullptr);
-    }
-}
-
-void VerilatedVcd::bufferFlush() VL_MT_UNSAFE_ONE {
-    // This function can be called from the trace offload thread
-    // This function is on the flush() call path
-    // We add output data to m_writep.
-    // When it gets nearly full we dump it using this routine which calls write()
-    // This is much faster than using buffered I/O
-    if (VL_UNLIKELY(!m_isOpen)) return;
-    const char* wp = m_wrBufp;
-    while (true) {
-        const ssize_t remaining = (m_writep - wp);
-        if (remaining == 0) break;
-        errno = 0;
-        const ssize_t got = m_filep->write(wp, remaining);
-        if (got > 0) {
-            wp += got;
-            m_wroteBytes += got;
-        } else if (VL_UNCOVERABLE(got < 0)) {
-            if (VL_UNCOVERABLE(errno != EAGAIN && errno != EINTR)) {
-                // LCOV_EXCL_START
-                // write failed, presume error (perhaps out of disk space)
-                const std::string msg = "VerilatedVcd::bufferFlush: "s + std::strerror(errno);
-                VL_FATAL_MT("", 0, "", msg.c_str());
-                closeErr();
-                break;
-                // LCOV_EXCL_STOP
-            }
-        }
-    }
-
-    // Reset buffer
-    m_writep = m_wrBufp;
-    m_wrTimeBeginp = nullptr;
-    m_wrTimeEndp = nullptr;
-}
-
-//=============================================================================
-// Definitions
-
-void VerilatedVcd::printIndent(int level_change) {
-    if (level_change < 0) m_indent += level_change;
-    for (int i = 0; i < m_indent; ++i) printStr(" ");
-    if (level_change > 0) m_indent += level_change;
-}
-
-void VerilatedVcd::pushPrefix(const char* namep, VerilatedTracePrefixType type) {
-    assert(!m_prefixStack.empty());  // Constructor makes an empty entry
-    const std::string name{namep};
-    // An empty name means this is the root of a model created with
-    // name()=="".  The tools get upset if we try to pass this as empty, so
-    // we put the signals under a new $rootio scope, but the signals
-    // further down will be peers, not children (as usual for name()!="").
-    const std::string prevPrefix = m_prefixStack.back().first;
-    if (name == "$rootio" && !prevPrefix.empty()) {
-        // Upper has name, we can suppress inserting $rootio, but still push so popPrefix works
-        m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
-        return;
-    } else if (name.empty()) {
-        m_prefixStack.emplace_back(prevPrefix, VerilatedTracePrefixType::ROOTIO_WRAPPER);
-        return;
-    }
-
-    const std::string newPrefix = prevPrefix + name;
-    bool properScope = false;
-    switch (type) {
-    case VerilatedTracePrefixType::SCOPE_MODULE:
-    case VerilatedTracePrefixType::SCOPE_INTERFACE:
-    case VerilatedTracePrefixType::STRUCT_PACKED:
-    case VerilatedTracePrefixType::STRUCT_UNPACKED:
-    case VerilatedTracePrefixType::UNION_PACKED: {
-        properScope = true;
-        break;
-    }
-    default: break;
-    }
-    if (properScope) {
-        printIndent(1);
-        printStr("$scope module ");
-        const std::string n = lastWord(newPrefix);
-        printStr(n.c_str());
-        printStr(" $end\n");
-    }
-    m_prefixStack.emplace_back(newPrefix + (properScope ? " " : ""), type);
-}
-
-void VerilatedVcd::popPrefix() {
-    assert(!m_prefixStack.empty());
-    switch (m_prefixStack.back().second) {
-    case VerilatedTracePrefixType::SCOPE_MODULE:
-    case VerilatedTracePrefixType::SCOPE_INTERFACE:
-    case VerilatedTracePrefixType::STRUCT_PACKED:
-    case VerilatedTracePrefixType::STRUCT_UNPACKED:
-    case VerilatedTracePrefixType::UNION_PACKED:
-        printIndent(-1);
-        printStr("$upscope $end\n");
-        break;
-    default: break;
-    }
-    m_prefixStack.pop_back();
-    assert(!m_prefixStack.empty());  // Always one left, the constructor's initial one
-}
-
-void VerilatedVcd::declare(uint32_t code, const char* name, const char* wirep, bool array,
-                           int arraynum, bool bussed, int msb, int lsb) {
-    const int bits = ((msb > lsb) ? (msb - lsb) : (lsb - msb)) + 1;
-
-    const std::string hierarchicalName = m_prefixStack.back().first + name;
-
-    const bool enabled = Super::declCode(code, hierarchicalName, bits);
-
-    if (m_suffixes.size() <= nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE) {
-        m_suffixes.resize(nextCode() * VL_TRACE_SUFFIX_ENTRY_SIZE * 2, 0);
-    }
-
-    // Keep upper bound on bytes a single signal can emit into the buffer
-    m_maxSignalBytes = std::max(m_maxSignalBytes, bits + 32);
-    // Make sure write buffer is large enough, plus header
-    bufferResize(m_maxSignalBytes + 1024);
-
-    if (!enabled) return;
-
-    // Create the VCD code and build the suffix array entry
-    char vcdCode[VL_TRACE_SUFFIX_ENTRY_SIZE];
-    {
-        // Render the VCD code
-        char* vcdCodeWritep = vcdCode;
-        uint32_t codeEnc = code;
-        do {
-            *vcdCodeWritep++ = static_cast('!' + codeEnc % 94);
-            codeEnc /= 94;
-        } while (codeEnc--);
-        *vcdCodeWritep = '\0';
-        const size_t vcdCodeLength = vcdCodeWritep - vcdCode;
-        assert(vcdCodeLength <= VL_TRACE_MAX_VCD_CODE_SIZE);
-        // Build suffix array entry
-        char* const entryBeginp = &m_suffixes[code * VL_TRACE_SUFFIX_ENTRY_SIZE];
-        entryBeginp[0] = ' ';  // Separator
-        // 1 bit values don't have a ' ' separator between value and string code
-        char* entryWritep = bits == 1 ? entryBeginp : entryBeginp + 1;
-        // Use memcpy as we know the size, and strcpy is flagged unsafe
-        std::memcpy(entryWritep, vcdCode, vcdCodeLength);
-        entryWritep += vcdCodeLength;
-        // Line terminator
-        *entryWritep++ = '\n';
-        // Set length of suffix (used to increment write pointer)
-        assert(entryWritep <= entryBeginp + VL_TRACE_SUFFIX_ENTRY_SIZE - 1);
-        entryBeginp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1] = static_cast(entryWritep - entryBeginp);
-    }
-
-    // Assemble the declaration
-    std::string decl = "$var ";
-    decl += wirep;
-    decl += ' ';
-    decl += std::to_string(bits);
-    decl += ' ';
-    decl += vcdCode;
-    decl += ' ';
-    decl += lastWord(hierarchicalName);
-    if (array) {
-        decl += '[';
-        decl += std::to_string(arraynum);
-        decl += ']';
-    }
-    if (bussed) {
-        decl += " [";
-        decl += std::to_string(msb);
-        decl += ':';
-        decl += std::to_string(lsb);
-        decl += ']';
-    }
-    decl += " $end\n";
-    printIndent(0);
-    printStr(decl.c_str());
-}
-
-void VerilatedVcd::declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                             VerilatedTraceSigDirection, VerilatedTraceSigKind,
-                             VerilatedTraceSigType, bool array, int arraynum) {
-    declare(code, name, "event", array, arraynum, false, 0, 0);
-}
-void VerilatedVcd::declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                           VerilatedTraceSigDirection, VerilatedTraceSigKind,
-                           VerilatedTraceSigType, bool array, int arraynum) {
-    declare(code, name, "wire", array, arraynum, false, 0, 0);
-}
-void VerilatedVcd::declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                           VerilatedTraceSigDirection, VerilatedTraceSigKind,
-                           VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
-    declare(code, name, "wire", array, arraynum, true, msb, lsb);
-}
-void VerilatedVcd::declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                            VerilatedTraceSigDirection, VerilatedTraceSigKind,
-                            VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
-    declare(code, name, "wire", array, arraynum, true, msb, lsb);
-}
-void VerilatedVcd::declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                             VerilatedTraceSigDirection, VerilatedTraceSigKind,
-                             VerilatedTraceSigType, bool array, int arraynum, int msb, int lsb) {
-    declare(code, name, "wire", array, arraynum, true, msb, lsb);
-}
-void VerilatedVcd::declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                              VerilatedTraceSigDirection, VerilatedTraceSigKind,
-                              VerilatedTraceSigType, bool array, int arraynum) {
-    declare(code, name, "real", array, arraynum, false, 63, 0);
-}
-
-//=============================================================================
-// Get/commit trace buffer
-
-VerilatedVcd::Buffer* VerilatedVcd::getTraceBuffer(uint32_t fidx) {
-    VerilatedVcd::Buffer* const bufp = new Buffer{*this};
-    if (parallel()) {
-        // Note: This is called from VerilatedVcd::dump, which already holds the lock
-        // If no buffer available, allocate a new one
-        if (m_freeBuffers.empty()) {
-            // cppcheck-suppress unreadVariable  // cppcheck bug, used below
-            constexpr size_t pageSize = 4096;
-            // 4 * m_maxSignalBytes, so we can reserve 2 * m_maxSignalBytes at the end for safety
-            size_t startingSize = vlstd::roundUpToMultipleOf(4 * m_maxSignalBytes);
-            m_freeBuffers.emplace_back(new char[startingSize], startingSize);
-            ++m_numBuffers;
-        }
-        // Grab a buffer
-        const auto pair = m_freeBuffers.back();
-        m_freeBuffers.pop_back();
-        // Initialize
-        bufp->m_writep = bufp->m_bufp = pair.first;
-        bufp->m_size = pair.second;
-        bufp->adjustGrowp();
-    }
-    // Return the buffer
-    return bufp;
-}
-
-void VerilatedVcd::commitTraceBuffer(VerilatedVcd::Buffer* bufp) {
-    if (parallel()) {
-        // Note: This is called from VerilatedVcd::dump, which already holds the lock
-        // Resize output buffer. Note, we use the full size of the trace buffer, as
-        // this is a lot more stable than the actual occupancy of the trace buffer.
-        // This helps us to avoid re-allocations due to small size changes.
-        bufferResize(bufp->m_size);
-        // Compute occupancy of buffer
-        const size_t usedSize = bufp->m_writep - bufp->m_bufp;
-        // Copy to output buffer
-        std::memcpy(m_writep, bufp->m_bufp, usedSize);
-        // Adjust write pointer
-        m_writep += usedSize;
-        // Flush if necessary
-        bufferCheck();
-        // Put buffer back on free list
-        m_freeBuffers.emplace_back(bufp->m_bufp, bufp->m_size);
-    } else {
-        // Needs adjusting for emitTimeChange
-        m_writep = bufp->m_writep;
-    }
-    delete bufp;
-}
-
-//=============================================================================
-// VerilatedVcdBuffer implementation
-
-//=============================================================================
-// Trace rendering primitives
-
-static void VerilatedVcdCCopyAndAppendNewLine(char* writep,
-                                              const char* suffixp) VL_ATTR_NO_SANITIZE_ALIGN;
-
-static void VerilatedVcdCCopyAndAppendNewLine(char* writep, const char* suffixp) {
-    // Copy the whole suffix (this avoid having hard to predict branches which
-    // helps a lot). Note: The maximum length of the suffix is
-    // VL_TRACE_MAX_VCD_CODE_SIZE + 2 == 7, but we unroll this here for speed.
-#ifdef VL_X86_64
-    // Copy the whole 8 bytes in one go, this works on little-endian machines
-    // supporting unaligned stores.
-    *reinterpret_cast(writep) = *reinterpret_cast(suffixp);
-#else
-    // Portable variant
-    writep[0] = suffixp[0];
-    writep[1] = suffixp[1];
-    writep[2] = suffixp[2];
-    writep[3] = suffixp[3];
-    writep[4] = suffixp[4];
-    writep[5] = suffixp[5];
-    writep[6] = '\n';  // The 6th index is always '\n' if it's relevant, no need to fetch it.
-#endif
-}
-
-void VerilatedVcdBuffer::finishLine(uint32_t code, char* writep) {
-    const char* const suffixp = m_suffixes + code * VL_TRACE_SUFFIX_ENTRY_SIZE;
-    VL_DEBUG_IFDEF(assert(suffixp[0]););
-    VerilatedVcdCCopyAndAppendNewLine(writep, suffixp);
-
-    // Now write back the write pointer incremented by the actual size of the
-    // suffix, which was stored in the last byte of the suffix buffer entry.
-    m_writep = writep + suffixp[VL_TRACE_SUFFIX_ENTRY_SIZE - 1];
-
-    if (m_owner.parallel()) {
-        // Double the size of the buffer if necessary
-        if (VL_UNLIKELY(m_writep >= m_growp)) {
-            // Compute occupied size of current buffer
-            const size_t usedSize = m_writep - m_bufp;
-            // We are always doubling the size
-            m_size *= 2;
-            // Allocate the new buffer
-            char* const newBufp = new char[m_size];
-            // Copy from current buffer to new buffer
-            std::memcpy(newBufp, m_bufp, usedSize);
-            // Delete current buffer
-            delete[] m_bufp;
-            // Make new buffer the current buffer
-            m_bufp = newBufp;
-            // Adjust write pointer
-            m_writep = m_bufp + usedSize;
-            // Adjust resize limit
-            adjustGrowp();
-        }
-    } else {
-        // Flush the write buffer if there's not enough space left for new information
-        // We only call this once per vector, so we need enough slop for a very wide "b###" line
-        if (VL_UNLIKELY(m_writep > m_wrFlushp)) {
-            m_owner.m_writep = m_writep;
-            m_owner.bufferFlush();
-            m_writep = m_owner.m_writep;
-        }
-    }
-}
-
-//=============================================================================
-// emit* trace routines
-
-// Note: emit* are only ever called from one place (full* in
-// verilated_trace_imp.h, which is included in this file at the top),
-// so always inline them.
-
-VL_ATTR_ALWINLINE
-void VerilatedVcdBuffer::emitEvent(uint32_t code) {
-    // Don't prefetch suffix as it's a bit too late;
-    char* wp = m_writep;
-    *wp++ = '1';
-    finishLine(code, wp);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedVcdBuffer::emitBit(uint32_t code, CData newval) {
-    // Don't prefetch suffix as it's a bit too late;
-    char* wp = m_writep;
-    *wp++ = '0' | static_cast(newval);
-    finishLine(code, wp);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedVcdBuffer::emitCData(uint32_t code, CData newval, int bits) {
-    char* wp = m_writep;
-    *wp++ = 'b';
-    cvtCDataToStr(wp, newval << (VL_BYTESIZE - bits));
-    finishLine(code, wp + bits);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedVcdBuffer::emitSData(uint32_t code, SData newval, int bits) {
-    char* wp = m_writep;
-    *wp++ = 'b';
-    cvtSDataToStr(wp, newval << (VL_SHORTSIZE - bits));
-    finishLine(code, wp + bits);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedVcdBuffer::emitIData(uint32_t code, IData newval, int bits) {
-    char* wp = m_writep;
-    *wp++ = 'b';
-    cvtIDataToStr(wp, newval << (VL_IDATASIZE - bits));
-    finishLine(code, wp + bits);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedVcdBuffer::emitQData(uint32_t code, QData newval, int bits) {
-    char* wp = m_writep;
-    *wp++ = 'b';
-    cvtQDataToStr(wp, newval << (VL_QUADSIZE - bits));
-    finishLine(code, wp + bits);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedVcdBuffer::emitWData(uint32_t code, const WData* newvalp, int bits) {
-    int words = VL_WORDS_I(bits);
-    char* wp = m_writep;
-    *wp++ = 'b';
-    // Handle the most significant word
-    const int bitsInMSW = VL_BITBIT_E(bits) ? VL_BITBIT_E(bits) : VL_EDATASIZE;
-    cvtEDataToStr(wp, newvalp[--words] << (VL_EDATASIZE - bitsInMSW));
-    wp += bitsInMSW;
-    // Handle the remaining words
-    while (words > 0) {
-        cvtEDataToStr(wp, newvalp[--words]);
-        wp += VL_EDATASIZE;
-    }
-    finishLine(code, wp);
-}
-
-VL_ATTR_ALWINLINE
-void VerilatedVcdBuffer::emitDouble(uint32_t code, double newval) {
-    char* wp = m_writep;
-    // Buffer can't overflow before VL_SNPRINTF; we sized during declaration
-    VL_SNPRINTF(wp, m_maxSignalBytes, "r%.16g", newval);
-    wp += std::strlen(wp);
-    finishLine(code, wp);
-}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.h
deleted file mode 100644
index 3942055a105..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_c.h
+++ /dev/null
@@ -1,329 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated tracing in VCD format header
-///
-/// User wrapper code should use this header when creating VCD traces.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_VCD_C_H_
-#define VERILATOR_VERILATED_VCD_C_H_
-
-#include "verilated.h"
-#include "verilated_trace.h"
-
-#include 
-#include 
-
-class VerilatedVcdBuffer;
-class VerilatedVcdFile;
-
-//=============================================================================
-// VerilatedVcd
-// Base class to create a Verilator VCD dump
-// This is an internally used class - see VerilatedVcdC for what to call from applications
-
-class VerilatedVcd VL_NOT_FINAL : public VerilatedTrace {
-public:
-    using Super = VerilatedTrace;
-
-private:
-    friend VerilatedVcdBuffer;  // Give the buffer access to the private bits
-
-    //=========================================================================
-    // VCD-specific internals
-
-    VerilatedVcdFile* m_filep;  // File we're writing to
-    bool m_fileNewed;  // m_filep needs destruction
-    bool m_isOpen = false;  // True indicates open file
-    std::string m_filename;  // Filename we're writing to (if open)
-    uint64_t m_rolloverSize = 0;  // File size to rollover at
-    int m_indent = 0;  // Indentation depth
-
-    char* m_wrBufp;  // Output buffer
-    char* m_wrFlushp;  // Output buffer flush trigger location
-    char* m_writep;  // Write pointer into output buffer
-    char* m_wrTimeBeginp = nullptr;  // Write pointer for last time dump
-    char* m_wrTimeEndp = nullptr;  // Write pointer for last time dump
-    size_t m_wrChunkSize;  // Output buffer size
-    size_t m_maxSignalBytes = 0;  // Upper bound on number of bytes a single signal can generate
-    uint64_t m_wroteBytes = 0;  // Number of bytes written to this file
-
-    std::vector m_suffixes;  // VCD line end string codes + metadata
-
-    // Prefixes to add to signal names/scope types
-    std::vector> m_prefixStack{
-        {"", VerilatedTracePrefixType::SCOPE_MODULE}};
-
-    // Vector of free trace buffers as (pointer, size) pairs.
-    std::vector> m_freeBuffers;
-    size_t m_numBuffers = 0;  // Number of trace buffers allocated
-
-    void bufferResize(size_t minsize);
-    void bufferFlush() VL_MT_UNSAFE_ONE;
-    void bufferCheck() {
-        // Flush the write buffer if there's not enough space left for new information
-        // We only call this once per vector, so we need enough slop for a very wide "b###" line
-        if (VL_UNLIKELY(m_writep > m_wrFlushp)) bufferFlush();
-    }
-    void openNextImp(bool incFilename);
-    void closePrev();
-    void closeErr();
-    void printIndent(int level_change);
-    void printStr(const char* str);
-    void declare(uint32_t code, const char* name, const char* wirep, bool array, int arraynum,
-                 bool bussed, int msb, int lsb);
-
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedVcd);
-
-protected:
-    //=========================================================================
-    // Implementation of VerilatedTrace interface
-
-    // Called when the trace moves forward to a new time point
-    void emitTimeChange(uint64_t timeui) override;
-
-    // Hooks called from VerilatedTrace
-    bool preFullDump() override { return isOpen(); }
-    bool preChangeDump() override;
-
-    // Trace buffer management
-    Buffer* getTraceBuffer(uint32_t fidx) override;
-    void commitTraceBuffer(Buffer*) override;
-
-    // Configure sub-class
-    void configure(const VerilatedTraceConfig&) override {};
-
-public:
-    //=========================================================================
-    // External interface to client code
-
-    // CONSTRUCTOR
-    explicit VerilatedVcd(VerilatedVcdFile* filep = nullptr);
-    ~VerilatedVcd();
-
-    // ACCESSORS
-    // Set size in bytes after which new file should be created.
-    void rolloverSize(uint64_t size) VL_MT_SAFE { m_rolloverSize = size; }
-
-    // METHODS - All must be thread safe
-    // Open the file; call isOpen() to see if errors
-    void open(const char* filename) VL_MT_SAFE_EXCLUDES(m_mutex);
-    // Open next data-only file
-    void openNext(bool incFilename) VL_MT_SAFE_EXCLUDES(m_mutex);
-    // Close the file
-    void close() VL_MT_SAFE_EXCLUDES(m_mutex);
-    // Flush any remaining data to this file
-    void flush() VL_MT_SAFE_EXCLUDES(m_mutex);
-    // Return if file is open
-    bool isOpen() const VL_MT_SAFE { return m_isOpen; }
-
-    //=========================================================================
-    // Internal interface to Verilator generated code
-
-    void pushPrefix(const char*, VerilatedTracePrefixType);
-    void popPrefix();
-
-    void declEvent(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                   VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                   bool array, int arraynum);
-    void declBit(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                 VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                 bool array, int arraynum);
-    void declBus(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                 VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                 bool array, int arraynum, int msb, int lsb);
-    void declQuad(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                  VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                  bool array, int arraynum, int msb, int lsb);
-    void declArray(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                   VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                   bool array, int arraynum, int msb, int lsb);
-    void declDouble(uint32_t code, uint32_t fidx, const char* name, int dtypenum,
-                    VerilatedTraceSigDirection, VerilatedTraceSigKind, VerilatedTraceSigType,
-                    bool array, int arraynum);
-};
-
-#ifndef DOXYGEN
-// Declare specialization here as it's used in VerilatedFstC just below
-template <>
-void VerilatedVcd::Super::dump(uint64_t time);
-template <>
-void VerilatedVcd::Super::set_time_unit(const char* unitp);
-template <>
-void VerilatedVcd::Super::set_time_unit(const std::string& unit);
-template <>
-void VerilatedVcd::Super::set_time_resolution(const char* unitp);
-template <>
-void VerilatedVcd::Super::set_time_resolution(const std::string& unit);
-template <>
-void VerilatedVcd::Super::dumpvars(int level, const std::string& hier);
-#endif  // DOXYGEN
-
-//=============================================================================
-// VerilatedVcdBuffer
-
-class VerilatedVcdBuffer VL_NOT_FINAL {
-    // Give the trace file and sub-classes access to the private bits
-    friend VerilatedVcd;
-    friend VerilatedVcd::Super;
-    friend VerilatedVcd::Buffer;
-    friend VerilatedVcd::OffloadBuffer;
-
-    VerilatedVcd& m_owner;  // Trace file owning this buffer. Required by subclasses.
-
-    // Write pointer into output buffer (in parallel mode, this is set up in 'getTraceBuffer')
-    char* m_writep = m_owner.parallel() ? nullptr : m_owner.m_writep;
-    // Output buffer flush trigger location (only used when not parallel)
-    char* const m_wrFlushp = m_owner.parallel() ? nullptr : m_owner.m_wrFlushp;
-
-    // VCD line end string codes + metadata
-    const char* const m_suffixes = m_owner.m_suffixes.data();
-    // The maximum number of bytes a single signal can emit
-    const size_t m_maxSignalBytes = m_owner.m_maxSignalBytes;
-
-    // Additional data for parallel tracing only
-    char* m_bufp = nullptr;  // The beginning of the trace buffer
-    size_t m_size = 0;  // The size of the buffer at m_bufp
-    char* m_growp = nullptr;  // Resize limit pointer
-
-    void adjustGrowp() {
-        m_growp = (m_bufp + m_size) - (2 * m_maxSignalBytes);
-        assert(m_growp >= m_bufp + m_maxSignalBytes);
-    }
-
-    void finishLine(uint32_t code, char* writep);
-
-    // CONSTRUCTOR
-    explicit VerilatedVcdBuffer(VerilatedVcd& owner)
-        : m_owner{owner} {}
-    virtual ~VerilatedVcdBuffer() = default;
-
-    //=========================================================================
-    // Implementation of VerilatedTraceBuffer interface
-    // Implementations of duck-typed methods for VerilatedTraceBuffer. These are
-    // called from only one place (the full* methods), so always inline them.
-    VL_ATTR_ALWINLINE void emitEvent(uint32_t code);
-    VL_ATTR_ALWINLINE void emitBit(uint32_t code, CData newval);
-    VL_ATTR_ALWINLINE void emitCData(uint32_t code, CData newval, int bits);
-    VL_ATTR_ALWINLINE void emitSData(uint32_t code, SData newval, int bits);
-    VL_ATTR_ALWINLINE void emitIData(uint32_t code, IData newval, int bits);
-    VL_ATTR_ALWINLINE void emitQData(uint32_t code, QData newval, int bits);
-    VL_ATTR_ALWINLINE void emitWData(uint32_t code, const WData* newvalp, int bits);
-    VL_ATTR_ALWINLINE void emitDouble(uint32_t code, double newval);
-};
-
-//=============================================================================
-// VerilatedFile
-/// Class representing a file to write to. These virtual methods can be
-/// overrode for e.g. socket I/O.
-
-class VerilatedVcdFile VL_NOT_FINAL {
-private:
-    int m_fd = 0;  // File descriptor we're writing to
-public:
-    // METHODS
-    /// Construct a (as yet) closed file
-    VerilatedVcdFile() = default;
-    /// Close and destruct
-    virtual ~VerilatedVcdFile() = default;
-    /// Open a file with given filename
-    virtual bool open(const std::string& name) VL_MT_UNSAFE;
-    /// Close object's file
-    virtual void close() VL_MT_UNSAFE;
-    /// Write data to file (if it is open)
-    virtual ssize_t write(const char* bufp, ssize_t len) VL_MT_UNSAFE;
-};
-
-//=============================================================================
-// VerilatedVcdC
-/// Class representing a VCD dump file in C standalone (no SystemC)
-/// simulations.  Also derived for use in SystemC simulations.
-
-class VerilatedVcdC VL_NOT_FINAL : public VerilatedTraceBaseC {
-    VerilatedVcd m_sptrace;  // Trace file being created
-
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedVcdC);
-
-public:
-    /// Construct the dump. Optional argument is a preconstructed file.
-    explicit VerilatedVcdC(VerilatedVcdFile* filep = nullptr)
-        : m_sptrace{filep} {}
-    /// Destruct, flush, and close the dump
-    virtual ~VerilatedVcdC() { close(); }
-
-    // METHODS - User called
-
-    /// Return if file is open
-    bool isOpen() const override VL_MT_SAFE { return m_sptrace.isOpen(); }
-    /// Open a new VCD file
-    /// This includes a complete header dump each time it is called,
-    /// just as if this object was deleted and reconstructed.
-    virtual void open(const char* filename) VL_MT_SAFE { m_sptrace.open(filename); }
-    /// Continue a VCD dump by rotating to a new file name
-    /// The header is only in the first file created, this allows
-    /// "cat" to be used to combine the header plus any number of data files.
-    void openNext(bool incFilename = true) VL_MT_SAFE { m_sptrace.openNext(incFilename); }
-    /// Set size in bytes after which new file should be created
-    /// This will create a header file, followed by each separate file
-    /// which might be larger than the given size (due to chunking and
-    /// alignment to a start of a given time's dump).  Any file but the
-    /// first may be removed.  Cat files together to create viewable vcd.
-    void rolloverSize(size_t size) VL_MT_SAFE { m_sptrace.rolloverSize(size); }
-    /// Close dump
-    void close() VL_MT_SAFE {
-        m_sptrace.close();
-        modelConnected(false);
-    }
-    /// Flush dump
-    void flush() VL_MT_SAFE { m_sptrace.flush(); }
-    /// Write one cycle of dump data
-    /// Call with the current context's time just after eval'ed,
-    /// e.g. ->dump(contextp->time())
-    void dump(uint64_t timeui) VL_MT_SAFE { m_sptrace.dump(timeui); }
-    /// Write one cycle of dump data - backward compatible and to reduce
-    /// conversion warnings.  It's better to use a uint64_t time instead.
-    void dump(double timestamp) { dump(static_cast(timestamp)); }
-    void dump(uint32_t timestamp) { dump(static_cast(timestamp)); }
-    void dump(int timestamp) { dump(static_cast(timestamp)); }
-
-    // METHODS - Internal/backward compatible
-    // \protectedsection
-
-    // Set time units (s/ms, defaults to ns)
-    // Users should not need to call this, as for Verilated models, these
-    // propagate from the Verilated default timeunit
-    void set_time_unit(const char* unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); }
-    void set_time_unit(const std::string& unit) VL_MT_SAFE { m_sptrace.set_time_unit(unit); }
-    // Set time resolution (s/ms, defaults to ns)
-    // Users should not need to call this, as for Verilated models, these
-    // propagate from the Verilated default timeprecision
-    void set_time_resolution(const char* unit) VL_MT_SAFE { m_sptrace.set_time_resolution(unit); }
-    void set_time_resolution(const std::string& unit) VL_MT_SAFE {
-        m_sptrace.set_time_resolution(unit);
-    }
-    // Set variables to dump, using $dumpvars format
-    // If level = 0, dump everything and hier is then ignored
-    void dumpvars(int level, const std::string& hier) VL_MT_SAFE {
-        m_sptrace.dumpvars(level, hier);
-    }
-
-    // Internal class access
-    VerilatedVcd* spTrace() { return &m_sptrace; }
-};
-
-#endif  // guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.cpp
deleted file mode 100644
index e367eabc59e..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.cpp
+++ /dev/null
@@ -1,24 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated tracing in VCD Format implementation code
-///
-/// This file is deprecated, only verilated_vcd_sc.h is needed.
-/// It is provided only for backward compatibility with user's linker scripts.
-///
-//=============================================================================
-
-#ifdef VL_NO_LEGACY
-#error "verilated_vcd_sc.cpp is deprecated; verilated_vcd_sc.h is self-sufficient"
-#endif
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.h
deleted file mode 100644
index 36e4e80edff..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vcd_sc.h
+++ /dev/null
@@ -1,57 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//=============================================================================
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2001-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=============================================================================
-///
-/// \file
-/// \brief Verilated tracing in VCD format for SystemC header
-///
-/// User wrapper code should use this header when creating VCD SystemC traces.
-///
-/// This class is not threadsafe, as the SystemC kernel is not threadsafe.
-///
-//=============================================================================
-
-#ifndef VERILATOR_VERILATED_VCD_SC_H_
-#define VERILATOR_VERILATED_VCD_SC_H_
-
-#include "verilatedos.h"
-
-#include "verilated_sc_trace.h"
-#include "verilated_vcd_c.h"
-
-//=============================================================================
-// VerilatedVcdSc
-/// Trace file used to create VCD dump for SystemC version of Verilated models. It's very similar
-/// to its C version (see the class VerilatedVcdC)
-
-class VerilatedVcdSc final : VerilatedScTraceBase, public VerilatedVcdC {
-    // CONSTRUCTORS
-    VL_UNCOPYABLE(VerilatedVcdSc);
-
-public:
-    VerilatedVcdSc() {
-        spTrace()->set_time_unit(VerilatedScTraceBase::getScTimeUnit());
-        spTrace()->set_time_resolution(VerilatedScTraceBase::getScTimeResolution());
-        VerilatedScTraceBase::enableDeltaCycles(false);
-    }
-
-    // METHODS
-    // Override VerilatedVcdC. Must be called after starting simulation.
-    void open(const char* filename) override VL_MT_SAFE {
-        VerilatedScTraceBase::checkScElaborationDone();
-        VerilatedVcdC::open(filename);
-    }
-
-    // METHODS - for SC kernel
-    // Called from SystemC kernel
-    void cycle() override { VerilatedVcdC::dump(sc_core::sc_time_stamp().to_double()); }
-};
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.cpp b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.cpp
deleted file mode 100644
index 7bcee1bdc62..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.cpp
+++ /dev/null
@@ -1,4029 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2009-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=========================================================================
-///
-/// \file
-/// \brief Verilated VPI implementation code
-///
-/// This file must be compiled and linked against all Verilated objects
-/// that use the VPI.
-///
-/// Use "verilator --vpi" to add this to the Makefile for the linker.
-///
-/// For documentation on the exported functions (named vpi_*) that are
-/// implemented here, refer to the IEEE DPI chapter.
-///
-//=========================================================================
-
-#include "verilatedos.h"
-#define VERILATOR_VERILATED_VPI_CPP_
-
-#include "verilated.h"
-#include "verilated_imp.h"
-#include "verilated_vpi.h"
-
-#include "vltstd/vpi_user.h"
-
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-#include 
-
-//======================================================================
-// Internal constants
-
-#define VL_DEBUG_IF_PLI VL_DEBUG_IF
-constexpr unsigned VL_VPI_LINE_SIZE_ = 8192;
-
-//======================================================================
-// Internal macros
-
-#define VL_VPI_INTERNAL_ VerilatedVpiImp::error_info()->setMessage(vpiInternal)->setMessage
-#define VL_VPI_SYSTEM_ VerilatedVpiImp::error_info()->setMessage(vpiSystem)->setMessage
-#define VL_VPI_ERROR_ VerilatedVpiImp::error_info()->setMessage(vpiError)->setMessage
-#define VL_VPI_WARNING_ VerilatedVpiImp::error_info()->setMessage(vpiWarning)->setMessage
-#define VL_VPI_NOTICE_ VerilatedVpiImp::error_info()->setMessage(vpiNotice)->setMessage
-#define VL_VPI_ERROR_RESET_ VerilatedVpiImp::error_info()->resetError
-
-// Not supported yet
-#define VL_VPI_UNIMP_() \
-    (VL_VPI_ERROR_(__FILE__, __LINE__, Verilated::catName("Unsupported VPI function: ", __func__)))
-
-//======================================================================
-// Implementation
-
-// Base VPI handled object
-class VerilatedVpio VL_NOT_FINAL {
-    // CONSTANTS
-    // Magic value stored in front of object to detect double free etc
-    // Must be odd, as aligned pointer can never be odd
-    static constexpr uint32_t activeMagic() VL_PURE { return 0xfeed100f; }
-
-    // MEM MANGLEMENT
-    // Internal note: Globals may multi-construct, see verilated.cpp top.
-    static thread_local uint8_t* t_freeHeadp;
-
-public:
-    // CONSTRUCTORS
-    VerilatedVpio() = default;
-    virtual ~VerilatedVpio() = default;
-    static void* operator new(size_t size) VL_MT_SAFE {
-        // We new and delete tons of vpi structures, so keep them around
-        // To simplify our free list, we use a size large enough for all derived types
-        // We reserve word zero for the next pointer, as that's safer in case a
-        // dangling reference to the original remains around.
-        static constexpr size_t CHUNK_SIZE = 256;
-        if (VL_UNCOVERABLE(size > CHUNK_SIZE))
-            VL_FATAL_MT(__FILE__, __LINE__, "", "increase CHUNK_SIZE");
-        if (VL_LIKELY(t_freeHeadp)) {
-            uint8_t* const newp = t_freeHeadp;
-            t_freeHeadp = *(reinterpret_cast(newp));
-            *(reinterpret_cast(newp)) = activeMagic();
-            return newp + 8;
-        }
-        // +8: 8 bytes for next
-        uint8_t* newp = reinterpret_cast(::operator new(CHUNK_SIZE + 8));
-        *(reinterpret_cast(newp)) = activeMagic();
-        return newp + 8;
-    }
-    static void operator delete(void* obj, size_t /*size*/) VL_MT_SAFE {
-        uint8_t* const oldp = (static_cast(obj)) - 8;
-        if (VL_UNLIKELY(*(reinterpret_cast(oldp)) != activeMagic())) {
-            VL_FATAL_MT(__FILE__, __LINE__, "",
-                        "vpi_release_handle() called on same object twice, or on non-Verilator "
-                        "VPI object");
-        }
-#ifdef VL_VPI_IMMEDIATE_FREE  // Define to aid in finding leaky handles
-        ::operator delete(oldp);
-#else
-        *(reinterpret_cast(oldp)) = t_freeHeadp;
-        t_freeHeadp = oldp;
-#endif
-    }
-    // MEMBERS
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpio* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    vpiHandle castVpiHandle() { return reinterpret_cast(this); }
-    // ACCESSORS
-    virtual const char* name() const { return ""; }
-    virtual const char* fullname() const { return ""; }
-    virtual const char* defname() const { return ""; }
-    virtual uint32_t type() const { return 0; }
-    virtual uint32_t constType() const { return vpiUndefined; }
-    virtual uint32_t size() const { return 0; }
-    virtual const VerilatedRange* rangep() const { return nullptr; }
-    virtual vpiHandle dovpi_scan() { return nullptr; }
-    virtual PLI_INT32 dovpi_remove_cb() { return 0; }
-};
-
-class VerilatedVpioReasonCb final : public VerilatedVpio {
-    // A handle to a timed or non-timed callback created with vpi_register_cb
-    // User can call vpi_remove_cb or vpi_release_handle on it
-    const uint64_t m_id;  // Unique id/sequence number to find schedule's event
-    const QData m_time;  // Scheduled time, or 0 = not timed
-    const PLI_INT32 m_reason;  // VPI callback reason code
-
-public:
-    // cppcheck-suppress uninitVar  // m_value
-    VerilatedVpioReasonCb(uint64_t id, QData time, PLI_INT32 reason)
-        : m_id{id}
-        , m_time{time}
-        , m_reason{reason} {}
-    ~VerilatedVpioReasonCb() override = default;
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioReasonCb* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiCallback; }
-    PLI_INT32 dovpi_remove_cb() override;
-};
-
-class VerilatedVpioConst final : public VerilatedVpio {
-    const int32_t m_num;
-
-public:
-    explicit VerilatedVpioConst(int32_t num)
-        : m_num{num} {}
-    ~VerilatedVpioConst() override = default;
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioConst* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiConstant; }
-    uint32_t constType() const override { return vpiDecConst; }
-    int32_t num() const { return m_num; }
-};
-
-class VerilatedVpioVarBase VL_NOT_FINAL : public VerilatedVpio {
-protected:
-    const VerilatedVar* m_varp = nullptr;
-    const VerilatedScope* m_scopep = nullptr;
-
-    // Usually empty, only gets filled when fullname() is called. Has to be stored as a member so
-    // the char* that fullname() returns is not a temporary.
-    mutable std::string m_fullname;
-
-    int32_t m_indexedDim = -1;
-    const VerilatedRange* get_range() const { return m_varp->range(m_indexedDim + 1); }
-
-public:
-    VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep)
-        : m_varp{varp}
-        , m_scopep{scopep} {}
-    explicit VerilatedVpioVarBase(const VerilatedVpioVarBase* varp) {
-        if (varp) {
-            m_varp = varp->m_varp;
-            m_scopep = varp->m_scopep;
-            m_indexedDim = varp->m_indexedDim;
-        }
-    }
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioVarBase* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    const VerilatedVar* varp() const { return m_varp; }
-    const VerilatedScope* scopep() const { return m_scopep; }
-    // Returns the number of the currently indexed dimension (starting at -1 for none).
-    int32_t indexedDim() const { return m_indexedDim; }
-    // Returns whether the currently indexed dimension is unpacked.
-    bool isIndexedDimUnpacked() const { return indexedDim() + 1 < varp()->udims(); }
-    // Returns a maximum accessible dimension number, counting only unpacked dimensions
-    // (if onlyUnpacked == true), or both unpacked + packed.
-    int32_t maxDim(bool onlyUnpacked) const {
-        return onlyUnpacked ? varp()->udims() - 1 : varp()->dims() - 1;
-    }
-    // Returns a number of elements in the array, stopping at the unpacked-packed boundary.
-    uint32_t size() const override {
-        const int maxDimNum = maxDim(isIndexedDimUnpacked());
-        int size = 1;
-        for (int dim = indexedDim() + 1; dim <= maxDimNum; ++dim)
-            size *= varp()->range(dim)->elements();
-        return size;
-    }
-    // If the array is unpacked, returns the bitsize of a single underlying packed element.
-    // If the array is packed, returns the bitsize of the whole array.
-    uint32_t bitSize() const {
-        if (isIndexedDimUnpacked())
-            return varp()->entBits();
-        else
-            return size();
-    }
-    const VerilatedRange* rangep() const override { return get_range(); }
-    const char* name() const override { return m_varp->name(); }
-    const char* fullname() const override {
-        if (m_fullname.empty()) m_fullname = std::string{m_scopep->name()} + '.' + m_varp->name();
-        return m_fullname.c_str();
-    }
-    virtual void* varDatap() const { return m_varp->datap(); }
-    CData* varCDatap() const {
-        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8););
-        return reinterpret_cast(varDatap());
-    }
-    SData* varSDatap() const {
-        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16););
-        return reinterpret_cast(varDatap());
-    }
-    IData* varIDatap() const {
-        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32););
-        return reinterpret_cast(varDatap());
-    }
-    QData* varQDatap() const {
-        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64););
-        return reinterpret_cast(varDatap());
-    }
-    EData* varEDatap() const {
-        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA););
-        return reinterpret_cast(varDatap());
-    }
-    double* varRealDatap() const {
-        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL););
-        return reinterpret_cast(varDatap());
-    }
-    std::string* varStringDatap() const {
-        VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING););
-        return reinterpret_cast(varDatap());
-    }
-    virtual uint32_t bitOffset() const { return 0; }
-};
-
-class VerilatedVpioParam final : public VerilatedVpioVarBase {
-public:
-    VerilatedVpioParam(const VerilatedVar* varp, const VerilatedScope* scopep)
-        : VerilatedVpioVarBase{varp, scopep} {}
-    ~VerilatedVpioParam() override = default;
-
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioParam* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiParameter; }
-    uint32_t constType() const override {
-        switch (m_varp->vltype()) {
-        case VLVT_UINT8:
-        case VLVT_UINT16:
-        case VLVT_UINT32:
-        case VLVT_UINT64:
-        case VLVT_WDATA: return vpiDecConst;
-        case VLVT_STRING: return vpiStringConst;
-        case VLVT_REAL: return vpiRealConst;
-        default: return vpiUndefined;
-        }
-    }
-};
-
-class VerilatedVpioRange final : public VerilatedVpio {
-    const VerilatedRange* const m_rangep;
-
-public:
-    explicit VerilatedVpioRange(const VerilatedRange* rangep)
-        : m_rangep{rangep} {}
-    ~VerilatedVpioRange() override = default;
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioRange* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiRange; }
-    uint32_t size() const override { return m_rangep->elements(); }
-    const VerilatedRange* rangep() const override { return m_rangep; }
-};
-
-class VerilatedVpioRangeIter final : public VerilatedVpio {
-    const std::vector m_ranges;
-    std::vector::const_iterator m_iter;
-
-public:
-    explicit VerilatedVpioRangeIter(const std::vector& ranges)
-        : m_ranges{ranges} {
-        m_iter = m_ranges.begin();
-    }
-    ~VerilatedVpioRangeIter() override = default;
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioRangeIter* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiIterator; }
-    vpiHandle dovpi_scan() override {
-        if (VL_UNLIKELY(m_iter == m_ranges.end())) {
-            delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
-            return nullptr;
-        }
-        VerilatedRange* const rangep = new VerilatedRange{*m_iter};
-        ++m_iter;
-        return ((new VerilatedVpioRange{rangep})->castVpiHandle());
-    }
-};
-
-class VerilatedVpioScope VL_NOT_FINAL : public VerilatedVpio {
-protected:
-    const VerilatedScope* const m_scopep;
-    bool m_toplevel = false;
-    const char* m_name;
-    const char* m_fullname;
-    const char* m_defname;
-
-public:
-    explicit VerilatedVpioScope(const VerilatedScope* scopep)
-        : m_scopep{scopep} {
-        m_fullname = m_scopep->name();
-        if (std::strncmp(m_fullname, "TOP.", 4) == 0) m_fullname += 4;
-        m_name = m_scopep->identifier();
-        m_defname = m_scopep->defname();
-    }
-    ~VerilatedVpioScope() override = default;
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioScope* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiGenScope; }
-    const VerilatedScope* scopep() const { return m_scopep; }
-    const char* name() const override { return m_name; }
-    const char* fullname() const override { return m_fullname; }
-    const char* defname() const override { return m_defname; }
-    bool toplevel() const { return m_toplevel; }
-};
-
-class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase {
-    uint8_t* m_prevDatap = nullptr;  // Previous value of data, for cbValueChange
-    union {
-        uint8_t u8[4];
-        uint32_t u32;
-    } m_mask;  // memoized variable mask
-    uint32_t m_entSize = 0;  // memoized variable size
-    uint32_t m_bitOffset = 0;
-
-protected:
-    void* m_varDatap = nullptr;  // varp()->datap() adjusted for array entries
-    std::vector m_index;
-
-public:
-    VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep)
-        : VerilatedVpioVarBase{varp, scopep} {
-        m_mask.u32 = VL_MASK_I(varp->entBits());
-        m_entSize = varp->entSize();
-        m_varDatap = varp->datap();
-    }
-    explicit VerilatedVpioVar(const VerilatedVpioVar* varp)
-        : VerilatedVpioVarBase{varp} {
-        if (varp) {
-            m_mask.u32 = varp->m_mask.u32;
-            m_entSize = varp->m_entSize;
-            m_varDatap = varp->m_varDatap;
-            m_index = varp->m_index;
-            // Not copying m_prevDatap, must be nullptr
-        } else {
-            m_mask.u32 = 0;
-        }
-    }
-    ~VerilatedVpioVar() override {
-        if (m_prevDatap) VL_DO_CLEAR(delete[] m_prevDatap, m_prevDatap = nullptr);
-    }
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioVar* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t bitOffset() const override { return m_bitOffset; }
-    uint32_t mask() const { return m_mask.u32; }
-    uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; }
-    uint32_t entSize() const { return m_entSize; }
-    const std::vector& index() const { return m_index; }
-    VerilatedVpioVar* withIndex(int32_t index) const {
-        if (VL_UNLIKELY(indexedDim() + 1 >= varp()->dims())) return nullptr;
-
-        auto ret = new VerilatedVpioVar{this};
-        ret->m_index.push_back(index);
-        ret->m_indexedDim++;
-
-        int chunkSize = 1;
-        for (int dim = maxDim(isIndexedDimUnpacked()); dim > indexedDim() + 1; dim--)
-            chunkSize *= varp()->range(dim)->elements();
-
-        if (isIndexedDimUnpacked())
-            ret->m_varDatap = (static_cast(ret->m_varDatap))
-                              + entSize() * chunkSize * (index - get_range()->low());
-        else
-            ret->m_bitOffset += chunkSize * (index - get_range()->low());
-
-        return ret;
-    }
-    uint32_t type() const override {
-        uint32_t type;
-        // TODO have V3EmitCSyms.cpp put vpiType directly into constant table
-        switch (varp()->vltype()) {
-        case VLVT_REAL: type = vpiRealVar; break;
-        case VLVT_STRING: type = vpiStringVar; break;
-        default: type = varp()->isBitVar() ? vpiBitVar : vpiReg; break;
-        }
-        if (isIndexedDimUnpacked())
-            return vpiRegArray;
-        else
-            return type;
-    }
-    const char* fullname() const override {
-        static thread_local std::string t_out;
-        t_out = std::string{scopep()->name()} + "." + name();
-        for (auto idx : index()) t_out += "[" + std::to_string(idx) + "]";
-        return t_out.c_str();
-    }
-    void* prevDatap() const { return m_prevDatap; }
-    void* varDatap() const override { return m_varDatap; }
-    void createPrevDatap() {
-        if (VL_UNLIKELY(!m_prevDatap)) {
-            m_prevDatap = new uint8_t[entSize()];
-            std::memcpy(prevDatap(), m_varDatap, entSize());
-        }
-    }
-};
-
-class VerilatedVpioVarIter final : public VerilatedVpio {
-    const VerilatedScope* const m_scopep;
-    VerilatedVarNameMap::const_iterator m_it;
-    bool m_started = false;
-    const VerilatedScope* m_topscopep = nullptr;
-    bool m_onlyParams;
-
-public:
-    explicit VerilatedVpioVarIter(const VerilatedVpioScope* vop, bool onlyParams = false)
-        : m_scopep{vop->scopep()}
-        , m_onlyParams{onlyParams} {
-        if (VL_UNLIKELY(vop->toplevel()))
-            // This is a toplevel, so get TOP scope to search for ports during vpi_scan.
-            m_topscopep = Verilated::threadContextp()->scopeFind("TOP");
-    }
-    ~VerilatedVpioVarIter() override = default;
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioVarIter* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiIterator; }
-    vpiHandle dovpi_scan() override {
-        if (VL_UNLIKELY(!m_scopep->varsp())) {
-            delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
-            return nullptr;  // End of list - only one deep
-        }
-        while (true) {
-            const VerilatedVarNameMap* const varsp = m_scopep->varsp();
-            if (VL_UNLIKELY(!m_started)) {
-                m_it = varsp->begin();
-                m_started = true;
-            } else if (VL_UNLIKELY(m_it == varsp->end())) {
-                delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
-                return nullptr;
-            } else {
-                ++m_it;
-            }
-            if (VL_UNLIKELY(m_it == varsp->end())) {
-                delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
-                return nullptr;
-            }
-            if (m_onlyParams && !m_it->second.isParam()) continue;
-            if (VL_UNLIKELY(m_topscopep)) {
-                if (const VerilatedVar* topvarp = m_topscopep->varFind(m_it->second.name())) {
-                    if (topvarp->isParam()) {
-                        return ((new VerilatedVpioParam{topvarp, m_topscopep})->castVpiHandle());
-                    } else {
-                        return ((new VerilatedVpioVar{topvarp, m_topscopep})->castVpiHandle());
-                    }
-                }
-            }
-            if (m_it->second.isParam()) {
-                return ((new VerilatedVpioParam{&(m_it->second), m_scopep})->castVpiHandle());
-            } else {
-                return ((new VerilatedVpioVar{&(m_it->second), m_scopep})->castVpiHandle());
-            }
-        }
-    }
-};
-
-class VerilatedVpioRegIter final : public VerilatedVpio {
-    VerilatedVpioVar* m_var;
-    std::vector m_ranges;
-    std::vector m_nextIndex;
-    const int32_t m_maxDim;
-
-public:
-    explicit VerilatedVpioRegIter(const VerilatedVpioVar* vop)
-        : m_var{new VerilatedVpioVar(vop)}
-        , m_maxDim{vop->varp()->udims() - 1} {
-        for (auto it = vop->indexedDim() + 1; it <= m_maxDim; ++it)
-            m_ranges.push_back(*vop->varp()->range(it));
-        for (auto it : m_ranges) m_nextIndex.push_back(it.right());
-    }
-    ~VerilatedVpioRegIter() override = default;
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioRegIter* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiIterator; }
-    vpiHandle dovpi_scan() override {
-        if (VL_UNLIKELY(m_var->indexedDim() >= m_maxDim)) {
-            // Trying to iterate over a non-array object
-            delete this;
-            return nullptr;
-        }
-        if (m_nextIndex.front() > m_ranges.front().high()
-            || m_nextIndex.front() < m_ranges.front().low()) {
-            // Finished iterating
-            delete this;
-            return nullptr;
-        }
-
-        VerilatedVpioVar* ret = m_var;
-        for (auto it : m_nextIndex) ret = ret->withIndex(it);
-
-        // Increase the index, pretending the dimensions are flattened
-        for (int32_t it = m_ranges.size() - 1; it >= 0; it--) {
-            m_nextIndex.at(it) += m_ranges.at(it).increment();
-            if (m_nextIndex.at(it) <= m_ranges.at(it).high()
-                && m_nextIndex.at(it) >= m_ranges.at(it).low())
-                break;
-            else if (it > 0)
-                m_nextIndex.at(it) = m_ranges.at(it).right();
-        }
-
-        return ret->castVpiHandle();
-    }
-};
-
-class VerilatedVpioModule final : public VerilatedVpioScope {
-
-public:
-    explicit VerilatedVpioModule(const VerilatedScope* modulep)
-        : VerilatedVpioScope{modulep} {
-        // Look for '.' not inside escaped identifier
-        const std::string scopename = m_fullname;
-        std::string::size_type pos = std::string::npos;
-        size_t i = 0;
-        while (i < scopename.length()) {
-            if (scopename[i] == '\\') {
-                while (i < scopename.length() && scopename[i] != ' ') ++i;
-                ++i;  // Proc ' ', it should always be there. Then grab '.' on next cycle
-            } else {
-                while (i < scopename.length() && scopename[i] != '.') ++i;
-                if (i < scopename.length()) pos = i++;
-            }
-        }
-        if (VL_UNLIKELY(pos == std::string::npos)) m_toplevel = true;
-    }
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioModule* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiModule; }
-};
-
-class VerilatedVpioModuleIter final : public VerilatedVpio {
-    const std::vector* m_vec;
-    std::vector::const_iterator m_it;
-
-public:
-    explicit VerilatedVpioModuleIter(const std::vector& vec)
-        : m_vec{&vec} {
-        m_it = m_vec->begin();
-    }
-    ~VerilatedVpioModuleIter() override = default;
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioModuleIter* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiIterator; }
-    vpiHandle dovpi_scan() override {
-        while (true) {
-            if (m_it == m_vec->end()) {
-                delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
-                return nullptr;
-            }
-            const VerilatedScope* const modp = *m_it++;
-            const VerilatedScope::Type itype = modp->type();
-            if (itype == VerilatedScope::SCOPE_MODULE) {
-                return (new VerilatedVpioModule{modp})->castVpiHandle();
-            }
-        }
-    }
-};
-
-class VerilatedVpioScopeIter final : public VerilatedVpio {
-    const std::vector* m_vec;
-    std::vector::const_iterator m_it;
-
-public:
-    explicit VerilatedVpioScopeIter(const std::vector& vec)
-        : m_vec{&vec} {
-        m_it = m_vec->begin();
-    }
-    ~VerilatedVpioScopeIter() override = default;
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioScopeIter* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiIterator; }
-    vpiHandle dovpi_scan() override {
-        while (true) {
-            if (m_it == m_vec->end()) {
-                delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
-                return nullptr;
-            }
-            const VerilatedScope* const modp = *m_it++;
-            const VerilatedScope::Type itype = modp->type();
-            if (itype == VerilatedScope::SCOPE_OTHER) {
-                return (new VerilatedVpioScope{modp})->castVpiHandle();
-            } else if (itype == VerilatedScope::SCOPE_MODULE) {
-                return (new VerilatedVpioModule{modp})->castVpiHandle();
-            }
-        }
-    }
-};
-
-static const char* d_unit = "$unit";
-class VerilatedVpioPackage final : public VerilatedVpioScope {
-    std::string m_fullname_string;
-
-public:
-    explicit VerilatedVpioPackage(const VerilatedScope* modulep)
-        : VerilatedVpioScope{modulep} {
-        m_fullname_string = std::string{m_fullname} + "::";
-        if (m_fullname_string == "\\$unit ::") m_fullname_string = "$unit::";
-
-        if (strcmp(m_name, "\\$unit ") == 0) m_name = d_unit;
-    }
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioPackage* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    const char* fullname() const override { return m_fullname_string.c_str(); }
-    uint32_t type() const override { return vpiPackage; }
-};
-
-class VerilatedVpioInstanceIter final : public VerilatedVpio {
-    const std::vector* m_vec;
-    std::vector::const_iterator m_it;
-
-public:
-    explicit VerilatedVpioInstanceIter(const std::vector& vec)
-        : m_vec{&vec} {
-        m_it = m_vec->begin();
-    }
-    ~VerilatedVpioInstanceIter() override = default;
-    // cppcheck-suppress duplInheritedMember
-    static VerilatedVpioInstanceIter* castp(vpiHandle h) {
-        return dynamic_cast(reinterpret_cast(h));
-    }
-    uint32_t type() const override { return vpiIterator; }
-    vpiHandle dovpi_scan() override {
-        while (true) {
-            if (m_it == m_vec->end()) {
-                delete this;  // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle
-                return nullptr;
-            }
-            const VerilatedScope::Type itype = (*m_it)->type();
-            const VerilatedScope* const modp = *m_it++;
-            if (itype == VerilatedScope::SCOPE_MODULE) {
-                return (new VerilatedVpioModule{modp})->castVpiHandle();
-            }
-            if (itype == VerilatedScope::SCOPE_PACKAGE) {
-                return (new VerilatedVpioPackage{modp})->castVpiHandle();
-            }
-        }
-    }
-};
-
-//======================================================================
-
-using VerilatedPliCb = PLI_INT32 (*)(struct t_cb_data*);
-
-class VerilatedVpiCbHolder final {
-    // Holds information needed to call a callback
-    uint64_t m_id;  // Unique id/sequence number to find schedule's event, 0 = invalid
-    s_cb_data m_cbData;
-    s_vpi_value m_value;
-    VerilatedVpioVar m_varo;  // If a cbValueChange callback, the object we will return
-
-public:
-    // cppcheck-suppress uninitVar  // m_value
-    VerilatedVpiCbHolder(uint64_t id, const s_cb_data* cbDatap, const VerilatedVpioVar* varop)
-        : m_id{id}
-        , m_cbData{*cbDatap}
-        , m_varo{varop} {
-        m_value.format = cbDatap->value ? cbDatap->value->format : vpiSuppressVal;
-        m_cbData.value = &m_value;
-        if (varop) {
-            m_cbData.obj = m_varo.castVpiHandle();
-            m_varo.createPrevDatap();
-        } else {
-            m_cbData.obj = nullptr;
-        }
-    }
-    ~VerilatedVpiCbHolder() = default;
-    VerilatedPliCb cb_rtnp() const { return m_cbData.cb_rtn; }
-    s_cb_data* cb_datap() { return &m_cbData; }
-    uint64_t id() const { return m_id; }
-    bool invalid() const { return !m_id; }
-    void invalidate() { m_id = 0; }
-};
-
-class VerilatedVpiPutHolder final {
-    VerilatedVpioVar m_var;
-    s_vpi_value m_value;
-    union Storage {
-        char init = 0;  // to ensure trivial constructor
-        std::string str;
-        std::vector vec;
-        ~Storage() noexcept {/* handled by VerilatedVpiPutHolder */};
-    } m_storage{};
-
-public:
-    VerilatedVpiPutHolder(const VerilatedVpioVar* vop, p_vpi_value valuep)
-        : m_var{vop} {
-        m_value.format = valuep->format;
-        switch (valuep->format) {
-        case vpiBinStrVal:  // FALLTHRU
-        case vpiOctStrVal:  // FALLTHRU
-        case vpiDecStrVal:  // FALLTHRU
-        case vpiHexStrVal:  // FALLTHRU
-        case vpiStringVal: {
-            new (&m_storage.str) std::string{valuep->value.str};
-            m_value.value.str = const_cast(m_storage.str.c_str());
-            break;
-        }
-        case vpiScalarVal: {
-            m_value.value.scalar = valuep->value.scalar;
-            break;
-        }
-        case vpiIntVal: {
-            m_value.value.integer = valuep->value.integer;
-            break;
-        }
-        case vpiRealVal: {
-            m_value.value.real = valuep->value.real;
-            break;
-        }
-        case vpiVectorVal: {
-            size_t words = 0;
-            switch (vop->varp()->vltype()) {
-            case VLVT_UINT8:
-            case VLVT_UINT16:
-            case VLVT_UINT32: {
-                words = 1;
-                break;
-            }
-            case VLVT_UINT64: {
-                words = 2;
-                break;
-            }
-            case VLVT_WDATA: {
-                words = VL_WORDS_I(vop->varp()->entBits());
-                break;
-            }
-            default: break;
-            }
-            new (&m_storage.vec)
-                std::vector{valuep->value.vector, &valuep->value.vector[words]};
-            m_value.value.vector = m_storage.vec.data();
-            break;
-        }
-        }
-    }
-
-    VerilatedVpiPutHolder(VerilatedVpiPutHolder const& o)
-        : m_var{o.m_var}
-        , m_value{o.m_value} {
-        switch (m_value.format) {
-        case vpiBinStrVal:  // FALLTHRU
-        case vpiOctStrVal:  // FALLTHRU
-        case vpiDecStrVal:  // FALLTHRU
-        case vpiHexStrVal:  // FALLTHRU
-        case vpiStringVal: {
-            new (&m_storage.str) std::string{o.m_storage.str};
-            break;
-        }
-        case vpiVectorVal: {
-            new (&m_storage.vec) std::vector{o.m_storage.vec};
-            break;
-        }
-        }
-    }
-
-    VerilatedVpiPutHolder(VerilatedVpiPutHolder&& o) noexcept
-        : m_var{std::move(o.m_var)}
-        , m_value{std::move(o.m_value)} {
-        switch (m_value.format) {
-        case vpiBinStrVal:  // FALLTHRU
-        case vpiOctStrVal:  // FALLTHRU
-        case vpiDecStrVal:  // FALLTHRU
-        case vpiHexStrVal:  // FALLTHRU
-        case vpiStringVal: {
-            new (&m_storage.str) std::string{std::move(o.m_storage.str)};
-            break;
-        }
-        case vpiVectorVal: {
-            new (&m_storage.vec) std::vector{std::move(o.m_storage.vec)};
-            break;
-        }
-        }
-    }
-
-    ~VerilatedVpiPutHolder() noexcept {
-        switch (m_value.format) {
-        case vpiBinStrVal:  // FALLTHRU
-        case vpiOctStrVal:  // FALLTHRU
-        case vpiDecStrVal:  // FALLTHRU
-        case vpiHexStrVal:  // FALLTHRU
-        case vpiStringVal: m_storage.str.~basic_string(); break;
-        case vpiVectorVal: m_storage.vec.~vector(); break;
-        }
-    }
-
-    VerilatedVpioVar* varp() { return &m_var; }
-    p_vpi_value valuep() { return &m_value; }
-
-    static bool canInertialDelay(p_vpi_value valuep) {
-        switch (valuep->format) {
-        case vpiBinStrVal:  // FALLTHRU
-        case vpiOctStrVal:  // FALLTHRU
-        case vpiDecStrVal:  // FALLTHRU
-        case vpiHexStrVal:  // FALLTHRU
-        case vpiStringVal: {
-            if (VL_UNLIKELY(!valuep->value.str)) return false;
-            break;
-        }
-        case vpiScalarVal:  // FALLTHRU
-        case vpiIntVal:  // FALLTHRU
-        case vpiRealVal: break;
-        case vpiVectorVal: {
-            if (VL_UNLIKELY(!valuep->value.vector)) return false;
-            break;
-        }
-        default: {
-            return false;
-        }
-        }
-        return true;
-    }
-};
-
-struct VerilatedVpiTimedCbsCmp final {
-    // Ordering sets keyed by time, then callback unique id
-    bool operator()(const std::pair& a,
-                    const std::pair& b) const {
-        if (a.first < b.first) return true;
-        if (a.first > b.first) return false;
-        return a.second < b.second;
-    }
-};
-
-class VerilatedVpiError;
-void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset);
-
-class VerilatedVpiImp final {
-    enum { CB_ENUM_MAX_VALUE = cbAtEndOfSimTime + 1 };  // Maximum callback reason
-    using VpioCbList = std::list;
-    using VpioFutureCbs = std::map, VerilatedVpiCbHolder>;
-
-    // All only medium-speed, so use singleton function
-    // Callbacks that are past or at current timestamp
-    std::array m_cbCurrentLists;
-    VpioCbList m_cbCallList;  // List of callbacks currently being called by callCbs
-    VpioFutureCbs m_futureCbs;  // Time based callbacks for future timestamps
-    VpioFutureCbs m_nextCbs;  // cbNextSimTime callbacks
-    std::list m_inertialPuts;  // Pending vpi puts due to vpiInertialDelay
-    VerilatedVpiError* m_errorInfop = nullptr;  // Container for vpi error info
-    VerilatedAssertOneThread m_assertOne;  // Assert only called from single thread
-    uint64_t m_nextCallbackId = 1;  // Id to identify callback
-    bool m_evalNeeded = false;  // Model has had signals updated via vpi_put_value()
-
-    static VerilatedVpiImp& s() {  // Singleton
-        static VerilatedVpiImp s_s;
-        return s_s;
-    }
-
-public:
-    static void assertOneCheck() { s().m_assertOne.check(); }
-    static uint64_t nextCallbackId() { return ++s().m_nextCallbackId; }
-
-    static void cbCurrentAdd(uint64_t id, const s_cb_data* cb_data_p) {
-        // The passed cb_data_p was property of the user, so need to recreate
-        if (VL_UNCOVERABLE(cb_data_p->reason >= CB_ENUM_MAX_VALUE)) {
-            VL_FATAL_MT(__FILE__, __LINE__, "", "vpi bb reason too large");
-        }
-        VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64 " obj=%p\n",
-                                    cb_data_p->reason, id, cb_data_p->obj););
-        VerilatedVpioVar* varop = nullptr;
-        if (cb_data_p->reason == cbValueChange) varop = VerilatedVpioVar::castp(cb_data_p->obj);
-        s().m_cbCurrentLists[cb_data_p->reason].emplace_back(id, cb_data_p, varop);
-    }
-    static void cbFutureAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
-        // The passed cb_data_p was property of the user, so need to recreate
-        VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d id=%" PRId64 " time=%" PRIu64
-                                    " obj=%p\n",
-                                    cb_data_p->reason, id, time, cb_data_p->obj););
-        s().m_futureCbs.emplace(std::piecewise_construct, std::forward_as_tuple(time, id),
-                                std::forward_as_tuple(id, cb_data_p, nullptr));
-    }
-    static void cbNextAdd(uint64_t id, const s_cb_data* cb_data_p, QData time) {
-        // The passed cb_data_p was property of the user, so need to recreate
-        VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_register_cb reason=%d(NEXT) id=%" PRId64
-                                    " time=%" PRIu64 " obj=%p\n",
-                                    cb_data_p->reason, id, time, cb_data_p->obj););
-        s().m_nextCbs.emplace(std::piecewise_construct, std::forward_as_tuple(time, id),
-                              std::forward_as_tuple(id, cb_data_p, nullptr));
-    }
-    static void cbReasonRemove(uint64_t id, uint32_t reason, QData time) {
-        // Id might no longer exist, if already removed due to call after event, or teardown
-        // We do not remove it now as we may be iterating the list,
-        // instead set to nullptr and will cleanup later
-        // Remove from cbCurrent queue
-        for (auto& ir : s().m_cbCurrentLists[reason]) {
-            if (ir.id() == id) {
-                ir.invalidate();
-                return;  // Once found, it won't also be in m_cbCallList, m_futureCbs, or m_nextCbs
-            }
-        }
-        for (auto& ir : s().m_cbCallList) {
-            if (ir.id() == id) {
-                ir.invalidate();
-                return;  // Once found, it won't also be in m_futureCbs or m_nextCbs
-            }
-        }
-        {  // Remove from cbFuture queue
-            const auto it = s().m_futureCbs.find(std::make_pair(time, id));
-            if (it != s().m_futureCbs.end()) {
-                it->second.invalidate();
-                return;
-            }
-        }
-        {  // Remove from cbNext
-            const auto it = s().m_nextCbs.find(std::make_pair(time, id));
-            if (it != s().m_nextCbs.end()) {
-                it->second.invalidate();
-                return;
-            }
-        }
-    }
-    static void moveFutureCbs() VL_MT_UNSAFE_ONE {
-        // For any events past current time, move from cbFuture queue to cbCurrent queue
-        if (s().m_futureCbs.empty() && s().m_nextCbs.empty()) return;
-        // VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs\n"); dumpCbs(); );
-        const QData time = VL_TIME_Q();
-        for (auto it = s().m_futureCbs.begin();  //
-             VL_UNLIKELY(it != s().m_futureCbs.end() && it->first.first <= time);) {
-            VerilatedVpiCbHolder& hor = it->second;
-            const auto last_it = it;
-            ++it;
-            if (VL_UNLIKELY(!hor.invalid())) {
-                VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs id=%" PRId64 "\n", hor.id()););
-                s().m_cbCurrentLists[hor.cb_datap()->reason].emplace_back(hor);
-            }
-            s().m_futureCbs.erase(last_it);
-        }
-        for (auto it = s().m_nextCbs.begin();  //
-             VL_UNLIKELY(it != s().m_nextCbs.end() && it->first.first < time);) {
-            VerilatedVpiCbHolder& hor = it->second;
-            const auto last_it = it;
-            ++it;
-            if (VL_UNLIKELY(!hor.invalid())) {
-                VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: moveFutureCbs id=%" PRId64 "\n", hor.id()););
-                s().m_cbCurrentLists[hor.cb_datap()->reason].emplace_back(hor);
-            }
-            s().m_nextCbs.erase(last_it);
-        }
-    }
-    static QData cbNextDeadline() {
-        const auto it = s().m_futureCbs.cbegin();
-        if (VL_LIKELY(it != s().m_futureCbs.cend())) return it->first.first;
-        return ~0ULL;  // maxquad
-    }
-    static bool hasCbs(const uint32_t reason) VL_MT_UNSAFE_ONE {
-        return !s().m_cbCurrentLists[reason].empty();
-    }
-    static bool callCbs(const uint32_t reason) VL_MT_UNSAFE_ONE {
-        VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: callCbs reason=%u\n", reason););
-        assertOneCheck();
-        moveFutureCbs();
-        if (s().m_cbCurrentLists[reason].empty()) return false;
-        // Iterate on old list, making new list empty, to prevent looping over newly added elements
-        std::swap(s().m_cbCurrentLists[reason], s().m_cbCallList);
-        bool called = false;
-        for (VerilatedVpiCbHolder& ihor : s().m_cbCallList) {
-            // cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
-            if (VL_LIKELY(!ihor.invalid())) {  // Not deleted earlier
-                VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: reason_callback reason=%d id=%" PRId64 "\n",
-                                            reason, ihor.id()););
-                ihor.invalidate();  // Timed callbacks are one-shot
-                (ihor.cb_rtnp())(ihor.cb_datap());
-                called = true;
-            }
-        }
-        s().m_cbCallList.clear();
-        return called;
-    }
-    static bool callValueCbs() VL_MT_UNSAFE_ONE {
-        assertOneCheck();
-        VpioCbList& cbObjList = s().m_cbCurrentLists[cbValueChange];
-        bool called = false;
-        std::set update;  // set of objects to update after callbacks
-        if (cbObjList.empty()) return called;
-        const auto last = std::prev(cbObjList.end());  // prevent looping over newly added elements
-        for (auto it = cbObjList.begin(); true;) {
-            // cbReasonRemove sets to nullptr, so we know on removal the old end() will still exist
-            const bool was_last = it == last;
-            if (VL_UNLIKELY(it->invalid())) {  // Deleted earlier, cleanup
-                it = cbObjList.erase(it);
-                if (was_last) break;
-                continue;
-            }
-            VerilatedVpiCbHolder& ho = *it++;
-            VerilatedVpioVar* const varop
-                = reinterpret_cast(ho.cb_datap()->obj);
-            void* const newDatap = varop->varDatap();
-            void* const prevDatap = varop->prevDatap();  // Was malloced when we added the callback
-            VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_test %s v[0]=%d/%d %p %p\n",
-                                        varop->fullname(), *(static_cast(newDatap)),
-                                        *(static_cast(prevDatap)), newDatap, prevDatap););
-            if (std::memcmp(prevDatap, newDatap, varop->entSize()) != 0) {
-                VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: value_callback %" PRId64 " %s v[0]=%d\n",
-                                            ho.id(), varop->fullname(),
-                                            *(static_cast(newDatap))););
-                update.insert(varop);
-                vpi_get_value(ho.cb_datap()->obj, ho.cb_datap()->value);
-                (ho.cb_rtnp())(ho.cb_datap());
-                called = true;
-            }
-            if (was_last) break;
-        }
-        for (const VerilatedVpioVar* const ip : update) {
-            std::memcpy(ip->prevDatap(), ip->varDatap(), ip->entSize());
-        }
-        return called;
-    }
-    static void dumpCbs() VL_MT_UNSAFE_ONE;
-    static VerilatedVpiError* error_info() VL_MT_UNSAFE_ONE;  // getter for vpi error info
-    static bool evalNeeded() { return s().m_evalNeeded; }
-    static void evalNeeded(bool evalNeeded) { s().m_evalNeeded = evalNeeded; }
-    static void inertialDelay(const VerilatedVpioVar* vop, p_vpi_value valuep) {
-        s().m_inertialPuts.emplace_back(vop, valuep);
-    }
-    static void doInertialPuts() {
-        for (auto& it : s().m_inertialPuts) {
-            vpi_put_value(it.varp()->castVpiHandle(), it.valuep(), nullptr, vpiNoDelay);
-        }
-        s().m_inertialPuts.clear();
-    }
-    static auto getForceControlSignals(const VerilatedVpioVarBase* vop);
-
-    // Used in the deleter of vopGuard_t, which is invoked upon
-    // destruction of the return value of getForceControlSignals.
-    // This means that it is called at the end of vpi_get_value whenever the signal
-    // is forceable and at the end of vpi_put_value whenever the signal is both forceable and
-    // either vpiForceFlag or vpiReleaseFlag is used.
-    // Because it is always automatically called at the end, it should not
-    // erase any previously issued errors or warnings.
-    static void releaseWithoutErrorReset(vpiHandle object) {
-        VerilatedVpiImp::assertOneCheck();
-        VerilatedVpio* const vop = VerilatedVpio::castp(object);
-        VL_DO_DANGLING(delete vop, vop);
-    }
-
-    static void releaseVop(VerilatedVpioVar* vop) {
-        releaseWithoutErrorReset(vop->castVpiHandle());
-    }
-
-    using vopGuard_t = std::unique_ptr;
-
-    static std::size_t vlTypeSize(VerilatedVarType vltype);
-    static void setAllBitsToValue(const VerilatedVpioVar* vop, uint8_t bitValue) {
-        assert(bitValue == 0 || bitValue == 1);
-        const uint64_t word = (bitValue == 1) ? -1ULL : 0ULL;
-        const std::size_t wordSize = vlTypeSize(vop->varp()->vltype());
-        assert(wordSize > 0);
-        const uint32_t varBits = vop->bitSize();
-        const std::size_t numChunks = (varBits / wordSize);
-        for (std::size_t i{0}; i < numChunks; ++i) {
-            vl_vpi_put_word(vop, word, wordSize, i * wordSize);
-        }
-        // addOffset == varBits would trigger assertion in vl_vpi_var_access_info even if
-        // bitCount == 0, so first check if there is a remainder
-        if (varBits % wordSize != 0)
-            vl_vpi_put_word(vop, word, varBits % wordSize, numChunks * wordSize);
-    }
-
-    // Recreates the __VforceRd signal's data vector, since __VforceRd is not publicly accessible
-    // in Verilated code.
-    template 
-    static std::vector
-    createReadDataVector(const void* const baseSignalDatap,
-                         const std::pair forceControlDatap,
-                         const std::size_t bitCount) {
-        const void* const forceEnableDatap = forceControlDatap.first;
-        const void* const forceValueDatap = forceControlDatap.second;
-        assert(bitCount > 0);
-        const std::size_t numWords = (bitCount + (8 * sizeof(T)) - 1) / (8 * sizeof(T));  // Ceil
-        std::vector readData(numWords);
-        for (std::size_t i{0}; i < numWords; ++i) {
-            const T forceEnableWord = reinterpret_cast(forceEnableDatap)[i];
-            const T forceValueWord = reinterpret_cast(forceValueDatap)[i];
-            const T baseSignalWord = reinterpret_cast(baseSignalDatap)[i];
-            const T readDataWord
-                = (forceEnableWord & forceValueWord) | (~forceEnableWord & baseSignalWord);
-            readData[i] = readDataWord;
-        }
-        return readData;
-    }
-};
-
-//======================================================================
-// Statics
-// Internal note: Globals may multi-construct, see verilated.cpp top.
-
-thread_local uint8_t* VerilatedVpio::t_freeHeadp = nullptr;
-
-//======================================================================
-// VerilatedVpiError
-// Internal container for vpi error info
-
-class VerilatedVpiError final {
-    t_vpi_error_info m_errorInfo;
-    bool m_flag = false;
-    char m_buff[VL_VPI_LINE_SIZE_];
-    void setError(PLI_BYTE8* message, PLI_BYTE8* code, PLI_BYTE8* file, PLI_INT32 line) {
-        m_errorInfo.message = message;
-        m_errorInfo.file = file;
-        m_errorInfo.line = line;
-        m_errorInfo.code = code;
-        do_callbacks();
-    }
-    void do_callbacks() {
-        if (getError()->level >= vpiError && Verilated::threadContextp()->fatalOnVpiError()) {
-            // Stop on vpi error/unsupported
-            vpi_unsupported();
-        }
-        // We need to run above code first because in the case that the
-        // callback executes further vpi functions we will loose the error
-        // as it will be overwritten.
-        VerilatedVpiImp::callCbs(cbPLIError);
-    }
-
-public:
-    VerilatedVpiError() {
-        m_buff[0] = '\0';
-        m_errorInfo.product = const_cast(Verilated::productName());
-    }
-    ~VerilatedVpiError() = default;
-    static void selfTest() VL_MT_UNSAFE_ONE;
-    VerilatedVpiError* setMessage(PLI_INT32 level) {
-        m_flag = true;
-        m_errorInfo.level = level;
-        return this;
-    }
-    void setMessage(const std::string& file, PLI_INT32 line, const char* message, ...) {
-        // message cannot be a const string& as va_start cannot use a reference
-        static thread_local std::string t_filehold;
-        va_list args;
-        va_start(args, message);
-        VL_VSNPRINTF(m_buff, sizeof(m_buff), message, args);
-        va_end(args);
-        m_errorInfo.state = vpiPLI;
-        t_filehold = file;
-        setError(static_cast(m_buff), nullptr,
-                 const_cast(t_filehold.c_str()), line);
-    }
-    p_vpi_error_info getError() {
-        if (m_flag) return &m_errorInfo;
-        return nullptr;
-    }
-    void resetError() { m_flag = false; }
-    static void vpi_unsupported() {
-        // Not supported yet
-        const p_vpi_error_info error_info_p = VerilatedVpiImp::error_info()->getError();
-        if (error_info_p) {
-            VL_FATAL_MT(error_info_p->file, error_info_p->line, "", error_info_p->message);
-            return;
-        }
-        VL_FATAL_MT(__FILE__, __LINE__, "", "vpi_unsupported called without error info set");
-    }
-    static const char* strFromVpiVal(PLI_INT32 vpiVal) VL_PURE;
-    static const char* strFromVpiObjType(PLI_INT32 vpiVal) VL_PURE;
-    static const char* strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE;
-    static const char* strFromVpiCallbackReason(PLI_INT32 vpiVal) VL_PURE;
-    static const char* strFromVpiProp(PLI_INT32 vpiVal) VL_PURE;
-    static const char* strFromVpiConstType(PLI_INT32 vpiVal) VL_PURE;
-};
-
-//======================================================================
-// VerilatedVpi implementation
-
-bool VerilatedVpi::callCbs(uint32_t reason) VL_MT_UNSAFE_ONE {
-    return VerilatedVpiImp::callCbs(reason);
-}
-
-bool VerilatedVpi::hasCbs(uint32_t reason) VL_MT_UNSAFE_ONE {
-    return VerilatedVpiImp::hasCbs(reason);
-}
-
-// Historical, before we had multiple kinds of timed callbacks
-void VerilatedVpi::callTimedCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::callCbs(cbAfterDelay); }
-
-bool VerilatedVpi::callValueCbs() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::callValueCbs(); }
-
-QData VerilatedVpi::cbNextDeadline() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::cbNextDeadline(); }
-
-void VerilatedVpi::dumpCbs() VL_MT_UNSAFE_ONE { VerilatedVpiImp::dumpCbs(); }
-
-PLI_INT32 VerilatedVpioReasonCb::dovpi_remove_cb() {
-    VerilatedVpiImp::cbReasonRemove(m_id, m_reason, m_time);
-    delete this;  // IEEE 37.2.2 a vpi_remove_cb does a vpi_release_handle
-    return 1;
-}
-
-void VerilatedVpi::clearEvalNeeded() VL_MT_UNSAFE_ONE { VerilatedVpiImp::evalNeeded(false); }
-bool VerilatedVpi::evalNeeded() VL_MT_UNSAFE_ONE { return VerilatedVpiImp::evalNeeded(); }
-
-void VerilatedVpi::doInertialPuts() VL_MT_UNSAFE_ONE { VerilatedVpiImp::doInertialPuts(); }
-
-//======================================================================
-// VerilatedVpiImp implementation
-
-void VerilatedVpiImp::dumpCbs() VL_MT_UNSAFE_ONE {
-    assertOneCheck();
-    VL_DBG_MSGF("- vpi: dumpCbs\n");
-    for (uint32_t reason = 0; reason < CB_ENUM_MAX_VALUE; ++reason) {
-        VpioCbList& cbObjList = s().m_cbCurrentLists[reason];
-        for (auto& ho : cbObjList) {
-            if (VL_UNLIKELY(!ho.invalid())) {
-                VL_DBG_MSGF("- vpi:   reason=%d=%s  id=%" PRId64 "\n", reason,
-                            VerilatedVpiError::strFromVpiCallbackReason(reason), ho.id());
-            }
-        }
-    }
-    for (auto& ifuture : s().m_nextCbs) {
-        const QData time = ifuture.first.first;
-        VerilatedVpiCbHolder& ho = ifuture.second;
-        if (VL_UNLIKELY(!ho.invalid())) {
-            VL_DBG_MSGF("- vpi:   time=%" PRId64 "(NEXT) reason=%d=%s  id=%" PRId64 "\n", time,
-                        ho.cb_datap()->reason,
-                        VerilatedVpiError::strFromVpiCallbackReason(ho.cb_datap()->reason),
-                        ho.id());
-        }
-    }
-    for (auto& ifuture : s().m_futureCbs) {
-        const QData time = ifuture.first.first;
-        VerilatedVpiCbHolder& ho = ifuture.second;
-        if (VL_UNLIKELY(!ho.invalid())) {
-            VL_DBG_MSGF("- vpi:   time=%" PRId64 " reason=%d=%s  id=%" PRId64 "\n", time,
-                        ho.cb_datap()->reason,
-                        VerilatedVpiError::strFromVpiCallbackReason(ho.cb_datap()->reason),
-                        ho.id());
-        }
-    }
-}
-
-VerilatedVpiError* VerilatedVpiImp::error_info() VL_MT_UNSAFE_ONE {
-    VerilatedVpiImp::assertOneCheck();
-    if (VL_UNLIKELY(!s().m_errorInfop)) s().m_errorInfop = new VerilatedVpiError;
-    return s().m_errorInfop;
-}
-
-auto VerilatedVpiImp::getForceControlSignals(const VerilatedVpioVarBase* const vop) {
-    const std::string signalName = vop->fullname();
-    const std::string forceEnableSignalName = signalName + "__VforceEn";
-    const std::string forceValueSignalName = signalName + "__VforceVal";
-
-    vpiHandle const forceEnableSignalp  // NOLINT(misc-misplaced-const)
-        = vpi_handle_by_name(const_cast(forceEnableSignalName.c_str()), nullptr);
-    vpiHandle const forceValueSignalp  // NOLINT(misc-misplaced-const)
-        = vpi_handle_by_name(const_cast(forceValueSignalName.c_str()), nullptr);
-    VerilatedVpioVar* forceEnableSignalVop = VerilatedVpioVar::castp(forceEnableSignalp);
-    VerilatedVpioVar* forceValueSignalVop = VerilatedVpioVar::castp(forceValueSignalp);
-    if (VL_UNLIKELY(!forceEnableSignalVop)) {
-        VL_VPI_ERROR_(__FILE__, __LINE__,
-                      "%s: VPI force or release requested for '%s', but vpiHandle '%p' of enable "
-                      "signal '%s' could not be cast to VerilatedVpioVar*. Ensure signal is "
-                      "marked as forceable",
-                      __func__, signalName.c_str(), forceEnableSignalp,
-                      forceEnableSignalName.c_str());
-    }
-    if (VL_UNLIKELY(!forceValueSignalVop)) {
-        VL_VPI_ERROR_(__FILE__, __LINE__,
-                      "%s: VPI force or release requested for '%s', but vpiHandle '%p' of value "
-                      "signal '%s' could not be cast to VerilatedVpioVar*. Ensure signal is "
-                      "marked as forceable",
-                      __func__, signalName.c_str(), forceValueSignalp,
-                      forceValueSignalName.c_str());
-    }
-    return std::pair{vopGuard_t{forceEnableSignalVop, releaseVop},
-                                             vopGuard_t{forceValueSignalVop, releaseVop}};
-}
-
-std::size_t VerilatedVpiImp::vlTypeSize(const VerilatedVarType vltype) {
-    switch (vltype) {
-    case VLVT_UINT8: return sizeof(CData); break;
-    case VLVT_UINT16: return sizeof(SData); break;
-    case VLVT_UINT32: return sizeof(IData); break;
-    case VLVT_UINT64: return sizeof(QData); break;
-    case VLVT_WDATA: return sizeof(EData); break;
-    default:  // LCOV_EXCL_START
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, vltype);
-        return 0;
-    }  // LCOV_EXCL_STOP
-}
-//======================================================================
-// VerilatedVpiError Methods
-
-const char* VerilatedVpiError::strFromVpiVal(PLI_INT32 vpiVal) VL_PURE {
-    // clang-format off
-    static const char* const names[] = {
-        "*undefined*",
-        "vpiBinStrVal",
-        "vpiOctStrVal",
-        "vpiDecStrVal",
-        "vpiHexStrVal",
-        "vpiScalarVal",
-        "vpiIntVal",
-        "vpiRealVal",
-        "vpiStringVal",
-        "vpiVectorVal",
-        "vpiStrengthVal",
-        "vpiTimeVal",
-        "vpiObjTypeVal",
-        "vpiSuppressVal",
-        "vpiShortIntVal",
-        "vpiLongIntVal",
-        "vpiShortRealVal",
-        "vpiRawTwoStateVal",
-        "vpiRawFourStateVal",
-    };
-    // clang-format on
-    if (VL_UNCOVERABLE(vpiVal < 0)) return names[0];
-    return names[(vpiVal <= vpiRawFourStateVal) ? vpiVal : 0];
-}
-const char* VerilatedVpiError::strFromVpiObjType(PLI_INT32 vpiVal) VL_PURE {
-    // clang-format off
-    static const char* const names[] = {
-        "*undefined*",
-        "vpiAlways",
-        "vpiAssignStmt",
-        "vpiAssignment",
-        "vpiBegin",
-        "vpiCase",
-        "vpiCaseItem",
-        "vpiConstant",
-        "vpiContAssign",
-        "vpiDeassign",
-        "vpiDefParam",
-        "vpiDelayControl",
-        "vpiDisable",
-        "vpiEventControl",
-        "vpiEventStmt",
-        "vpiFor",
-        "vpiForce",
-        "vpiForever",
-        "vpiFork",
-        "vpiFuncCall",
-        "vpiFunction",
-        "vpiGate",
-        "vpiIf",
-        "vpiIfElse",
-        "vpiInitial",
-        "vpiIntegerVar",
-        "vpiInterModPath",
-        "vpiIterator",
-        "vpiIODecl",
-        "vpiMemory",
-        "vpiMemoryWord",
-        "vpiModPath",
-        "vpiModule",
-        "vpiNamedBegin",
-        "vpiNamedEvent",
-        "vpiNamedFork",
-        "vpiNet",
-        "vpiNetBit",
-        "vpiNullStmt",
-        "vpiOperation",
-        "vpiParamAssign",
-        "vpiParameter",
-        "vpiPartSelect",
-        "vpiPathTerm",
-        "vpiPort",
-        "vpiPortBit",
-        "vpiPrimTerm",
-        "vpiRealVar",
-        "vpiReg",
-        "vpiRegBit",
-        "vpiRelease",
-        "vpiRepeat",
-        "vpiRepeatControl",
-        "vpiSchedEvent",
-        "vpiSpecParam",
-        "vpiSwitch",
-        "vpiSysFuncCall",
-        "vpiSysTaskCall",
-        "vpiTableEntry",
-        "vpiTask",
-        "vpiTaskCall",
-        "vpiTchk",
-        "vpiTchkTerm",
-        "vpiTimeVar",
-        "vpiTimeQueue",
-        "vpiUdp",
-        "vpiUdpDefn",
-        "vpiUserSystf",
-        "vpiVarSelect",
-        "vpiWait",
-        "vpiWhile",
-        "vpiCondition",
-        "vpiDelay",
-        "vpiElseStmt",
-        "vpiForIncStmt",
-        "vpiForInitStmt",
-        "vpiHighConn",
-        "vpiLhs",
-        "vpiIndex",
-        "vpiLeftRange",
-        "vpiLowConn",
-        "vpiParent",
-        "vpiRhs",
-        "vpiRightRange",
-        "vpiScope",
-        "vpiSysTfCall",
-        "vpiTchkDataTerm",
-        "vpiTchkNotifier",
-        "vpiTchkRefTerm",
-        "vpiArgument",
-        "vpiBit",
-        "vpiDriver",
-        "vpiInternalScope",
-        "vpiLoad",
-        "vpiModDataPathIn",
-        "vpiModPathIn",
-        "vpiModPathOut",
-        "vpiOperand",
-        "vpiPortInst",
-        "vpiProcess",
-        "vpiVariables",
-        "vpiUse",
-        "vpiExpr",
-        "vpiPrimitive",
-        "vpiStmt",
-        "vpiAttribute",
-        "vpiBitSelect",
-        "vpiCallback",
-        "vpiDelayTerm",
-        "vpiDelayDevice",
-        "vpiFrame",
-        "vpiGateArray",
-        "vpiModuleArray",
-        "vpiPrimitiveArray",
-        "vpiNetArray",
-        "vpiRange",
-        "vpiRegArray",
-        "vpiSwitchArray",
-        "vpiUdpArray",
-        "vpiActiveTimeFormat",
-        "vpiInTerm",
-        "vpiInstanceArray",
-        "vpiLocalDriver",
-        "vpiLocalLoad",
-        "vpiOutTerm",
-        "vpiPorts",
-        "vpiSimNet",
-        "vpiTaskFunc",
-        "vpiContAssignBit",
-        "vpiNamedEventArray",
-        "vpiIndexedPartSelect",
-        "vpiBaseExpr",
-        "vpiWidthExpr",
-        "vpiGenScopeArray",
-        "vpiGenScope",
-        "vpiGenVar",
-        "vpiAutomatics"
-    };
-    static const char* const sv_names1[] = {
-        "vpiPackage",
-        "vpiInterface",
-        "vpiProgram",
-        "vpiInterfaceArray",
-        "vpiProgramArray",
-        "vpiTypespec",
-        "vpiModport",
-        "vpiInterfaceTfDecl",
-        "vpiRefObj",
-        "vpiTypeParameter",
-        "vpiLongIntVar",
-        "vpiShortIntVar",
-        "vpiIntVar",
-        "vpiShortRealVar",
-        "vpiByteVar",
-        "vpiClassVar",
-        "vpiStringVar",
-        "vpiEnumVar",
-        "vpiStructVar",
-        "vpiUnionVar",
-        "vpiBitVar",
-        "vpiClassObj",
-        "vpiChandleVar",
-        "vpiPackedArrayVar",
-        "*undefined*",  // 624 is not defined for object types
-        "vpiLongIntTypespec",
-        "vpiShortRealTypespec",
-        "vpiByteTypespec",
-        "vpiShortIntTypespec",
-        "vpiIntTypespec",
-        "vpiClassTypespec",
-        "vpiStringTypespec",
-        "vpiChandleTypespec",
-        "vpiEnumTypespec",
-        "vpiEnumConst",
-        "vpiIntegerTypespec",
-        "vpiTimeTypespec",
-        "vpiRealTypespec",
-        "vpiStructTypespec",
-        "vpiUnionTypespec",
-        "vpiBitTypespec",
-        "vpiLogicTypespec",
-        "vpiArrayTypespec",
-        "vpiVoidTypespec",
-        "vpiTypespecMember",
-        "vpiDistItem",
-        "vpiAliasStmt",
-        "vpiThread",
-        "vpiMethodFuncCall",
-        "vpiMethodTaskCall",
-        "vpiClockingBlock",
-        "vpiClockingIODecl",
-        "vpiClassDefn",
-        "vpiConstraint",
-        "vpiConstraintOrdering",
-        "vpiPropertyDecl",
-        "vpiPropertySpec",
-        "vpiPropertyExpr",
-        "vpiMulticlockSequenceExpr",
-        "vpiClockedSeq",
-        "vpiPropertyInst",
-        "vpiSequenceDecl",
-        "vpiCaseProperty",
-        "*undefined*", // 663 is not defined for object types
-        "vpiSequenceInst",
-        "vpiImmediateAssert",
-        "vpiReturn",
-        "vpiAnyPattern",
-        "vpiTaggedPattern",
-        "vpiStructPattern",
-        "vpiDoWhile",
-        "vpiOrderedWait",
-        "vpiWaitFork",
-        "vpiDisableFork",
-        "vpiExpectStmt",
-        "vpiForeachStmt",
-        "vpiFinal",
-        "vpiExtends",
-        "vpiDistribution",
-        "vpiSeqFormalDecl",
-        "vpiEnumNet",
-        "vpiIntegerNet",
-        "vpiTimeNet",
-        "vpiStructNet",
-        "vpiBreak",
-        "vpiContinue",
-        "vpiAssert",
-        "vpiAssume",
-        "vpiCover",
-        "vpiDisableCondition",
-        "vpiClockingEvent",
-        "vpiReturnStmt",
-        "vpiPackedArrayTypespec",
-        "vpiPackedArrayNet",
-        "vpiImmediateAssume",
-        "vpiImmediateCover",
-        "vpiSequenceTypespec",
-        "vpiPropertyTypespec",
-        "vpiEventTypespec",
-        "vpiPropFormalDecl",
-    };
-    // clang-format on
-    if (VL_UNCOVERABLE(vpiVal < 0))
-        return names[0];
-    else if (vpiVal <= vpiAutomatics)
-        return names[vpiVal];
-    else if (vpiVal >= vpiPackage && vpiVal <= vpiPropFormalDecl)
-        return sv_names1[(vpiVal - vpiPackage)];
-    else
-        return names[0];
-}
-const char* VerilatedVpiError::strFromVpiMethod(PLI_INT32 vpiVal) VL_PURE {
-    // clang-format off
-    static const char* const names[] = {
-        "vpiCondition",
-        "vpiDelay",
-        "vpiElseStmt",
-        "vpiForIncStmt",
-        "vpiForInitStmt",
-        "vpiHighConn",
-        "vpiLhs",
-        "vpiIndex",
-        "vpiLeftRange",
-        "vpiLowConn",
-        "vpiParent",
-        "vpiRhs",
-        "vpiRightRange",
-        "vpiScope",
-        "vpiSysTfCall",
-        "vpiTchkDataTerm",
-        "vpiTchkNotifier",
-        "vpiTchkRefTerm",
-        "vpiArgument",
-        "vpiBit",
-        "vpiDriver",
-        "vpiInternalScope",
-        "vpiLoad",
-        "vpiModDataPathIn",
-        "vpiModPathIn",
-        "vpiModPathOut",
-        "vpiOperand",
-        "vpiPortInst",
-        "vpiProcess",
-        "vpiVariables",
-        "vpiUse",
-        "vpiExpr",
-        "vpiPrimitive",
-        "vpiStmt"
-    };
-    // clang-format on
-    if (vpiVal > vpiStmt || vpiVal < vpiCondition) return "*undefined*";
-    return names[vpiVal - vpiCondition];
-}
-
-const char* VerilatedVpiError::strFromVpiCallbackReason(PLI_INT32 vpiVal) VL_PURE {
-    // clang-format off
-    static const char* const names[] = {
-        "*undefined*",
-        "cbValueChange",
-        "cbStmt",
-        "cbForce",
-        "cbRelease",
-        "cbAtStartOfSimTime",
-        "cbReadWriteSynch",
-        "cbReadOnlySynch",
-        "cbNextSimTime",
-        "cbAfterDelay",
-        "cbEndOfCompile",
-        "cbStartOfSimulation",
-        "cbEndOfSimulation",
-        "cbError",
-        "cbTchkViolation",
-        "cbStartOfSave",
-        "cbEndOfSave",
-        "cbStartOfRestart",
-        "cbEndOfRestart",
-        "cbStartOfReset",
-        "cbEndOfReset",
-        "cbEnterInteractive",
-        "cbExitInteractive",
-        "cbInteractiveScopeChange",
-        "cbUnresolvedSystf",
-        "cbAssign",
-        "cbDeassign",
-        "cbDisable",
-        "cbPLIError",
-        "cbSignal",
-        "cbNBASynch",
-        "cbAtEndOfSimTime"
-    };
-    // clang-format on
-    if (VL_UNCOVERABLE(vpiVal < 0)) return names[0];
-    return names[(vpiVal <= cbAtEndOfSimTime) ? vpiVal : 0];
-}
-
-const char* VerilatedVpiError::strFromVpiProp(PLI_INT32 vpiVal) VL_PURE {
-    // clang-format off
-    static const char* const names[] = {
-        "*undefined or other*",
-        "vpiType",
-        "vpiName",
-        "vpiFullName",
-        "vpiSize",
-        "vpiFile",
-        "vpiLineNo",
-        "vpiTopModule",
-        "vpiCellInstance",
-        "vpiDefName",
-        "vpiProtected",
-        "vpiTimeUnit",
-        "vpiTimePrecision",
-        "vpiDefNetType",
-        "vpiUnconnDrive",
-        "vpiDefFile",
-        "vpiDefLineNo",
-        "vpiScalar",
-        "vpiVector",
-        "vpiExplicitName",
-        "vpiDirection",
-        "vpiConnByName",
-        "vpiNetType",
-        "vpiExplicitScalared",
-        "vpiExplicitVectored",
-        "vpiExpanded",
-        "vpiImplicitDecl",
-        "vpiChargeStrength",
-        "vpiArray",
-        "vpiPortIndex",
-        "vpiTermIndex",
-        "vpiStrength0",
-        "vpiStrength1",
-        "vpiPrimType",
-        "vpiPolarity",
-        "vpiDataPolarity",
-        "vpiEdge",
-        "vpiPathType",
-        "vpiTchkType",
-        "vpiOpType",
-        "vpiConstType",
-        "vpiBlocking",
-        "vpiCaseType",
-        "vpiFuncType",
-        "vpiNetDeclAssign",
-        "vpiUserDefn",
-        "vpiScheduled",
-        "*undefined*",
-        "*undefined*",
-        "vpiActive",
-        "vpiAutomatic",
-        "vpiCell",
-        "vpiConfig",
-        "vpiConstantSelect",
-        "vpiDecompile",
-        "vpiDefAttribute",
-        "vpiDelayType",
-        "vpiIteratorType",
-        "vpiLibrary",
-        "*undefined*",
-        "vpiOffset",
-        "vpiResolvedNetType",
-        "vpiSaveRestartID",
-        "vpiSaveRestartLocation",
-        "vpiValid",
-        "vpiSigned",
-        "vpiStop",
-        "vpiFinish",
-        "vpiReset",
-        "vpiSetInteractiveScope",
-        "vpiLocalParam",
-        "vpiModPathHasIfNone",
-        "vpiIndexedPartSelectType",
-        "vpiIsMemory",
-        "vpiIsProtected"
-    };
-    // clang-format on
-    if (vpiVal == vpiUndefined) return "vpiUndefined";
-    return names[(vpiVal <= vpiIsProtected) ? vpiVal : 0];
-}
-const char* VerilatedVpiError::strFromVpiConstType(PLI_INT32 constType) VL_PURE {
-    // clang-format off
-    static const char* const names[] = {
-        "*undefined*",
-        "vpiDecConst",
-        "vpiRealConst",
-        "vpiBinaryConst",
-        "vpiOctConst",
-        "vpiHexConst",
-        "vpiStringConst",
-        "vpiIntConst",
-        "vpiTimeConst",
-    };
-    // clang-format on
-    if (VL_UNCOVERABLE(constType < 0)) return names[0];
-    return names[(constType <= vpiTimeConst) ? constType : 0];
-}
-
-#define SELF_CHECK_RESULT_CSTR(got, exp) \
-    if (0 != std::strcmp((got), (exp))) { \
-        const std::string msg = "%Error: GOT = '"s + (got) + "'" + "  EXP = '" + (exp) + "'"; \
-        VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); \
-    }
-
-#define SELF_CHECK_ENUM_STR(fn, enumn) \
-    do { \
-        const char* const strVal = VerilatedVpiError::fn(enumn); \
-        SELF_CHECK_RESULT_CSTR(strVal, #enumn); \
-    } while (0)
-
-void VerilatedVpi::selfTest() VL_MT_UNSAFE_ONE { VerilatedVpiError::selfTest(); }
-void VerilatedVpiError::selfTest() VL_MT_UNSAFE_ONE {
-    VerilatedVpiImp::assertOneCheck();
-
-    SELF_CHECK_ENUM_STR(strFromVpiVal, vpiBinStrVal);
-    SELF_CHECK_ENUM_STR(strFromVpiVal, vpiRawFourStateVal);
-
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAlways);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssignStmt);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssignment);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBegin);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCase);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCaseItem);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiConstant);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiContAssign);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDeassign);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDefParam);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelayControl);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDisable);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEventControl);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEventStmt);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFor);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForce);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForever);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFork);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFuncCall);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFunction);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGate);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIf);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIfElse);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInitial);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntegerVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterModPath);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIterator);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIODecl);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMemory);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMemoryWord);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModPath);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModule);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedBegin);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedEvent);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedFork);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNet);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNetBit);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNullStmt);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOperation);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiParamAssign);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiParameter);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPartSelect);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPathTerm);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPort);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPortBit);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPrimTerm);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRealVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiReg);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRegBit);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRelease);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRepeat);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRepeatControl);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSchedEvent);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSpecParam);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSwitch);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSysFuncCall);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSysTaskCall);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTableEntry);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTask);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTaskCall);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchk);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkTerm);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeQueue);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUdp);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUdpDefn);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUserSystf);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiVarSelect);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWait);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWhile);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCondition);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelay);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiElseStmt);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForIncStmt);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForInitStmt);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiHighConn);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLhs);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIndex);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLeftRange);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLowConn);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiParent);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRhs);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRightRange);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiScope);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSysTfCall);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkDataTerm);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkNotifier);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTchkRefTerm);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiArgument);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBit);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDriver);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInternalScope);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLoad);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModDataPathIn);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModPathIn);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModPathOut);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOperand);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPortInst);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiProcess);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiVariables);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUse);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiExpr);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPrimitive);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStmt);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAttribute);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitSelect);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCallback);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelayTerm);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDelayDevice);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFrame);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGateArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModuleArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPrimitiveArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNetArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRange);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRegArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSwitchArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUdpArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiActiveTimeFormat);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInTerm);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInstanceArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLocalDriver);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLocalLoad);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOutTerm);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPorts);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSimNet);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTaskFunc);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiContAssignBit);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiNamedEventArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIndexedPartSelect);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBaseExpr);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWidthExpr);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGenScopeArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGenScope);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiGenVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAutomatics);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackage);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterface);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiProgram);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterfaceArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiProgramArray);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiModport);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiInterfaceTfDecl);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRefObj);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypeParameter);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLongIntVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortIntVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortRealVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiByteVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStringVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassObj);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayVar);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLongIntTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortRealTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiByteTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiShortIntTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStringTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiChandleTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumConst);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntegerTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiRealTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiUnionTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBitTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiLogicTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiArrayTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiVoidTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTypespecMember);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDistItem);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAliasStmt);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiThread);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMethodFuncCall);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMethodTaskCall);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingBlock);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingIODecl);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClassDefn);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiConstraint);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiConstraintOrdering);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyDecl);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertySpec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyExpr);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiMulticlockSequenceExpr);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockedSeq);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyInst);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceDecl);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCaseProperty);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceInst);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateAssert);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiReturn);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAnyPattern);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTaggedPattern);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructPattern);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDoWhile);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiOrderedWait);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiWaitFork);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDisableFork);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiExpectStmt);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiForeachStmt);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiFinal);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiExtends);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDistribution);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSeqFormalDecl);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEnumNet);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiIntegerNet);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiTimeNet);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiStructNet);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiBreak);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiContinue);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssert);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiAssume);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiCover);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiDisableCondition);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiClockingEvent);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiReturnStmt);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPackedArrayNet);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateAssume);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiImmediateCover);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiSequenceTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropertyTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiEventTypespec);
-    SELF_CHECK_ENUM_STR(strFromVpiObjType, vpiPropFormalDecl);
-
-    SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiCondition);
-    SELF_CHECK_ENUM_STR(strFromVpiMethod, vpiStmt);
-
-    SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbValueChange);
-    SELF_CHECK_ENUM_STR(strFromVpiCallbackReason, cbAtEndOfSimTime);
-
-    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiType);
-    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiProtected);
-    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiDirection);
-    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiTermIndex);
-    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiConstType);
-    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiAutomatic);
-    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiOffset);
-    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiStop);
-    SELF_CHECK_ENUM_STR(strFromVpiProp, vpiIsProtected);
-
-    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiDecConst);
-    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiRealConst);
-    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiBinaryConst);
-    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiOctConst);
-    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiHexConst);
-    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiStringConst);
-    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiIntConst);
-    SELF_CHECK_ENUM_STR(strFromVpiConstType, vpiTimeConst);
-}
-
-#undef SELF_CHECK_ENUM_STR
-#undef SELF_CHECK_RESULT_CSTR
-
-//======================================================================
-// callback related
-
-vpiHandle vpi_register_cb(p_cb_data cb_data_p) {
-    // Returns handle so user can remove the callback, user must vpi_release_handle it
-    // Don't confuse with the callback-activated t_cb_data object handle
-    // which is the object causing the callback rather than the callback itself
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    // cppcheck-suppress nullPointer
-    if (VL_UNLIKELY(!cb_data_p)) {
-        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: VPI callback data pointer is null", __func__);
-        return nullptr;
-    }
-    const PLI_INT32 reason = cb_data_p->reason;
-    switch (reason) {
-    case cbAfterDelay:  // FALLTHRU // One-shot; time relative
-    case cbAtEndOfSimTime:  // FALLTHRU // One-shot; time absolute; supported via vlt_main.cpp
-    case cbAtStartOfSimTime:  // FALLTHRU // One-shot; time absolute; supported via vlt_main.cpp
-    case cbReadOnlySynch:  // FALLTHRU // One-shot; time relative; supported via vlt_main.cpp
-    case cbReadWriteSynch: {  // One-shot; time relative; supported via vlt_main.cpp
-        const bool abs = reason == cbAtStartOfSimTime || reason == cbAtEndOfSimTime;
-        const QData time = VL_TIME_Q();
-        QData abstime = 0;
-        if (cb_data_p->time) {
-            if (abs) {
-                abstime = VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
-            } else {
-                abstime = time + VL_SET_QII(cb_data_p->time->high, cb_data_p->time->low);
-            }
-        }
-        const uint64_t id = VerilatedVpiImp::nextCallbackId();
-        VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, abstime, reason};
-        if (abstime <= time) {
-            VerilatedVpiImp::cbCurrentAdd(id, cb_data_p);
-        } else {
-            VerilatedVpiImp::cbFutureAdd(id, cb_data_p, abstime);
-        }
-        return vop->castVpiHandle();
-    }
-    case cbNextSimTime: {  // One-shot; time always next; supported via vlt_main.cpp
-        const QData time = VL_TIME_Q();
-        const uint64_t id = VerilatedVpiImp::nextCallbackId();
-        VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, 0, reason};
-        VerilatedVpiImp::cbNextAdd(id, cb_data_p, time);
-        return vop->castVpiHandle();
-    }
-    case cbEndOfSimulation:  // FALLTHRU // One-shot; time ignored; supported via vlt_main.cpp
-    case cbEnterInteractive:  // FALLTHRU // NOP, but need to return handle, so make object
-    case cbExitInteractive:  // FALLTHRU // NOP, but need to return handle, so make object
-    case cbInteractiveScopeChange:  // FALLTHRU // NOP, but need to return handle, so make object
-    case cbPLIError:  // FALLTHRU // NOP, but need to return handle, so make object
-    case cbStartOfSimulation:  // FALLTHRU // One-shot; time ignored; supported via vlt_main.cpp
-    case cbValueChange: {  // Multi-shot; supported via vlt_main.cpp
-        const uint64_t id = VerilatedVpiImp::nextCallbackId();
-        VerilatedVpioReasonCb* const vop = new VerilatedVpioReasonCb{id, 0, reason};
-        VerilatedVpiImp::cbCurrentAdd(id, cb_data_p);
-        return vop->castVpiHandle();
-    }
-    default:
-        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported callback type %s", __func__,
-                        VerilatedVpiError::strFromVpiCallbackReason(reason));
-        return nullptr;
-    }
-}
-
-PLI_INT32 vpi_remove_cb(vpiHandle cb_obj) {
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_remove_cb %p\n", cb_obj););
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    VerilatedVpio* const vop = VerilatedVpio::castp(cb_obj);
-    if (VL_UNLIKELY(!vop)) return 0;
-    return vop->dovpi_remove_cb();
-}
-
-void vpi_get_cb_info(vpiHandle /*object*/, p_cb_data /*cb_data_p*/) { VL_VPI_UNIMP_(); }
-vpiHandle vpi_register_systf(p_vpi_systf_data /*systf_data_p*/) {
-    VL_VPI_UNIMP_();
-    return nullptr;
-}
-void vpi_get_systf_info(vpiHandle /*object*/, p_vpi_systf_data /*systf_data_p*/) {
-    VL_VPI_UNIMP_();
-}
-
-// for obtaining handles
-
-vpiHandle vpi_handle_by_name(PLI_BYTE8* namep, vpiHandle scope) {
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    if (VL_UNLIKELY(!namep)) return nullptr;
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_name %s %p\n", namep, scope););
-    const VerilatedVar* varp = nullptr;
-    const VerilatedScope* scopep;
-    const VerilatedVpioScope* const voScopep = VerilatedVpioScope::castp(scope);
-    std::string scopeAndName = namep;
-    if (0 == std::strncmp(namep, "$root.", std::strlen("$root."))) {
-        namep += std::strlen("$root.");
-        scopeAndName = namep;
-    } else if (voScopep) {
-        const bool scopeIsPackage = VerilatedVpioPackage::castp(scope) != nullptr;
-        scopeAndName = std::string{voScopep->fullname()} + (scopeIsPackage ? "" : ".") + namep;
-        namep = const_cast(scopeAndName.c_str());
-    }
-    {
-        // This doesn't yet follow the hierarchy in the proper way
-        bool isPackage = false;
-        scopep = Verilated::threadContextp()->scopeFind(namep);
-        if (scopep) {  // Whole thing found as a scope
-            if (scopep->type() == VerilatedScope::SCOPE_MODULE) {
-                return (new VerilatedVpioModule{scopep})->castVpiHandle();
-            } else if (scopep->type() == VerilatedScope::SCOPE_PACKAGE) {
-                return (new VerilatedVpioPackage{scopep})->castVpiHandle();
-            } else {
-                return (new VerilatedVpioScope{scopep})->castVpiHandle();
-            }
-        }
-        std::string basename = scopeAndName;
-        std::string scopename;
-        std::string::size_type prevpos = std::string::npos;
-        std::string::size_type pos = std::string::npos;
-        // Split hierarchical names at last '.' not inside escaped identifier
-        size_t i = 0;
-        while (i < scopeAndName.length()) {
-            if (scopeAndName[i] == '\\') {
-                while (i < scopeAndName.length() && scopeAndName[i] != ' ') ++i;
-                ++i;  // Proc ' ', it should always be there. Then grab '.' on next cycle
-            } else {
-                while (i < scopeAndName.length()
-                       && (scopeAndName[i] != '.'
-                           && (i + 1 >= scopeAndName.length() || scopeAndName[i] != ':'
-                               || scopeAndName[i + 1] != ':')))
-                    ++i;
-                if (i < scopeAndName.length()) {
-                    prevpos = pos;
-                    pos = i++;
-                    if (scopeAndName[i - 1] == ':') isPackage = true;
-                }
-            }
-        }
-        // Do the split
-        if (VL_LIKELY(pos != std::string::npos)) {
-            basename.erase(0, pos + (isPackage ? 2 : 1));
-            scopename = scopeAndName.substr(0, pos);
-            if (scopename == "$unit") scopename = "\\$unit ";
-        }
-        if (prevpos == std::string::npos) {
-            // scopename is a toplevel (no '.' separator), so search in our TOP ports first.
-            scopep = Verilated::threadContextp()->scopeFind("TOP");
-            if (scopep) varp = scopep->varFind(basename.c_str());
-        }
-        if (!varp) {
-            scopep = Verilated::threadContextp()->scopeFind(scopename.c_str());
-            if (!scopep) return nullptr;
-            varp = scopep->varFind(basename.c_str());
-        }
-    }
-    if (!varp) return nullptr;
-
-    if (varp->isParam()) {
-        return (new VerilatedVpioParam{varp, scopep})->castVpiHandle();
-    } else {
-        return (new VerilatedVpioVar{varp, scopep})->castVpiHandle();
-    }
-}
-
-vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) {
-    // Used to get array entries
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_index %p %d\n", object, indx););
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object);
-    if (VL_LIKELY(varop)) {
-        // Case: no dimensions left to index
-        if (VL_UNLIKELY(varop->indexedDim() + 1 > varop->varp()->dims() - 1)) return nullptr;
-
-        // Case: index out of range
-        if (VL_UNLIKELY(indx < varop->rangep()->low() || indx > varop->rangep()->high()))
-            return nullptr;
-
-        return varop->withIndex(indx)->castVpiHandle();
-    }
-    VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__);
-    return nullptr;
-}
-
-// for traversing relationships
-
-vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) {
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle %d %p\n", type, object););
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    switch (type) {
-    case vpiLeftRange: {
-        if (const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
-            if (VL_UNLIKELY(!vop->rangep())) return nullptr;
-            return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
-        } else if (const VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
-            if (VL_UNLIKELY(!vop->rangep())) return nullptr;
-            return (new VerilatedVpioConst{vop->rangep()->left()})->castVpiHandle();
-        }
-        VL_VPI_WARNING_(__FILE__, __LINE__,
-                        "%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
-                        __func__, object, VerilatedVpiError::strFromVpiMethod(type));
-        return nullptr;
-    }
-    case vpiRightRange: {
-        if (const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object)) {
-            if (VL_UNLIKELY(!vop->rangep())) return nullptr;
-            return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
-        } else if (const VerilatedVpioRange* const vop = VerilatedVpioRange::castp(object)) {
-            if (VL_UNLIKELY(!vop->rangep())) return nullptr;
-            return (new VerilatedVpioConst{vop->rangep()->right()})->castVpiHandle();
-        }
-        VL_VPI_WARNING_(__FILE__, __LINE__,
-                        "%s: Unsupported vpiHandle (%p) for type %s, nothing will be returned",
-                        __func__, object, VerilatedVpiError::strFromVpiMethod(type));
-        return nullptr;
-    }
-    case vpiIndex: {
-        const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
-        if (VL_UNLIKELY(!vop)) return nullptr;
-        const int32_t val = vop->index().back();
-        return (new VerilatedVpioConst{val})->castVpiHandle();
-    }
-    case vpiScope: {
-        const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
-        if (VL_UNLIKELY(!vop)) return nullptr;
-        return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle();
-    }
-    case vpiParent: {
-        const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
-        if (VL_UNLIKELY(!vop)) return nullptr;
-        return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle();
-    }
-    default:
-        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
-                        __func__, VerilatedVpiError::strFromVpiMethod(type));
-        return nullptr;
-    }
-}
-
-vpiHandle vpi_handle_multi(PLI_INT32 /*type*/, vpiHandle /*refHandle1*/, vpiHandle /*refHandle2*/,
-                           ...) {
-    VL_VPI_UNIMP_();
-    return nullptr;
-}
-
-vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) {
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_iterate %d %p\n", type, object););
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    switch (type) {
-    case vpiRange: {
-        const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
-        if (VL_UNLIKELY(!vop)) return nullptr;
-
-        std::vector ranges;
-        const int maxDim = vop->maxDim(vop->isIndexedDimUnpacked());
-        for (int dim = vop->indexedDim() + 1; dim <= maxDim; ++dim)
-            ranges.emplace_back(*vop->varp()->range(dim));
-
-        // allow one more range layer (regbit)
-        if (ranges.empty()) ranges.emplace_back(VerilatedRange(0, 0));
-        return ((new VerilatedVpioRangeIter{ranges})->castVpiHandle());
-    }
-    case vpiReg: {
-        const VerilatedVpioScope* const vscopep = VerilatedVpioScope::castp(object);
-        if (vscopep) return ((new VerilatedVpioVarIter{vscopep, false})->castVpiHandle());
-        const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
-        if (vop) return ((new VerilatedVpioRegIter{vop})->castVpiHandle());
-        return nullptr;
-    }
-    case vpiParameter: {
-        const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
-        if (VL_UNLIKELY(!vop)) return nullptr;
-        return ((new VerilatedVpioVarIter{vop, true})->castVpiHandle());
-    }
-    case vpiModule: {
-        const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
-        const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
-        const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
-        const auto it = vlstd::as_const(map)->find(const_cast(modp));
-        if (it == map->end()) return nullptr;
-        return ((new VerilatedVpioModuleIter{it->second})->castVpiHandle());
-    }
-    case vpiInternalScope: {
-        const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
-        const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
-        const VerilatedScope* const modp = vop ? vop->scopep() : nullptr;
-        const auto it = vlstd::as_const(map)->find(const_cast(modp));
-        if (it == map->end()) return nullptr;
-        return ((new VerilatedVpioScopeIter{it->second})->castVpiHandle());
-    }
-    case vpiInstance: {
-        if (object) return nullptr;
-        const VerilatedHierarchyMap* const map = VerilatedImp::hierarchyMap();
-        const auto it = vlstd::as_const(map)->find(nullptr);
-        if (it == map->end()) return nullptr;
-        return ((new VerilatedVpioInstanceIter{it->second})->castVpiHandle());
-    }
-    default:
-        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
-                        __func__, VerilatedVpiError::strFromVpiObjType(type));
-        return nullptr;
-    }
-}
-vpiHandle vpi_scan(vpiHandle object) {
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_scan %p\n", object););
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    VerilatedVpio* const vop = VerilatedVpio::castp(object);
-    if (VL_UNLIKELY(!vop)) return nullptr;
-    return vop->dovpi_scan();
-}
-
-// for processing properties
-
-PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) {
-    // Leave this in the header file - in many cases the compiler can constant propagate "object"
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get %d %p\n", property, object););
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    switch (property) {
-    case vpiTimePrecision: {
-        return Verilated::threadContextp()->timeprecision();
-    }
-    case vpiTimeUnit: {
-        const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object);
-        if (!vop)
-            return Verilated::threadContextp()->timeunit();  // Null asks for global, not unlikely
-        return vop->scopep()->timeunit();
-    }
-    case vpiType: {
-        const VerilatedVpio* const vop = VerilatedVpio::castp(object);
-        if (VL_UNLIKELY(!vop)) return vpiUndefined;
-        return vop->type();
-    }
-    case vpiConstType: {
-        const VerilatedVpio* const vop = VerilatedVpio::castp(object);
-        if (VL_UNLIKELY(!vop)) return vpiUndefined;
-        return vop->constType();
-    }
-    case vpiDirection: {
-        // By forethought, the directions already are vpi enumerated
-        const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
-        if (VL_UNLIKELY(!vop)) return vpiUndefined;
-        return vop->varp()->vldir();
-    }
-    case vpiScalar:  // FALLTHRU
-    case vpiVector: {
-        const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
-        if (VL_UNLIKELY(!vop)) return vpiUndefined;
-        return (property == vpiVector) ^ (vop->varp()->packedRanges().empty() || !vop->rangep());
-    }
-    case vpiSize: {
-        const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
-        if (VL_UNLIKELY(!vop)) return vpiUndefined;
-        return vop->size();
-    }
-    case vpiSigned: {
-        const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object);
-        if (VL_UNLIKELY(!vop)) return vpiUndefined;
-        return vop->varp()->isSigned();
-    }
-    default:
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported property %s, nothing will be returned",
-                      __func__, VerilatedVpiError::strFromVpiProp(property));
-        return vpiUndefined;
-    }
-}
-
-PLI_INT64 vpi_get64(PLI_INT32 /*property*/, vpiHandle /*object*/) {
-    VL_VPI_UNIMP_();
-    return vpiUndefined;
-}
-
-PLI_BYTE8* vpi_get_str(PLI_INT32 property, vpiHandle object) {
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_str %d %p\n", property, object););
-    VerilatedVpiImp::assertOneCheck();
-    const VerilatedVpio* const vop = VerilatedVpio::castp(object);
-    VL_VPI_ERROR_RESET_();
-    if (VL_UNLIKELY(!vop)) return nullptr;
-    switch (property) {
-    case vpiName: {
-        return const_cast(vop->name());
-    }
-    case vpiFullName: {
-        return const_cast(vop->fullname());
-    }
-    case vpiDefName: {
-        return const_cast(vop->defname());
-    }
-    case vpiType: {
-        return const_cast(VerilatedVpiError::strFromVpiObjType(vop->type()));
-    }
-    case vpiConstType: {
-        const PLI_INT32 constType = vpi_get(vpiConstType, object);
-        VL_VPI_ERROR_RESET_();
-        return const_cast(VerilatedVpiError::strFromVpiConstType(constType));
-    }
-    default:
-        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, nothing will be returned",
-                        __func__, VerilatedVpiError::strFromVpiProp(property));
-        return nullptr;
-    }
-}
-
-// delay processing
-
-void vpi_get_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { VL_VPI_UNIMP_(); }
-void vpi_put_delays(vpiHandle /*object*/, p_vpi_delay /*delay_p*/) { VL_VPI_UNIMP_(); }
-
-// value processing
-bool vl_check_format(const VerilatedVpioVarBase* vop, const p_vpi_value valuep, bool isGetValue) {
-    const VerilatedVar* varp = vop->varp();
-    bool status = true;
-    if ((valuep->format == vpiVectorVal) || (valuep->format == vpiBinStrVal)
-        || (valuep->format == vpiOctStrVal) || (valuep->format == vpiHexStrVal)) {
-        switch (varp->vltype()) {
-        case VLVT_UINT8:
-        case VLVT_UINT16:
-        case VLVT_UINT32:
-        case VLVT_UINT64:
-        case VLVT_WDATA: return status;
-        default: status = false;  // LCOV_EXCL_LINE
-        }
-    } else if (valuep->format == vpiDecStrVal) {
-        switch (varp->vltype()) {
-        case VLVT_UINT8:
-        case VLVT_UINT16:
-        case VLVT_UINT32:
-        case VLVT_UINT64: return status;
-        default: status = false;  // LCOV_EXCL_LINE
-        }
-    } else if (valuep->format == vpiStringVal) {
-        switch (varp->vltype()) {
-        case VLVT_UINT8:
-        case VLVT_UINT16:
-        case VLVT_UINT32:
-        case VLVT_UINT64:
-        case VLVT_WDATA: return status;
-        case VLVT_STRING:
-            // string parameter values can't be changed
-            if (isGetValue || !varp->isParam()) {
-                return status;
-            } else {
-                status = false;
-                break;
-            }
-        default: status = false;  // LCOV_EXCL_LINE
-        }
-    } else if (valuep->format == vpiIntVal) {
-        switch (varp->vltype()) {
-        case VLVT_UINT8:
-        case VLVT_UINT16:
-        case VLVT_UINT32:
-        case VLVT_UINT64:
-        case VLVT_WDATA: return status;
-        default: status = false;  // LCOV_EXCL_LINE
-        }
-    } else if (valuep->format == vpiRealVal) {
-        switch (varp->vltype()) {
-        case VLVT_REAL: return status;
-        default: status = false;  // LCOV_EXCL_LINE
-        }
-    } else if (valuep->format == vpiScalarVal) {
-        switch (varp->vltype()) {
-        case VLVT_UINT8:
-        case VLVT_UINT16:
-        case VLVT_UINT32:
-        case VLVT_UINT64:
-        case VLVT_WDATA: return status;
-        default: status = false;  // LCOV_EXCL_LINE
-        }
-    } else if (valuep->format == vpiSuppressVal) {
-        return status;
-    } else {
-        status = false;
-    }
-    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", __func__,
-                  VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
-    return status;
-}
-
-static void vl_strprintf(std::string& buffer, char const* fmt, ...) {
-    va_list args, args_copy;
-    va_start(args, fmt);
-    buffer.clear();
-    // Make copy of args since we may need to call VL_VSNPRINTF more than once
-    va_copy(args_copy, args);
-    // Try VL_VSNPRINTF in existing buffer
-    const int result
-        = VL_VSNPRINTF(const_cast(buffer.data()), buffer.capacity(), fmt, args_copy);
-    va_end(args_copy);
-    const int required = result + 1;  // Returned size doesn't include NUL terminator
-    // If there wasn't enough space, reallocate and try again
-    if (buffer.capacity() < required) {
-        buffer.reserve(required * 2);
-        VL_VSNPRINTF(const_cast(buffer.data()), buffer.capacity(), fmt, args);
-    }
-    va_end(args);
-}
-
-// Information about how to access packed array data.
-// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word
-// boundaries, in which case m_maskHi != 0.
-template 
-struct VarAccessInfo final {
-    T* m_datap;  // Typed pointer to packed array base address
-    size_t m_bitOffset;  // Data start location (bit offset)
-    size_t m_wordOffset;  // Data start location (word offset, VLVT_WDATA only)
-    T m_maskLo;  // Access mask for m_datap[m_wordOffset]
-    T m_maskHi;  // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only)
-};
-
-template 
-VarAccessInfo vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount,
-                                        size_t addOffset) {
-    // VarAccessInfo generation
-    // vop - variable to access (already indexed)
-    // bitCount - how many bits to write/read
-    // addOffset - additional offset to apply (within the packed array element)
-
-    const size_t wordBits = sizeof(T) * 8;
-    uint32_t varBits = vop->bitSize();
-
-    if (vop->varp()->vltype() == VLVT_REAL) varBits *= sizeof(double) * 8;
-
-    // make sure we're not trying to write outside var bounds
-    assert(varBits > addOffset);
-    bitCount = std::min(bitCount, varBits - addOffset);
-
-    VarAccessInfo info;
-    info.m_datap = reinterpret_cast(vop->varDatap());
-    if (vop->varp()->vltype() == VLVT_WDATA) {
-        assert(sizeof(T) == sizeof(EData));
-        assert(bitCount <= wordBits);
-        info.m_wordOffset = (vop->bitOffset() + addOffset) / wordBits;
-        info.m_bitOffset = (vop->bitOffset() + addOffset) % wordBits;
-        if (bitCount + info.m_bitOffset <= wordBits) {
-            // within single word
-            if (bitCount == wordBits)
-                info.m_maskLo = ~static_cast(0);
-            else
-                info.m_maskLo = (static_cast(1) << bitCount) - 1;
-            info.m_maskLo = info.m_maskLo << info.m_bitOffset;
-            info.m_maskHi = 0;
-        } else {
-            // straddles word boundary
-            info.m_maskLo = (static_cast(1) << (wordBits - info.m_bitOffset)) - 1;
-            info.m_maskLo = info.m_maskLo << info.m_bitOffset;
-            info.m_maskHi = (static_cast(1) << (bitCount + info.m_bitOffset - wordBits)) - 1;
-        }
-    } else {
-        info.m_wordOffset = 0;
-        info.m_bitOffset = vop->bitOffset() + addOffset;
-        assert(bitCount + info.m_bitOffset <= wordBits);
-        if (bitCount < wordBits) {
-            info.m_maskLo = (static_cast(1) << bitCount) - 1;
-            info.m_maskLo = info.m_maskLo << info.m_bitOffset;
-        } else {
-            info.m_maskLo = ~static_cast(0);
-        }
-        info.m_maskHi = 0;
-    }
-    return info;
-}
-
-template 
-T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
-    const size_t wordBits = sizeof(T) * 8;
-    const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset);
-    if (info.m_maskHi)
-        return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset)
-               | ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi)
-                  << (wordBits - info.m_bitOffset));
-    else
-        return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset;
-}
-
-template 
-void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) {
-    const size_t wordBits = sizeof(T) * 8;
-    const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset);
-
-    if (info.m_maskHi) {
-        info.m_datap[info.m_wordOffset + 1]
-            = (info.m_datap[info.m_wordOffset + 1] & ~info.m_maskHi)
-              | ((word >> (wordBits - info.m_bitOffset)) & info.m_maskHi);
-    }
-    // cppcheck-suppress unreadVariable
-    info.m_datap[info.m_wordOffset] = (info.m_datap[info.m_wordOffset] & ~info.m_maskLo)
-                                      | ((word << info.m_bitOffset) & info.m_maskLo);
-}
-
-// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds
-// addOffset: additional read bitoffset
-QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) {
-    switch (vop->varp()->vltype()) {
-    case VLVT_UINT8: return vl_vpi_get_word_gen(vop, bitCount, addOffset);
-    case VLVT_UINT16: return vl_vpi_get_word_gen(vop, bitCount, addOffset);
-    case VLVT_UINT32: return vl_vpi_get_word_gen(vop, bitCount, addOffset);
-    case VLVT_UINT64: return vl_vpi_get_word_gen(vop, bitCount, addOffset);
-    case VLVT_WDATA: return vl_vpi_get_word_gen(vop, bitCount, addOffset);
-    default:
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__,
-                      vop->varp()->vltype());
-        return 0;
-    }
-}
-
-// word: data to be written
-// bitCount: maximum number of bits to write, will stop earlier if it reaches the var bounds
-// addOffset: additional write bitoffset
-void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset) {
-    switch (vop->varp()->vltype()) {
-    case VLVT_UINT8: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break;
-    case VLVT_UINT16: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break;
-    case VLVT_UINT32: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break;
-    case VLVT_UINT64: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break;
-    case VLVT_WDATA: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break;
-    default:
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__,
-                      vop->varp()->vltype());
-    }
-}
-
-void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) {
-    const VerilatedVar* const varp = vop->varp();
-    void* const varDatap = vop->varDatap();
-
-    if (!vl_check_format(vop, valuep, true)) return;
-    // string data type is dynamic and may vary in size during simulation
-    static thread_local std::string t_outDynamicStr;
-
-    const int varBits = vop->bitSize();
-
-    // __VforceRd already has the correct value, but that signal is not public and thus not
-    // present in the scope's m_varsp map, so its value has to be recreated using the __VforceEn
-    // and __VforceVal signals.
-    // TODO: Implement a way to retrieve __VforceRd, rather than needing to recreate it.
-    const auto forceControlSignals
-        = vop->varp()->isForceable()
-              ? VerilatedVpiImp::getForceControlSignals(vop)
-              : std::pair{
-                    VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop},
-                    VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop}};
-    const VerilatedVpioVarBase* const forceEnableSignalVop = forceControlSignals.first.get();
-    const VerilatedVpioVarBase* const forceValueSignalVop = forceControlSignals.second.get();
-    t_vpi_error_info getForceControlSignalsError{};
-    const bool errorOccurred = vpi_chk_error(&getForceControlSignalsError);
-    // LCOV_EXCL_START - Cannot test, since getForceControlSignals does not (currently) produce
-    // any notices or warnings.
-    if (errorOccurred && getForceControlSignalsError.level < vpiError) {
-        vpi_printf(getForceControlSignalsError.message);
-        VL_VPI_ERROR_RESET_();
-    }  // LCOV_EXCL_STOP
-    // NOLINTNEXTLINE(readability-simplify-boolean-expr);
-    if (VL_UNLIKELY(
-            (errorOccurred && getForceControlSignalsError.level >= vpiError)
-            || (vop->varp()->isForceable() && (!forceEnableSignalVop || !forceValueSignalVop)))) {
-
-        // Check if getForceControlSignals provided any additional error info
-        const bool gotErrorMessage = vpi_chk_error(&getForceControlSignalsError);
-        const std::string previousErrorMessage
-            = gotErrorMessage
-                  ? std::string{" Error message: "} + getForceControlSignalsError.message
-                  : "";
-
-        VL_VPI_ERROR_(__FILE__, __LINE__,
-                      "%s: Signal '%s' is marked forceable, but force "
-                      "control signals could not be retrieved.%s",
-                      __func__, vop->fullname(),
-                      gotErrorMessage ? previousErrorMessage.c_str() : "");
-        return;
-    }
-
-    const std::function getForceableSignalWord
-        = [forceEnableSignalVop, forceValueSignalVop](const VerilatedVpioVarBase* baseSignalVop,
-                                                      size_t bitCount, size_t addOffset) -> QData {
-        // variables are QData, even though signals may have different representation, because any
-        // extraneous bits are simply truncated upon implicit casting when this function is called.
-        const QData baseSignalData = vl_vpi_get_word(baseSignalVop, bitCount, addOffset);
-        const QData forceEnableData = vl_vpi_get_word(forceEnableSignalVop, bitCount, addOffset);
-        const QData forceValueData = vl_vpi_get_word(forceValueSignalVop, bitCount, addOffset);
-        const QData readData
-            = (forceEnableData & forceValueData) | (~forceEnableData & baseSignalData);
-        return readData;
-    };
-
-    const std::function get_word
-        = vop->varp()->isForceable() ? getForceableSignalWord : vl_vpi_get_word;
-
-    // We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal
-    // This may cause backward compatibility issues with older code.
-    if (valuep->format == vpiVectorVal) {
-        // Vector pointer must come from our memory pool
-        // It only needs to persist until the next vpi_get_value
-        static thread_local t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2];
-        valuep->value.vector = t_out;
-        if (varp->vltype() == VLVT_WDATA) {
-            const int words = VL_WORDS_I(varBits);
-            if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) {
-                VL_VPI_ERROR_(
-                    __FILE__, __LINE__,
-                    "vpi_get_value with more than VL_VALUE_STRING_MAX_WORDS; increase and "
-                    "recompile");
-                return;
-            }
-            for (int i = 0; i < words; ++i) {
-                t_out[i].aval = get_word(vop, 32, i * 32);
-                t_out[i].bval = 0;
-            }
-            return;
-        } else if (varp->vltype() == VLVT_UINT64 && varBits > 32) {
-            const QData data = get_word(vop, 64, 0);
-            t_out[1].aval = static_cast(data >> 32ULL);
-            t_out[1].bval = 0;
-            t_out[0].aval = static_cast(data);
-            t_out[0].bval = 0;
-            return;
-        } else {
-            t_out[0].aval = get_word(vop, 32, 0);
-            t_out[0].bval = 0;
-            return;
-        }
-    } else if (valuep->format == vpiBinStrVal) {
-        t_outDynamicStr.resize(varBits);
-
-        static thread_local std::vector forceReadCData;
-        forceReadCData
-            = vop->varp()->isForceable()
-                  ? VerilatedVpiImp::createReadDataVector(
-                        varDatap,
-                        {forceEnableSignalVop->varDatap(), forceValueSignalVop->varDatap()},
-                        vop->bitSize())
-                  : std::vector{};
-        const uint8_t* const varCDatap = vop->varp()->isForceable()
-                                             ? forceReadCData.data()
-                                             : reinterpret_cast(varDatap);
-
-        const CData* datap = varCDatap;
-        for (size_t i = 0; i < varBits; ++i) {
-            const size_t pos = i + vop->bitOffset();
-            const char val = (datap[pos >> 3] >> (pos & 7)) & 1;
-            t_outDynamicStr[varBits - i - 1] = val ? '1' : '0';
-        }
-        valuep->value.str = const_cast(t_outDynamicStr.c_str());
-        return;
-    } else if (valuep->format == vpiOctStrVal) {
-        const int chars = (varBits + 2) / 3;
-        t_outDynamicStr.resize(chars);
-        for (size_t i = 0; i < chars; ++i) {
-            const char val = get_word(vop, 3, i * 3);
-            t_outDynamicStr[chars - i - 1] = '0' + val;
-        }
-        valuep->value.str = const_cast(t_outDynamicStr.c_str());
-        return;
-    } else if (valuep->format == vpiDecStrVal) {
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_strprintf(t_outDynamicStr, "%hhu", static_cast(get_word(vop, 8, 0)));
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_strprintf(t_outDynamicStr, "%hu",
-                         static_cast(get_word(vop, 16, 0)));
-        } else if (varp->vltype() == VLVT_UINT32) {
-            vl_strprintf(t_outDynamicStr, "%u", static_cast(get_word(vop, 32, 0)));
-        } else if (varp->vltype() == VLVT_UINT64) {
-            vl_strprintf(t_outDynamicStr, "%llu",  // lintok-format-ll
-                         static_cast(get_word(vop, 64, 0)));
-        }
-        valuep->value.str = const_cast(t_outDynamicStr.c_str());
-        return;
-    } else if (valuep->format == vpiHexStrVal) {
-        const int chars = (varBits + 3) >> 2;
-        t_outDynamicStr.resize(chars);
-        for (size_t i = 0; i < chars; ++i) {
-            const char val = get_word(vop, 4, i * 4);
-            t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast(val)];
-        }
-        valuep->value.str = const_cast(t_outDynamicStr.c_str());
-        return;
-    } else if (valuep->format == vpiStringVal) {
-        if (varp->vltype() == VLVT_STRING) {
-            if (varp->isParam()) {
-                valuep->value.str = reinterpret_cast(varDatap);
-                return;
-            } else {
-                t_outDynamicStr = *vop->varStringDatap();
-                valuep->value.str = const_cast(t_outDynamicStr.c_str());
-                return;
-            }
-        } else {
-            const int chars = VL_BYTES_I(varBits);
-            t_outDynamicStr.resize(chars);
-            for (size_t i = 0; i < chars; ++i) {
-                const char val = get_word(vop, 8, i * 8);
-                // other simulators replace [leading?] zero chars with spaces, replicate here.
-                t_outDynamicStr[chars - i - 1] = val ? val : ' ';
-            }
-            valuep->value.str = const_cast(t_outDynamicStr.c_str());
-            return;
-        }
-    } else if (valuep->format == vpiIntVal) {
-        valuep->value.integer = get_word(vop, 32, 0);
-        return;
-    } else if (valuep->format == vpiRealVal) {
-        // Only cover the scalar case, since reals cannot be packed (IEEE 1800, section 7.4.1), and
-        // unpacked arrays are not supported for forcing in Verilator (#4735).
-        if (vop->varp()->isForceable() && *forceEnableSignalVop->varCDatap())
-            valuep->value.real = *forceValueSignalVop->varRealDatap();
-        else
-            valuep->value.real = *vop->varRealDatap();
-
-        return;
-    } else if (valuep->format == vpiScalarVal) {
-        valuep->value.scalar = get_word(vop, 32, 0) ? vpi1 : vpi0;
-        return;
-    } else if (valuep->format == vpiSuppressVal) {
-        return;
-    }
-    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
-                  VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
-}
-
-void vpi_get_value(vpiHandle object, p_vpi_value valuep) {
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value %p\n", object););
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    if (VL_UNLIKELY(!valuep)) return;
-
-    if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) {
-        vl_vpi_get_value(vop, valuep);
-        return;
-    } else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
-        vl_vpi_get_value(vop, valuep);
-        return;
-    } else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
-        if (valuep->format == vpiIntVal) {
-            valuep->value.integer = vop->num();
-            return;
-        }
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) for %s", __func__,
-                      VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname());
-        return;
-    }
-    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
-}
-
-vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_p*/,
-                        PLI_INT32 flags) {
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value %p %p\n", object, valuep););
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    if (VL_UNLIKELY(!valuep)) {
-        VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer");
-        return nullptr;
-    }
-    const PLI_INT32 delay_mode = flags & 0xfff;
-    const PLI_INT32 forceFlag = flags & 0xfff;
-    if (const VerilatedVpioVar* const baseSignalVop = VerilatedVpioVar::castp(object)) {
-        VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi:   vpi_put_value name=%s fmt=%d vali=%d\n",
-                                    baseSignalVop->fullname(), valuep->format,
-                                    valuep->value.integer);
-                        VL_DBG_MSGF("- vpi:   varp=%p  putatp=%p\n",
-                                    baseSignalVop->varp()->datap(), baseSignalVop->varDatap()););
-
-        if (VL_UNLIKELY(!baseSignalVop->varp()->isPublicRW())) {
-            VL_VPI_ERROR_(__FILE__, __LINE__,
-                          "vpi_put_value was used on signal marked read-only,"
-                          " use public_flat_rw instead for '%s'",
-                          baseSignalVop->fullname());
-            return nullptr;
-        }
-
-        // NOLINTNEXTLINE(readability-simplify-boolean-expr);
-        if (VL_UNLIKELY((forceFlag == vpiForceFlag || forceFlag == vpiReleaseFlag)
-                        && !baseSignalVop->varp()->isForceable())) {
-            VL_VPI_ERROR_("", 0, "vpi_put_value used with %s on non-forceable signal '%s'",
-                          forceFlag == vpiForceFlag ? "vpiForceFlag" : "vpiReleaseFlag",
-                          baseSignalVop->fullname());
-            return nullptr;
-        }
-        if (!vl_check_format(baseSignalVop, valuep, false)) return nullptr;
-        if (delay_mode == vpiInertialDelay) {
-            if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) {
-                VL_VPI_WARNING_(
-                    __FILE__, __LINE__,
-                    "%s: Unsupported p_vpi_value as requested for '%s' with vpiInertialDelay",
-                    __func__, baseSignalVop->fullname());
-                return nullptr;
-            }
-            VerilatedVpiImp::inertialDelay(baseSignalVop, valuep);
-            return object;
-        }
-        VerilatedVpiImp::evalNeeded(true);
-        const int varBits = baseSignalVop->bitSize();
-
-        const auto forceControlSignals
-            = baseSignalVop->varp()->isForceable()
-                      && (forceFlag == vpiForceFlag || forceFlag == vpiReleaseFlag)
-                  ? VerilatedVpiImp::getForceControlSignals(baseSignalVop)
-                  : std::pair{
-                        VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop},
-                        VerilatedVpiImp::vopGuard_t{nullptr, VerilatedVpiImp::releaseVop}};
-        const VerilatedVpioVar* const forceEnableSignalVop = forceControlSignals.first.get();
-        const VerilatedVpioVar* const forceValueSignalVop = forceControlSignals.second.get();
-        t_vpi_error_info getForceControlSignalsError{};
-        bool errorOccurred = vpi_chk_error(&getForceControlSignalsError);
-        // LCOV_EXCL_START - Cannot test, since getForceControlSignals does not (currently) produce
-        // any notices or warnings.
-        if (errorOccurred && getForceControlSignalsError.level < vpiError) {
-            vpi_printf(getForceControlSignalsError.message);
-            VL_VPI_ERROR_RESET_();
-        }  // LCOV_EXCL_STOP
-        // NOLINTNEXTLINE(readability-simplify-boolean-expr);
-        if (VL_UNLIKELY(baseSignalVop->varp()->isForceable()
-                        && (forceFlag == vpiForceFlag || forceFlag == vpiReleaseFlag)
-                        && (!forceEnableSignalVop || !forceValueSignalVop))) {
-
-            // Check if getForceControlSignals provided any additional error info
-            const bool gotErrorMessage = vpi_chk_error(&getForceControlSignalsError);
-            const std::string previousErrorMessage
-                = gotErrorMessage
-                      ? std::string{" Error message: "} + getForceControlSignalsError.message
-                      : "";
-
-            VL_VPI_ERROR_(__FILE__, __LINE__,
-                          "%s: Signal '%s' with vpiHandle '%p' is marked forceable, but force "
-                          "control signals could not be retrieved.%s",
-                          __func__, baseSignalVop->fullname(), object,
-                          gotErrorMessage ? previousErrorMessage.c_str() : "");
-            return nullptr;
-        }
-
-        const VerilatedVpioVar* const valueVop
-            = (forceFlag == vpiForceFlag) ? forceValueSignalVop : baseSignalVop;
-
-        if (forceFlag == vpiForceFlag) {
-            // Enable __VforceEn
-            VerilatedVpiImp::setAllBitsToValue(forceEnableSignalVop, 1);
-        }
-        if (forceFlag == vpiReleaseFlag) {
-            // If signal is continuously assigned, first clear the force enable bits, then get the
-            // (non-forced) value. Else, get the (still forced) value first, then clear the force
-            // enable bits.
-
-            if (baseSignalVop->varp()->isContinuously())
-                VerilatedVpiImp::setAllBitsToValue(forceEnableSignalVop, 0);
-
-            vl_vpi_get_value(baseSignalVop, valuep);
-
-            t_vpi_error_info baseValueGetError{};
-            errorOccurred = vpi_chk_error(&baseValueGetError);
-            // LCOV_EXCL_START - Cannot test, because missing signal would already trigger error
-            // earlier, at the getForceControlSignals stage
-            // NOLINTNEXTLINE(readability-simplify-boolean-expr);
-            if (VL_UNLIKELY(errorOccurred && baseValueGetError.level >= vpiError)) {
-                const std::string baseValueSignalName = baseSignalVop->fullname();
-                const std::string previousErrorMessage = baseValueGetError.message;
-                VL_VPI_ERROR_(__FILE__, __LINE__,
-                              "%s: Could not retrieve value of signal '%s' with "
-                              "vpiHandle '%p'. Error message: %s",
-                              __func__, baseValueSignalName.c_str(), object,
-                              previousErrorMessage.c_str());
-                return nullptr;
-            }
-            // NOLINTNEXTLINE(readability-simplify-boolean-expr);
-            if (VL_UNCOVERABLE(errorOccurred && baseValueGetError.level < vpiError)) {
-                vpi_printf(baseValueGetError.message);
-                VL_VPI_ERROR_RESET_();
-            }  // LCOV_EXCL_STOP
-
-            if (!baseSignalVop->varp()->isContinuously())
-                VerilatedVpiImp::setAllBitsToValue(forceEnableSignalVop, 0);
-
-            return nullptr;
-        }
-
-        if (valuep->format == vpiVectorVal) {
-            if (VL_UNLIKELY(!valuep->value.vector)) return nullptr;
-            if (valueVop->varp()->vltype() == VLVT_WDATA) {
-                const int words = VL_WORDS_I(varBits);
-                for (int i = 0; i < words; ++i)
-                    vl_vpi_put_word(valueVop, valuep->value.vector[i].aval, 32, i * 32);
-                return object;
-            } else if (valueVop->varp()->vltype() == VLVT_UINT64 && varBits > 32) {
-                const QData val = (static_cast(valuep->value.vector[1].aval) << 32)
-                                  | static_cast(valuep->value.vector[0].aval);
-                vl_vpi_put_word(valueVop, val, 64, 0);
-                return object;
-            } else {
-                vl_vpi_put_word(valueVop, valuep->value.vector[0].aval, 32, 0);
-                return object;
-            }
-        } else if (valuep->format == vpiBinStrVal) {
-            const int len = std::strlen(valuep->value.str);
-            CData* const datap = reinterpret_cast(valueVop->varDatap());
-            for (int i = 0; i < varBits; ++i) {
-                const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1');
-                const size_t pos = valueVop->bitOffset() + i;
-
-                if (set)
-                    datap[pos >> 3] |= 1 << (pos & 7);
-                else
-                    datap[pos >> 3] &= ~(1 << (pos & 7));
-            }
-            return object;
-        } else if (valuep->format == vpiOctStrVal) {
-            const int len = std::strlen(valuep->value.str);
-            for (int i = 0; i < len; ++i) {
-                unsigned char digit = valuep->value.str[len - i - 1] - '0';
-                if (digit > 7) {  // If str was < '0', then as unsigned, digit > 7
-                    VL_VPI_WARNING_(__FILE__, __LINE__,
-                                    "%s: Non octal character '%c' in '%s' as value %s for %s",
-                                    __func__, digit + '0', valuep->value.str,
-                                    VerilatedVpiError::strFromVpiVal(valuep->format),
-                                    valueVop->fullname());
-                    digit = 0;
-                }
-                vl_vpi_put_word(valueVop, digit, 3, i * 3);
-            }
-            return object;
-        } else if (valuep->format == vpiDecStrVal) {
-            char remainder[16];
-            unsigned long long val;
-            const int success = std::sscanf(valuep->value.str, "%30llu%15s",  // lintok-format-ll
-                                            &val, remainder);
-            if (success < 1) {
-                VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Parsing failed for '%s' as value %s for %s",
-                              __func__, valuep->value.str,
-                              VerilatedVpiError::strFromVpiVal(valuep->format),
-                              valueVop->fullname());
-                return nullptr;
-            }
-            if (success > 1) {
-                VL_VPI_WARNING_(
-                    __FILE__, __LINE__, "%s: Trailing garbage '%s' in '%s' as value %s for %s",
-                    __func__, remainder, valuep->value.str,
-                    VerilatedVpiError::strFromVpiVal(valuep->format), valueVop->fullname());
-            }
-            vl_vpi_put_word(valueVop, val, 64, 0);
-            return object;
-        } else if (valuep->format == vpiHexStrVal) {
-            const int chars = (varBits + 3) >> 2;
-            const char* val = valuep->value.str;
-            // skip hex ident if one is detected at the start of the string
-            if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2;
-            const int len = std::strlen(val);
-            for (int i = 0; i < chars; ++i) {
-                char hex;
-                // compute hex digit value
-                if (i < len) {
-                    const char digit = val[len - i - 1];
-                    if (digit >= '0' && digit <= '9') {
-                        hex = digit - '0';
-                    } else if (digit >= 'a' && digit <= 'f') {
-                        hex = digit - 'a' + 10;
-                    } else if (digit >= 'A' && digit <= 'F') {
-                        hex = digit - 'A' + 10;
-                    } else {
-                        VL_VPI_WARNING_(__FILE__, __LINE__,
-                                        "%s: Non hex character '%c' in '%s' as value %s for %s",
-                                        __func__, digit, valuep->value.str,
-                                        VerilatedVpiError::strFromVpiVal(valuep->format),
-                                        valueVop->fullname());
-                        hex = 0;
-                    }
-                } else {
-                    hex = 0;
-                }
-                // assign hex digit value to destination
-                vl_vpi_put_word(valueVop, hex, 4, i * 4);
-            }
-            return object;
-        } else if (valuep->format == vpiStringVal) {
-            if (valueVop->varp()->vltype() == VLVT_STRING) {
-                // Does not use valueVop, because strings are not forceable anyway
-                *(baseSignalVop->varStringDatap()) = valuep->value.str;
-                return object;
-            } else {
-                const int chars = VL_BYTES_I(varBits);
-                const int len = std::strlen(valuep->value.str);
-                for (int i = 0; i < chars; ++i) {
-                    // prepend with 0 values before placing string the least significant bytes
-                    const char c = (i < len) ? valuep->value.str[len - i - 1] : 0;
-                    vl_vpi_put_word(valueVop, c, 8, i * 8);
-                }
-            }
-            return object;
-        } else if (valuep->format == vpiIntVal) {
-            vl_vpi_put_word(valueVop, valuep->value.integer, 64, 0);
-            return object;
-        } else if (valuep->format == vpiRealVal) {
-            if (valueVop->varp()->vltype() == VLVT_REAL) {
-                *(valueVop->varRealDatap()) = valuep->value.real;
-                return object;
-            }
-        } else if (valuep->format == vpiScalarVal) {
-            vl_vpi_put_word(valueVop, (valuep->value.scalar == vpi1 ? 1 : 0), 1, 0);
-            return object;
-        }
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s",
-                      __func__, VerilatedVpiError::strFromVpiVal(valuep->format),
-                      valueVop->fullname());
-        return nullptr;
-    } else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) {
-        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiParameter: %s",
-                        __func__, vop->fullname());
-        return nullptr;
-    } else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) {
-        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Ignoring vpi_put_value to vpiConstant: %s",
-                        __func__, vop->fullname());
-        return nullptr;
-    }
-    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
-    return nullptr;
-}
-
-bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arrayvalue_p,
-                           const char* fullname) {
-    if (arrayvalue_p->format == vpiVectorVal) {
-        switch (varp->vltype()) {
-        case VLVT_UINT8:
-        case VLVT_UINT16:
-        case VLVT_UINT32:
-        case VLVT_UINT64:
-        case VLVT_WDATA: return true;
-        default:;  // LCOV_EXCL_LINE
-        }
-    } else if (arrayvalue_p->format == vpiIntVal) {
-        switch (varp->vltype()) {
-        case VLVT_UINT8:
-        case VLVT_UINT16:
-        case VLVT_UINT32: return true;
-        default:;  // LCOV_EXCL_LINE
-        }
-    } else if ((arrayvalue_p->format == vpiRawTwoStateVal)
-               || (arrayvalue_p->format == vpiRawFourStateVal)) {
-        switch (varp->vltype()) {
-        case VLVT_UINT8:
-        case VLVT_UINT16:
-        case VLVT_UINT32:
-        case VLVT_UINT64:
-        case VLVT_WDATA: return true;
-        default:;  // LCOV_EXCL_LINE
-        }
-    } else if (arrayvalue_p->format == vpiShortIntVal) {
-        switch (varp->vltype()) {
-        case VLVT_UINT8:
-        case VLVT_UINT16: return true;
-        default:;  // LCOV_EXCL_LINE
-        }
-    } else if (arrayvalue_p->format == vpiLongIntVal) {
-        switch (varp->vltype()) {
-        case VLVT_UINT8:
-        case VLVT_UINT16:
-        case VLVT_UINT32:
-        case VLVT_UINT64: return true;
-        default:;  // LCOV_EXCL_LINE
-        }
-    }
-
-    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
-                  VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), fullname);
-
-    return false;
-}
-
-template 
-void vl_get_value_array_integrals(unsigned index, const unsigned num, const unsigned size,
-                                  const unsigned packedSize, const bool leftIsLow, const T* src,
-                                  K* dst) {
-    static_assert(sizeof(K) >= sizeof(T), "size of type K is less than size of type T");
-    for (int i = 0; i < num; ++i) {
-        dst[i] = src[index];
-        index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
-                : index == 0 ? size - 1
-                             : index - 1;
-    }
-}
-
-template 
-void vl_put_value_array_integrals(unsigned index, const unsigned num, const unsigned size,
-                                  const unsigned packedSize, const bool leftIsLow, const T* src,
-                                  K* dst) {
-    static_assert(std::is_integral::value, "type T is not an integral type");
-    static_assert(std::is_unsigned::value, "type T is not unsigned");
-    static_assert(sizeof(T) >= sizeof(K), "size of type T is less than size of type K");
-    const unsigned element_size_bytes = VL_BYTES_I(packedSize);
-    const T mask = element_size_bytes == sizeof(T)
-                       ? static_cast(-1)
-                       : ~(static_cast(-1) << (element_size_bytes * 8));
-    for (unsigned i = 0; i < num; ++i) {
-        dst[index] = src[i] & static_cast(mask);
-        index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
-                : index == 0 ? size - 1
-                             : index - 1;
-    }
-}
-
-template 
-void vl_get_value_array_vectors(unsigned index, const unsigned num, const unsigned size,
-                                const unsigned packedSize, const bool leftIsLow, const T* src,
-                                p_vpi_vecval dst) {
-    static_assert(std::is_unsigned::value,
-                  "type T is not unsigned");  // ensure logical right shift
-    const unsigned element_size_words = VL_WORDS_I(packedSize);
-    if (sizeof(T) == sizeof(QData)) {
-        for (unsigned i = 0; i < num; ++i) {
-            dst[i * 2].aval = static_cast(src[index]);
-            dst[i * 2].bval = 0;
-            dst[(i * 2) + 1].aval = static_cast(src[index]) >> 32;
-            dst[(i * 2) + 1].bval = 0;
-            index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
-                    : index == 0 ? size - 1
-                                 : index - 1;
-        }
-    } else {
-        for (unsigned i = 0; i < num; ++i) {
-            const size_t dst_index = i * element_size_words;
-            const size_t src_index = index * element_size_words;
-            for (unsigned j = 0; j < element_size_words; ++j) {
-                dst[dst_index + j].aval = src[src_index + j];
-                dst[dst_index + j].bval = 0;
-            }
-            index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
-                    : index == 0 ? size - 1
-                                 : index - 1;
-        }
-    }
-}
-
-template 
-void vl_put_value_array_vectors(unsigned index, const unsigned num, const unsigned size,
-                                const unsigned packedSize, const bool leftIsLow,
-                                const bool fourState, const p_vpi_vecval src, T* dst) {
-    static_assert(std::is_unsigned::value, "type T is not unsigned");
-    static_assert(std::is_integral::value, "type T is not an integral type");
-    const unsigned element_size_bytes VL_BYTES_I(packedSize);
-    const unsigned element_size_words VL_WORDS_I(packedSize);
-    if (sizeof(T) == sizeof(QData)) {  //destination is QDATA
-        const QData mask = element_size_bytes == sizeof(T)
-                               ? static_cast(-1)
-                               : ~(static_cast(-1) << (element_size_bytes * 8));
-        for (unsigned i = 0; i < num; ++i) {
-            dst[index] = src[i * 2].aval;
-            dst[index]
-                |= (static_cast(src[(i * 2) + 1].aval) << (sizeof(PLI_UINT32) * 8)) & mask;
-            index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
-                    : index == 0 ? size - 1
-                                 : index - 1;
-        }
-    } else {
-        for (unsigned i = 0; i < num; ++i) {
-            unsigned bytes_stored = 0;
-            for (unsigned j = 0; j < element_size_words; ++j) {
-                if (bytes_stored >= element_size_bytes) break;
-                const T mask
-                    = (element_size_bytes - bytes_stored) >= sizeof(PLI_UINT32)
-                          ? static_cast(-1)
-                          : ~(static_cast(-1) << ((element_size_bytes - bytes_stored) * 8));
-                dst[(index * element_size_words) + j]
-                    = static_cast(src[(i * element_size_words) + j].aval) & mask;
-                bytes_stored += sizeof(PLI_UINT32);
-            }
-            index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
-                    : index == 0 ? size - 1
-                                 : index - 1;
-        }
-    }
-}
-
-template 
-void vl_get_value_array_rawvals(unsigned index, unsigned num, const unsigned size,
-                                const unsigned packedSize, const bool leftIsLow,
-                                const bool fourState, const T* src, PLI_BYTE8* dst) {
-    static_assert(std::is_unsigned::value,
-                  "type T is not unsigned");  //ensure logical right shift
-    const unsigned element_size_bytes VL_BYTES_I(packedSize);
-    const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T);
-    size_t dst_index = 0;
-    while (num-- > 0) {
-        const size_t src_offset = index * element_size_repr;
-        unsigned bytes_copied = 0;
-        for (unsigned j = 0; j < element_size_repr; ++j) {
-            const T& src_data = src[src_offset + j];
-            for (unsigned k = 0; k < sizeof(T); ++k) {
-                if (bytes_copied++ == element_size_bytes) break;
-                dst[dst_index++] = src_data >> (k * 8);
-            }
-        }
-        if (fourState) {
-            std::fill(dst + dst_index, dst + dst_index + element_size_bytes, 0);
-            dst_index += element_size_bytes;
-        }
-        index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
-                : index == 0 ? size - 1
-                             : index - 1;
-    }
-}
-
-template 
-void vl_put_value_array_rawvals(unsigned index, const unsigned num, const unsigned size,
-                                const unsigned packedSize, const bool leftIsLow,
-                                const bool fourState, const PLI_UBYTE8* src, T* dst) {
-    const unsigned element_size_bytes VL_BYTES_I(packedSize);
-    const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T);
-    for (unsigned i = 0; i < num; ++i) {
-        unsigned bytes_copied = 0;
-        const size_t dst_offset = index * element_size_repr;
-        const size_t src_offset = i * element_size_bytes;
-        for (unsigned j = 0; j < element_size_repr; ++j) {
-            T& dst_data = dst[dst_offset + j];
-            for (unsigned k = 0; k < sizeof(T); ++k) {
-                if (bytes_copied == element_size_bytes) break;
-                const unsigned src_index
-                    = fourState ? (src_offset * 2) + bytes_copied : (src_offset) + bytes_copied;
-                dst_data &= ~((static_cast(0xFF) & 0xFF) << (k * 8));
-                dst_data |= ((static_cast(src[src_index]) & 0xFF) << (k * 8));
-                bytes_copied++;
-            }
-        }
-        index = leftIsLow    ? index == (size - 1) ? 0 : index + 1
-                : index == 0 ? size - 1
-                             : index - 1;
-    }
-}
-
-void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const PLI_INT32* index_p,
-                        PLI_UINT32 num) {
-    const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
-    if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
-
-    const VerilatedVar* const varp = vop->varp();
-
-    static thread_local EData t_out_data[VL_VALUE_STRING_MAX_WORDS * 2];
-
-    const unsigned size = vop->size();
-    if (VL_UNCOVERABLE(num > size)) {
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Requested elements (%u) exceed array size (%u)",
-                      __func__, num, size);
-        return;
-    }
-
-    const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low();
-    const int index
-        = leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0];
-
-    if (arrayvalue_p->format == vpiShortIntVal) {
-        if (VL_UNCOVERABLE((sizeof(PLI_INT16) * num) >= VL_VALUE_STRING_MAX_CHARS)) {
-            VL_FATAL_MT(__FILE__, __LINE__, "",
-                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
-                        "increase and recompile");
-        }
-
-        PLI_INT16* shortintsp = reinterpret_cast(t_out_data);
-        arrayvalue_p->value.shortints = shortintsp;
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
-                                         vop->varCDatap(), shortintsp);
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
-                                         vop->varSDatap(), shortintsp);
-        }
-
-        return;
-    } else if (arrayvalue_p->format == vpiIntVal) {
-        if (VL_UNCOVERABLE(num >= VL_VALUE_STRING_MAX_WORDS)) {
-            VL_FATAL_MT(__FILE__, __LINE__, "",
-                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
-                        "increase and recompile");
-        }
-
-        PLI_INT32* integersp = reinterpret_cast(t_out_data);
-        arrayvalue_p->value.integers = integersp;
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
-                                         vop->varCDatap(), integersp);
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
-                                         vop->varSDatap(), integersp);
-        } else if (varp->vltype() == VLVT_UINT32) {
-            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
-                                         vop->varIDatap(), integersp);
-        }
-
-        return;
-    } else if (arrayvalue_p->format == vpiLongIntVal) {
-        if (VL_UNCOVERABLE((sizeof(PLI_INT64) * num) >= VL_VALUE_STRING_MAX_CHARS)) {
-            VL_FATAL_MT(__FILE__, __LINE__, "",
-                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
-                        "increase and recompile");
-        }
-
-        PLI_INT64* longintsp = reinterpret_cast(t_out_data);
-        arrayvalue_p->value.longints = longintsp;
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
-                                         vop->varCDatap(), longintsp);
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
-                                         vop->varSDatap(), longintsp);
-        } else if (varp->vltype() == VLVT_UINT32) {
-            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
-                                         vop->varIDatap(), longintsp);
-        } else if (varp->vltype() == VLVT_UINT64) {
-            vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow,
-                                         vop->varQDatap(), longintsp);
-        }
-
-        return;
-    } else if (arrayvalue_p->format == vpiVectorVal) {
-        if (VL_UNCOVERABLE((VL_WORDS_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_WORDS)) {
-            VL_FATAL_MT(__FILE__, __LINE__, "",
-                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
-                        "increase and recompile");
-        }
-
-        p_vpi_vecval vectorsp = reinterpret_cast(t_out_data);
-        arrayvalue_p->value.vectors = vectorsp;
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
-                                       vop->varCDatap(), vectorsp);
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
-                                       vop->varSDatap(), vectorsp);
-        } else if (varp->vltype() == VLVT_UINT32) {
-            vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
-                                       vop->varIDatap(), vectorsp);
-        } else if (varp->vltype() == VLVT_UINT64) {
-            vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
-                                       vop->varQDatap(), vectorsp);
-        } else if (varp->vltype() == VLVT_WDATA) {
-            vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow,
-                                       vop->varEDatap(), vectorsp);
-        }
-
-        return;
-    } else if (arrayvalue_p->format == vpiRawFourStateVal) {
-        if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_CHARS)) {
-            VL_FATAL_MT(__FILE__, __LINE__, "",
-                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
-                        "increase and recompile");
-        }
-
-        PLI_BYTE8* valuep = reinterpret_cast(t_out_data);
-        arrayvalue_p->value.rawvals = valuep;
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
-                                       vop->varCDatap(), valuep);
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
-                                       vop->varSDatap(), valuep);
-        } else if (varp->vltype() == VLVT_UINT32) {
-            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
-                                       vop->varIDatap(), valuep);
-        } else if (varp->vltype() == VLVT_UINT64) {
-            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
-                                       vop->varQDatap(), valuep);
-        } else if (varp->vltype() == VLVT_WDATA) {
-            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true,
-                                       vop->varEDatap(), valuep);
-        }
-
-        return;
-    } else if (arrayvalue_p->format == vpiRawTwoStateVal) {
-        if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * num) >= VL_VALUE_STRING_MAX_CHARS)) {
-            VL_FATAL_MT(__FILE__, __LINE__, "",
-                        "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; "
-                        "increase and recompile");
-        }
-
-        PLI_BYTE8* valuep = reinterpret_cast(t_out_data);
-        arrayvalue_p->value.rawvals = valuep;
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
-                                       vop->varCDatap(), valuep);
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
-                                       vop->varSDatap(), valuep);
-        } else if (varp->vltype() == VLVT_UINT32) {
-            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
-                                       vop->varIDatap(), valuep);
-        } else if (varp->vltype() == VLVT_UINT64) {
-            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
-                                       vop->varQDatap(), valuep);
-        } else if (varp->vltype() == VLVT_WDATA) {
-            vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false,
-                                       vop->varEDatap(), valuep);
-        }
-
-        return;
-    }
-
-    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
-                  VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname());
-}
-
-void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p,
-                         PLI_UINT32 num) {
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value_array %p\n", object););
-    VerilatedVpiImp::assertOneCheck();
-
-    VL_VPI_ERROR_RESET_();
-    if (VL_UNLIKELY(!object)) return;
-
-    if (VL_UNLIKELY(!arrayvalue_p)) {
-        VL_VPI_WARNING_(__FILE__, __LINE__,
-                        "Ignoring vpi_get_value_array with null value pointer");
-        return;
-    }
-
-    if (VL_UNLIKELY(!index_p)) {
-        VL_VPI_WARNING_(__FILE__, __LINE__,
-                        "Ignoring vpi_get_value_array with null index pointer");
-        return;
-    }
-
-    const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
-    if (VL_UNLIKELY(!vop)) {
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
-        return;
-    }
-
-    if (vop->type() != vpiRegArray) {
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
-                      VerilatedVpiError::strFromVpiObjType(vop->type()));
-        return;
-    }
-
-    const int lowRange = vop->rangep()->low();
-    const int highRange = vop->rangep()->high();
-    if ((index_p[0] > highRange) || (index_p[0] < lowRange)) {
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Index %u for object '%s' is out of bounds [%u,%u]",
-                      __func__, index_p[0], vop->fullname(), lowRange, highRange);
-        return;
-    }
-
-    if (arrayvalue_p->flags & vpiUserAllocFlag) {
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiUserAllocFlag (%x)", __func__,
-                      arrayvalue_p->flags);
-        return;
-    }
-
-    vl_get_value_array(object, arrayvalue_p, index_p, num);
-}
-
-void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, const PLI_INT32* index_p,
-                        PLI_UINT32 num) {
-    const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
-    if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return;
-
-    const VerilatedVar* const varp = vop->varp();
-
-    const int size = vop->size();
-    if (VL_UNCOVERABLE(num > size)) {
-        VL_VPI_ERROR_(__FILE__, __LINE__,
-                      "%s: Requested elements to set (%u) exceed array size (%u)", __func__, num,
-                      size);
-        return;
-    }
-
-    const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low();
-    const int index
-        = leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0];
-
-    if (arrayvalue_p->format == vpiShortIntVal) {
-        const PLI_UINT16* shortintsp
-            = reinterpret_cast(arrayvalue_p->value.shortints);
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
-                                         vop->varCDatap());
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp,
-                                         vop->varSDatap());
-        }
-
-        return;
-    } else if (arrayvalue_p->format == vpiIntVal) {
-        const PLI_UINT32* integersp = reinterpret_cast(arrayvalue_p->value.integers);
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
-                                         vop->varCDatap());
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
-                                         vop->varSDatap());
-        } else if (varp->vltype() == VLVT_UINT32) {
-            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp,
-                                         vop->varIDatap());
-        }
-
-        return;
-    } else if (arrayvalue_p->format == vpiLongIntVal) {
-        const PLI_UINT64* longintsp = reinterpret_cast(arrayvalue_p->value.longints);
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
-                                         vop->varCDatap());
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
-                                         vop->varSDatap());
-        } else if (varp->vltype() == VLVT_UINT32) {
-            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
-                                         vop->varIDatap());
-        } else if (varp->vltype() == VLVT_UINT64) {
-            vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp,
-                                         vop->varQDatap());
-        }
-
-        return;
-    } else if (arrayvalue_p->format == vpiVectorVal) {
-        const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors;
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
-                                       vectorsp, vop->varCDatap());
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
-                                       vectorsp, vop->varSDatap());
-        } else if (varp->vltype() == VLVT_UINT32) {
-            vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
-                                       vectorsp, vop->varIDatap());
-        } else if (varp->vltype() == VLVT_UINT64) {
-            vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
-                                       vectorsp, vop->varQDatap());
-        } else if (varp->vltype() == VLVT_WDATA) {
-            vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true,
-                                       vectorsp, vop->varEDatap());
-        }
-
-        return;
-    } else if (arrayvalue_p->format == vpiRawFourStateVal) {
-        const PLI_UBYTE8* valuep = reinterpret_cast(arrayvalue_p->value.rawvals);
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
-                                       vop->varCDatap());
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
-                                       vop->varSDatap());
-        } else if (varp->vltype() == VLVT_UINT32) {
-            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
-                                       vop->varIDatap());
-        } else if (varp->vltype() == VLVT_UINT64) {
-            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
-                                       vop->varQDatap());
-        } else if (varp->vltype() == VLVT_WDATA) {
-            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep,
-                                       vop->varEDatap());
-        }
-
-        return;
-    } else if (arrayvalue_p->format == vpiRawTwoStateVal) {
-        const PLI_UBYTE8* valuep = reinterpret_cast(arrayvalue_p->value.rawvals);
-
-        if (varp->vltype() == VLVT_UINT8) {
-            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
-                                       vop->varCDatap());
-        } else if (varp->vltype() == VLVT_UINT16) {
-            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
-                                       vop->varSDatap());
-        } else if (varp->vltype() == VLVT_UINT32) {
-            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
-                                       vop->varIDatap());
-        } else if (varp->vltype() == VLVT_UINT64) {
-            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
-                                       vop->varQDatap());
-        } else if (varp->vltype() == VLVT_WDATA) {
-            vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep,
-                                       vop->varEDatap());
-        }
-
-        return;
-    }
-
-    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__,
-                  VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname());
-}
-
-void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p,
-                         PLI_UINT32 num) {
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value_array %p\n", object););
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-
-    if (VL_UNLIKELY(!arrayvalue_p)) {
-        VL_VPI_WARNING_(__FILE__, __LINE__,
-                        "Ignoring vpi_put_value_array with null value pointer");
-        return;
-    }
-
-    if (VL_UNLIKELY(!index_p)) {
-        VL_VPI_WARNING_(__FILE__, __LINE__,
-                        "Ignoring vpi_put_value_array with null index pointer");
-        return;
-    }
-
-    const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object);
-    if (VL_UNLIKELY(!vop)) {
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object);
-        return;
-    }
-
-    if (vop->type() != vpiRegArray) {
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object,
-                      VerilatedVpiError::strFromVpiObjType(vop->type()));
-        return;
-    }
-
-    const int lowRange = vop->rangep()->low();
-    const int highRange = vop->rangep()->high();
-    if ((index_p[0] > highRange) || (index_p[0] < lowRange)) {
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Index %u for object '%s' is out of bounds [%u,%u]",
-                      __func__, index_p[0], vop->fullname(), lowRange, highRange);
-        return;
-    }
-
-    if (VL_UNLIKELY(!vop->varp()->isPublicRW())) {
-        VL_VPI_ERROR_(__FILE__, __LINE__,
-                      "Ignoring vpi_put_value_array to signal marked read-only,"
-                      " use public_flat_rw instead: %s",
-                      vop->fullname());
-        return;
-    }
-
-    if (arrayvalue_p->flags & (vpiPropagateOff | vpiOneValue)) {
-        VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported flags (%x)", __func__,
-                      arrayvalue_p->flags);
-        return;
-    }
-
-    vl_put_value_array(object, arrayvalue_p, index_p, num);
-}
-
-// time processing
-
-void vpi_get_time(vpiHandle object, p_vpi_time time_p) {
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    // cppcheck-suppress nullPointer
-    if (VL_UNLIKELY(!time_p)) {
-        VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_get_time with nullptr value pointer");
-        return;
-    }
-    if (time_p->type == vpiSimTime) {
-        const QData qtime = VL_TIME_Q();
-        VlWide<2> itime;
-        VL_SET_WQ(itime, qtime);
-        time_p->low = itime[0];
-        time_p->high = itime[1];
-        return;
-    } else if (time_p->type == vpiScaledRealTime) {
-        double dtime = VL_TIME_D();
-        if (const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object)) {
-            const int scalePow10
-                = Verilated::threadContextp()->timeprecision() - vop->scopep()->timeunit();
-            const double scale = vl_time_multiplier(scalePow10);  // e.g. 0.0001
-            dtime *= scale;
-        }
-        time_p->real = dtime;
-        return;
-    }
-    VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%d)", __func__, time_p->type);
-}
-
-// I/O routines
-
-PLI_UINT32 vpi_mcd_open(PLI_BYTE8* filenamep) {
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    return VL_FOPEN_NN(filenamep, "wb");
-}
-
-PLI_UINT32 vpi_mcd_close(PLI_UINT32 mcd) {
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    VL_FCLOSE_I(mcd);
-    return 0;
-}
-
-PLI_BYTE8* vpi_mcd_name(PLI_UINT32 /*mcd*/) {
-    VL_VPI_UNIMP_();
-    return nullptr;
-}
-
-PLI_INT32 vpi_mcd_printf(PLI_UINT32 mcd, PLI_BYTE8* formatp, ...) {
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    va_list ap;
-    va_start(ap, formatp);
-    const int chars = vpi_mcd_vprintf(mcd, formatp, ap);
-    va_end(ap);
-    return chars;
-}
-
-PLI_INT32 vpi_printf(PLI_BYTE8* formatp, ...) {
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    va_list ap;
-    va_start(ap, formatp);
-    const int chars = vpi_vprintf(formatp, ap);
-    va_end(ap);
-    return chars;
-}
-
-// cppcheck-suppress constParameterPointer
-PLI_INT32 vpi_vprintf(PLI_BYTE8* formatp, va_list ap) {
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    return VL_VPRINTF(formatp, ap);
-}
-
-// cppcheck-suppress constParameterPointer
-PLI_INT32 vpi_mcd_vprintf(PLI_UINT32 mcd, PLI_BYTE8* format, va_list ap) {
-    VerilatedVpiImp::assertOneCheck();
-    FILE* const fp = VL_CVT_I_FP(mcd);
-    VL_VPI_ERROR_RESET_();
-    // cppcheck-suppress nullPointer
-    if (VL_UNLIKELY(!fp)) return 0;
-    const int chars = vfprintf(fp, format, ap);
-    return chars;
-}
-
-PLI_INT32 vpi_flush(void) {
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    Verilated::runFlushCallbacks();
-    return 0;  // Gcc coverage bug // LCOV_EXCL_LINE
-}
-
-PLI_INT32 vpi_mcd_flush(PLI_UINT32 mcd) {
-    VerilatedVpiImp::assertOneCheck();
-    FILE* const fp = VL_CVT_I_FP(mcd);
-    VL_VPI_ERROR_RESET_();
-    if (VL_UNLIKELY(!fp)) return 1;
-    std::fflush(fp);
-    return 0;
-}
-
-// utility routines
-
-PLI_INT32 vpi_compare_objects(vpiHandle /*object1*/, vpiHandle /*object2*/) {
-    VL_VPI_UNIMP_();
-    return 0;
-}
-PLI_INT32 vpi_chk_error(p_vpi_error_info error_info_p) {
-    // executing vpi_chk_error does not reset error
-    // error_info_p can be nullptr, so only return level in that case
-    VerilatedVpiImp::assertOneCheck();
-    const p_vpi_error_info imp_info_p = VerilatedVpiImp::error_info()->getError();
-    if (error_info_p && imp_info_p) *error_info_p = *imp_info_p;
-    if (!imp_info_p) return 0;  // no error occurred
-    return imp_info_p->level;  // return error severity level
-}
-
-#ifndef VL_NO_LEGACY
-PLI_INT32 vpi_free_object(vpiHandle object) {
-    // vpi_free_object is IEEE deprecated, use vpi_release_handle
-    return vpi_release_handle(object);
-}
-#endif
-
-PLI_INT32 vpi_release_handle(vpiHandle object) {
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_release_handle %p\n", object););
-    VerilatedVpiImp::assertOneCheck();
-    VerilatedVpio* const vop = VerilatedVpio::castp(object);
-    VL_VPI_ERROR_RESET_();
-    if (VL_UNLIKELY(!vop)) return 0;
-    VL_DO_DANGLING(delete vop, vop);
-    return 1;
-}
-
-PLI_INT32 vpi_get_vlog_info(p_vpi_vlog_info vlog_info_p) {
-    // This is VL_MT_SAFE, but not marked as can't indicate it in the standardized header file
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    const auto argc_argv = Verilated::threadContextp()->impp()->argc_argv();
-    vlog_info_p->argc = argc_argv.first;
-    vlog_info_p->argv = argc_argv.second;
-    vlog_info_p->product = const_cast(Verilated::productName());
-    vlog_info_p->version = const_cast(Verilated::productVersion());
-    return 1;
-}
-
-// routines added with 1364-2001
-
-PLI_INT32 vpi_get_data(PLI_INT32 /*id*/, PLI_BYTE8* /*dataLoc*/, PLI_INT32 /*numOfBytes*/) {
-    VL_VPI_UNIMP_();
-    return 0;
-}
-PLI_INT32 vpi_put_data(PLI_INT32 /*id*/, PLI_BYTE8* /*dataLoc*/, PLI_INT32 /*numOfBytes*/) {
-    VL_VPI_UNIMP_();
-    return 0;
-}
-void* vpi_get_userdata(vpiHandle /*obj*/) {
-    VL_VPI_UNIMP_();
-    return nullptr;
-}
-PLI_INT32 vpi_put_userdata(vpiHandle /*obj*/, void* /*userdata*/) {
-    VL_VPI_UNIMP_();
-    return 0;
-}
-
-PLI_INT32 vpi_control(PLI_INT32 operation, ...) {
-    VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_control %d\n", operation););
-    VerilatedVpiImp::assertOneCheck();
-    VL_VPI_ERROR_RESET_();
-    switch (operation) {
-    case vpiFinish: {
-        VL_FINISH_MT("", 0, "*VPI*");
-        return 1;
-    }
-    case vpiStop: {
-        VL_STOP_MT("", 0, "*VPI*");
-        return 1;  // LCOV_EXCL_LINE
-    }
-    default: {
-        VL_VPI_WARNING_(__FILE__, __LINE__, "%s: Unsupported type %s, ignoring", __func__,
-                        VerilatedVpiError::strFromVpiProp(operation));
-        return 0;
-    }
-    }
-}
-
-vpiHandle vpi_handle_by_multi_index(vpiHandle /*obj*/, PLI_INT32 /*num_index*/,
-                                    PLI_INT32* /*index_array*/) {
-    VL_VPI_UNIMP_();
-    return nullptr;
-}
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.h
deleted file mode 100644
index 14ac9bff046..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilated_vpi.h
+++ /dev/null
@@ -1,72 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2009-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//=========================================================================
-///
-/// \file
-/// \brief Verilated VPI header
-///
-/// This file contains routines related to using VPI with Verilated models.
-///
-/// User wrapper code may need to include this if controlling Verilated
-/// models that use ths VPI.
-///
-//=========================================================================
-
-#ifndef VERILATOR_VERILATED_VPI_H_
-#define VERILATOR_VERILATED_VPI_H_
-
-#include "verilatedos.h"
-
-#include "verilated.h"
-#include "verilated_syms.h"
-
-//======================================================================
-// From IEEE 1800-2023 annex M
-
-#include "vltstd/sv_vpi_user.h"
-
-//======================================================================
-
-/// Class for namespace-like grouping of Verilator VPI functions.
-
-class VerilatedVpi final {
-public:
-    /// Call timed callbacks.
-    /// User wrapper code should call this from their main loops.
-    static void callTimedCbs() VL_MT_UNSAFE_ONE;
-    /// Call value based callbacks.
-    /// User wrapper code should call this from their main loops.
-    static bool callValueCbs() VL_MT_UNSAFE_ONE;
-    /// Call callbacks of arbitrary types.
-    /// User wrapper code should call this from their main loops.
-    static bool callCbs(uint32_t reason) VL_MT_UNSAFE_ONE;
-    /// Returns true if there are callbacks of the given reason registered.
-    /// User wrapper code should call this from their main loops.
-    static bool hasCbs(uint32_t reason) VL_MT_UNSAFE_ONE;
-    /// Returns time of the next registered VPI callback, or
-    /// ~(0ULL) if none are registered
-    static QData cbNextDeadline() VL_MT_UNSAFE_ONE;
-    /// Debug dump of callbacks
-    static void dumpCbs() VL_MT_UNSAFE_ONE;
-    /// Checks VPI dirty state (i.e. whether vpi_put_value() has
-    /// been called since the last clearEvalNeeded())
-    static bool evalNeeded() VL_MT_UNSAFE_ONE;
-    /// Clears VPI dirty state (see evalNeeded())
-    static void clearEvalNeeded() VL_MT_UNSAFE_ONE;
-    /// Perform inertially delayed puts
-    static void doInertialPuts() VL_MT_UNSAFE_ONE;
-
-    // Self test, for internal use only
-    static void selfTest() VL_MT_UNSAFE_ONE;
-};
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos.h
deleted file mode 100644
index b93eaae5617..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos.h
+++ /dev/null
@@ -1,753 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilated/Verilator common header for OS portability
-///
-/// This header is included by user wrappers and defines the Verilated
-/// public-facing API.
-///
-/// User wrapper code does not generally need to include this, instead
-/// include verilated.h.
-///
-/// This header is used by both the Verilator source code (run on the build
-/// and host system), and the Verilated output (run on the target system).
-///
-/// Configuration code needed by only the host system is in
-/// config_build.h.in, code needed by Verilated code only is in
-/// verilated.h, and code needed by both is here (verilatedos.h).
-///
-//*************************************************************************
-
-#ifndef VERILATOR_VERILATEDOS_H_
-#define VERILATOR_VERILATEDOS_H_
-
-// Current clang-format versions botch #ifdef inclusion, so
-// clang-format off
-//=========================================================================
-// Compiler pragma abstraction
-
-#if defined(__clang__)
-# define VL_CLANG_ATTR(attr) __attribute__(( attr ))
-#else
-# define VL_CLANG_ATTR(attr)
-#endif
-
-#ifdef __GNUC__
-# define VL_ATTR_ALWINLINE __attribute__((always_inline)) inline
-# define VL_ATTR_NOINLINE __attribute__((noinline))
-# define VL_ATTR_COLD __attribute__((cold))
-# define VL_ATTR_HOT __attribute__((hot))
-# define VL_ATTR_NORETURN __attribute__((noreturn))
-// clang and gcc-8.0+ support no_sanitize("string") style attribute
-# if defined(__clang__) || (__GNUC__ >= 8)
-#  define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize("alignment")))
-# else  // The entire undefined sanitizer has to be disabled for older gcc
-#  define VL_ATTR_NO_SANITIZE_ALIGN __attribute__((no_sanitize_undefined))
-# endif
-# define VL_ATTR_PRINTF(fmtArgNum) __attribute__((format(printf, (fmtArgNum), (fmtArgNum) + 1)))
-# define VL_ATTR_PURE __attribute__((pure))
-# define VL_ATTR_UNUSED __attribute__((unused))
-# define VL_ATTR_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
-# if !defined(_WIN32) && !defined(__MINGW32__)
-// All VL_ATTR_WEAK symbols must be marked with the macOS -U linker flag in verilated.mk.in
-#  define VL_ATTR_WEAK __attribute__((weak))
-# endif
-# define VL_LIKELY(x) __builtin_expect(!!(x), 1)  // Prefer over C++20 [[likely]]
-# define VL_UNLIKELY(x) __builtin_expect(!!(x), 0)  // Prefer over C++20 [[unlikely]]
-# define VL_PREFETCH_RD(p) __builtin_prefetch((p), 0)
-# define VL_PREFETCH_RW(p) __builtin_prefetch((p), 1)
-#endif
-
-#ifdef __cpp_lib_unreachable
-/// Statement that may never be reached (for coverage etc)
-# define VL_UNREACHABLE std::unreachable()  // C++23
-#elif defined(__GNUC__)
-# define VL_UNREACHABLE __builtin_unreachable()
-#elif defined(_MSC_VER)  // MSVC
-# define VL_UNREACHABLE __assume(false)
-#else
-# define VL_UNREACHABLE
-#endif
-
-// Function acquires a capability/lock (-fthread-safety)
-#define VL_ACQUIRE(...) \
-        VL_CLANG_ATTR(annotate("ACQUIRE")) \
-        VL_CLANG_ATTR(acquire_capability(__VA_ARGS__))
-// Function acquires a shared capability/lock (-fthread-safety)
-#define VL_ACQUIRE_SHARED(...) \
-        VL_CLANG_ATTR(annotate("ACQUIRE_SHARED")) \
-        VL_CLANG_ATTR(acquire_shared_capability(__VA_ARGS__))
-// Function releases a capability/lock (-fthread-safety)
-#define VL_RELEASE(...) \
-        VL_CLANG_ATTR(annotate("RELEASE")) \
-        VL_CLANG_ATTR(release_capability(__VA_ARGS__))
-// Function releases a shared capability/lock (-fthread-safety)
-#define VL_RELEASE_SHARED(...) \
-        VL_CLANG_ATTR(annotate("RELEASE_SHARED")) \
-        VL_CLANG_ATTR(release_shared_capability(__VA_ARGS__))
-// Function returns bool if acquired a capability (-fthread-safety)
-#define VL_TRY_ACQUIRE(...) \
-        VL_CLANG_ATTR(try_acquire_capability(__VA_ARGS__))
-// Function returns bool if acquired shared (-fthread-safety)
-#define VL_TRY_ACQUIRE_SHARED(...) \
-        VL_CLANG_ATTR(try_acquire_shared_capability(__VA_ARGS__))
-// Function requires a capability inbound (-fthread-safety)
-#define VL_CAPABILITY(x) \
-        VL_CLANG_ATTR(capability(x))
-// Name of mutex protecting this variable (-fthread-safety)
-#define VL_EXCLUDES(x) \
-        VL_CLANG_ATTR(annotate("EXCLUDES")) \
-        VL_CLANG_ATTR(locks_excluded(x))
-// Scoped threaded capability/lock (-fthread-safety)
-#define VL_SCOPED_CAPABILITY \
-        VL_CLANG_ATTR(scoped_lockable)
-// Annotated function returns reference to the given capability.
-// Allowed on: function, method. (-fthread-safety)
-#define VL_RETURN_CAPABILITY(x) \
-        VL_CLANG_ATTR(lock_returned(x))
-// Assert that capability is already held.
-// Allowed on: function, method. (-fthread-safety)
-#define VL_ASSERT_CAPABILITY(x) \
-        VL_CLANG_ATTR(assert_capability(x))
-// Disable thread safety analysis for the annotted function
-// Use this only when absolutely sure code is correct, but too
-// complicated for the compiler to prove.
-#define VL_NO_THREAD_SAFETY_ANALYSIS \
-        VL_CLANG_ATTR(no_thread_safety_analysis)
-
-// Require mutex locks only in code units which work with enabled multi-threading.
-#if !defined(VL_MT_DISABLED_CODE_UNIT)
-// Function requires not having a capability inbound (-fthread-safety)
-# define VL_REQUIRES(x) \
-        VL_CLANG_ATTR(annotate("REQUIRES")) \
-        VL_CLANG_ATTR(requires_capability(x))
-// Name of capability/lock (-fthread-safety)
-# define VL_GUARDED_BY(x) \
-        VL_CLANG_ATTR(annotate("GUARDED_BY")) \
-        VL_CLANG_ATTR(guarded_by(x))
-// The data that the annotated pointer points to is protected by the given capability.
-// The pointer itself is not protected.
-// Allowed on: pointer data member. (-fthread-safety)
-# define VL_PT_GUARDED_BY(x) \
-        VL_CLANG_ATTR(annotate("PT_GUARDED_BY")) \
-        VL_CLANG_ATTR(pt_guarded_by(x))
-#else
-// Keep annotations for clang_check_attributes
-# define VL_REQUIRES(x) \
-        VL_CLANG_ATTR(annotate("REQUIRES"))
-# define VL_GUARDED_BY(x) \
-        VL_CLANG_ATTR(annotate("GUARDED_BY"))
-# define VL_PT_GUARDED_BY(x) \
-        VL_CLANG_ATTR(annotate("PT_GUARDED_BY"))
-#endif
-
-// Defaults for unsupported compiler features
-#ifndef VL_ATTR_ALWINLINE
-# define VL_ATTR_ALWINLINE inline  ///< Attribute to inline, even when not optimizing
-#endif
-#ifndef VL_ATTR_NOINLINE
-# define VL_ATTR_NOINLINE  ///< Attribute to never inline, even when optimizing
-#endif
-#ifndef VL_ATTR_COLD
-# define VL_ATTR_COLD  ///< Attribute that function is rarely executed
-#endif
-#ifndef VL_ATTR_HOT
-# define VL_ATTR_HOT  ///< Attribute that function is highly executed
-#endif
-#ifndef VL_ATTR_NORETURN
-# define VL_ATTR_NORETURN  ///< Attribute that function does not ever return
-#endif
-#ifndef VL_ATTR_NO_SANITIZE_ALIGN
-# define VL_ATTR_NO_SANITIZE_ALIGN  ///< Attribute that function contains intended unaligned access
-#endif
-#ifndef VL_ATTR_PRINTF
-# define VL_ATTR_PRINTF(fmtArgNum)  ///< Attribute for function with printf format checking
-#endif
-#ifndef VL_ATTR_PURE
-# define VL_ATTR_PURE  ///< Attribute that function is pure (and thus also VL_MT_SAFE)
-#endif
-#ifndef VL_ATTR_UNUSED
-# define VL_ATTR_UNUSED  ///< Attribute that function that may be never used
-#endif
-#ifndef VL_ATTR_WARN_UNUSED_RESULT
-# define VL_ATTR_WARN_UNUSED_RESULT  ///< Attribute that return value of function must be used
-#endif
-#ifndef VL_ATTR_WEAK
-# define VL_ATTR_WEAK  ///< Attribute that function external that is optionally defined
-#endif
-#ifndef VL_LIKELY
-# define VL_LIKELY(x) (!!(x))  ///< Return boolean expression that is more often true
-# define VL_UNLIKELY(x) (!!(x))  ///< Return boolean expression that is more often false
-#endif
-/// Boolean expression never hit by users (branch coverage disabled)
-# define VL_UNCOVERABLE(x) VL_UNLIKELY(x)
-#ifndef VL_PREFETCH_RD
-# define VL_PREFETCH_RD(p)  ///< Prefetch pointer argument with read intent
-#endif
-#ifndef VL_PREFETCH_RW
-# define VL_PREFETCH_RW(p)  ///< Prefetch pointer argument with read/write intent
-#endif
-
-
-#ifndef VL_NO_LEGACY
-# define VL_ATTR_ALIGNED(alignment)  // Deprecated
-# define VL_FUNC __func__  // Deprecated
-# define VL_THREAD  // Deprecated
-# define VL_THREAD_LOCAL thread_local  // Deprecated
-# define VL_STATIC_OR_THREAD static  // Deprecated
-#endif
-
-// Comment tag that Function is pure (and thus also VL_MT_SAFE)
-#define VL_PURE VL_CLANG_ATTR(annotate("PURE"))
-// Annotated function can be called only in MT_DISABLED context, i.e. either in a code unit
-// compiled with VL_MT_DISABLED_CODE_UNIT preprocessor definition, or in the main thread.
-#define VL_MT_DISABLED \
-    VL_CLANG_ATTR(annotate("MT_DISABLED")) \
-    VL_EXCLUDES(VlOs::MtScopeMutex::s_haveThreadScope)
-// Comment tag that function is threadsafe
-#define VL_MT_SAFE VL_CLANG_ATTR(annotate("MT_SAFE"))
-// Comment tag that function is threadsafe, only if
-// other threads doesn't change tree topology
-#define VL_MT_STABLE VL_CLANG_ATTR(annotate("MT_STABLE"))
-// Comment tag that function is threadsafe, only
-// during normal operation (post-init)
-#define VL_MT_SAFE_POSTINIT VL_CLANG_ATTR(annotate("MT_SAFE_POSTINIT"))
-// Attribute that function is clang threadsafe and uses given mutex
-#define VL_MT_SAFE_EXCLUDES(mutex) VL_CLANG_ATTR(annotate("MT_SAFE_EXCLUDES")) VL_EXCLUDES(mutex)
-// Comment tag that function is not threadsafe
-#define VL_MT_UNSAFE VL_CLANG_ATTR(annotate("MT_UNSAFE"))
-// Comment tag that function is not threadsafe
-// protected to make sure single-caller
-#define VL_MT_UNSAFE_ONE VL_CLANG_ATTR(annotate("MT_UNSAFE_ONE"))
-// Comment tag that function is entry point of parallelization
-#define VL_MT_START VL_CLANG_ATTR(annotate("MT_START")) VL_REQUIRES(VlOs::MtScopeMutex::s_haveThreadScope)
-
-#ifndef VL_NO_LEGACY
-# define VL_ULL(c) (c##ULL)  // Add appropriate suffix to 64-bit constant (deprecated)
-#endif
-
-// Convert argument to IData
-// This is not necessarily the same as "#UL", depending on what the IData typedef is.
-#define VL_UL(c) (static_cast(c##UL))
-
-#if defined(VL_CPPCHECK) || defined(__clang_analyzer__) || __cplusplus < 201103L
-# define VL_DANGLING(var)
-#else
-/// After e.g. delete, set variable to nullptr to indicate must not use later
-# define VL_DANGLING(var) \
-    do { \
-        *const_cast(reinterpret_cast(&var)) = nullptr; \
-    } while (false)
-#endif
-
-/// Perform an e.g. delete, then set variable to nullptr to indicate must not use later.
-/// Unlike VL_DO_CLEAR the setting of the variable is only for debug reasons.
-#define VL_DO_DANGLING(stmt, var) \
-    do { \
-        do { \
-            stmt; \
-        } while (false); \
-        VL_DANGLING(var); \
-    } while (false)
-
-/// As with VL_DO_DANGLING, but two variables dangle.
-#define VL_DO_DANGLING2(stmt, var, var2) \
-    do { \
-        do { \
-            stmt; \
-        } while (false); \
-        VL_DANGLING(var); \
-        VL_DANGLING(var2); \
-    } while (false)
-
-/// Perform an e.g. delete, then set variable to nullptr as a requirement
-#define VL_DO_CLEAR(stmt, stmt2) \
-    do { \
-        do { \
-            stmt; \
-        } while (false); \
-        do { \
-            stmt2; \
-        } while (false); \
-    } while (false)
-
-#ifdef _MSC_VER
-# if _MSC_VER < 1929
-#  error "Verilator requires at least Visual Studio 2019 version 16.11.2"
-# endif
-#endif
-
-//=========================================================================
-// C++-2014
-
-#if __cplusplus >= 201402L || defined(VL_CPPCHECK) || defined(_MSC_VER)
-#else
-# error "Verilator requires a C++14 or newer compiler"
-#endif
-
-#ifndef VL_NO_LEGACY
-// These are deprecated historical defines. We leave them in case users referenced them.
-# define VL_EQ_DELETE = delete
-# define vl_unique_ptr std::unique_ptr
-# define vl_unordered_map std::unordered_map
-# define vl_unordered_set std::unordered_set
-# define VL_INCLUDE_UNORDERED_MAP 
-# define VL_INCLUDE_UNORDERED_SET 
-# define VL_FINAL final
-# define VL_MUTABLE mutable
-# define VL_OVERRIDE override
-#endif
-
-//=========================================================================
-// C++-2017
-
-#if __cplusplus >= 201703L
-# define VL_CONSTEXPR_CXX17 constexpr
-#else
-# define VL_CONSTEXPR_CXX17
-#endif
-
-
-//=========================================================================
-// Optimization
-
-#ifndef VL_NO_LEGACY
-# ifndef VL_INLINE_OPT
-#   define VL_INLINE_OPT  // Historical, has no effect on Verilated models.
-# endif
-#endif
-
-//=========================================================================
-// Internal coverage
-
-#ifdef VL_GCOV
-extern "C" void __gcov_dump();
-// Dump internal code coverage data before e.g. std::abort()
-# define VL_GCOV_DUMP() __gcov_dump()
-#else
-# define VL_GCOV_DUMP()
-#endif
-
-//=========================================================================
-// Warning disabled
-
-#ifndef VL_WARNINGS
-# ifdef _MSC_VER
-#  pragma warning(disable:4099)  // C4099: type name first seen using 'class' now seen using 'struct' (V3AstNode)
-#  pragma warning(disable:4100)  // C4100: unreferenced formal parameter (L4)
-#  pragma warning(disable:4127)  // C4127: conditional expression is constant (L4)
-#  pragma warning(disable:4146)  // C4146: unary minus operator applied to unsigned type, result still unsigned
-#  pragma warning(disable:4189)  // C4189: local variable is initialized but not referenced (L4)
-#  pragma warning(disable:4244)  // C4244: conversion from 'uint64_t' to 'uint_32_t', possible loss of data
-#  pragma warning(disable:4245)  // C4245: conversion from 'int' to 'unsigned', signed/unsigned mismatch
-#  pragma warning(disable:4996)  // C4996: sscanf/fopen/etc may be unsafe
-# endif
-#endif
-
-//=========================================================================
-// Basic integer types
-
-#ifdef __MINGW32__
-# define __USE_MINGW_ANSI_STDIO 1  // Force old MinGW (GCC 5 and older) to use C99 formats
-#endif
-
-// The inttypes supplied with some GCC & MINGW32 versions requires STDC_FORMAT_MACROS
-// to be declared in order to get the PRIxx macros used by fstapi.c
-#define __STDC_FORMAT_MACROS
-
-// Now that C++ requires these standard types the vl types are deprecated
-#include 
-#include 
-#include 
-#include 
-
-#ifndef VL_NO_LEGACY
-using vluint8_t = uint8_t;  ///< 8-bit unsigned type (backward compatibility)
-using vluint16_t = uint16_t;  ///< 16-bit unsigned type (backward compatibility)
-using vluint32_t = uint32_t;  ///< 32-bit unsigned type (backward compatibility)
-using vluint64_t = uint64_t;  ///< 64-bit unsigned type (backward compatibility)
-using vlsint8_t = int8_t;  ///< 8-bit signed type (backward compatibility)
-using vlsint16_t = int16_t;  ///< 16-bit signed type (backward compatibility)
-using vlsint32_t = int32_t;  ///< 32-bit signed type (backward compatibility)
-using vlsint64_t = int64_t;  ///< 64-bit signed type (backward compatibility)
-#endif
-
-#if defined(__CYGWIN__)
-
-# include   // __WORDSIZE
-# include   // ssize_t
-
-#elif defined(_WIN32) && defined(_MSC_VER)
-
-# ifndef _SSIZE_T_DEFINED
-#  ifdef _WIN64
-using ssize_t = uint64_t;  ///< signed size_t; returned from read()
-#  else
-using ssize_t = uint32_t;  ///< signed size_t; returned from read()
-#  endif
-# endif
-
-#else  // Linux or compliant Unix flavors, -m64
-
-# include   // Solaris
-# include   // __WORDSIZE
-# include   // ssize_t
-#endif
-
-//=========================================================================
-// Printing printf/scanf formats
-
-// Use Microsoft-specific format specifiers for Microsoft Visual C++ only
-// Deprecated, favor C++11's PRIx64, etc, instead
-#ifndef VL_NO_LEGACY
-# ifdef _MSC_VER
-#  define VL_PRI64 "I64"  ///< print a uint64_t (backward compatibility)
-# else  // use standard C99 format specifiers
-#  if defined(__WORDSIZE) && (__WORDSIZE == 64)
-#   define VL_PRI64 "l"  ///< print a uint64_t (backward compatibility)
-#  else
-#   define VL_PRI64 "ll"  ///< print a uint64_t (backward compatibility)
-#  endif
-# endif
-#endif
-
-#if defined(_WIN32) && defined(_MSC_VER)
-# if (_MSC_VER < 1900)
-#  define VL_SNPRINTF _snprintf
-# else
-#  define VL_SNPRINTF snprintf
-# endif
-# define VL_VSNPRINTF vsnprintf
-#else
-# define VL_SNPRINTF snprintf
-# define VL_VSNPRINTF vsnprintf
-#endif
-
-//=========================================================================
-// File system functions
-
-#ifdef _WIN32
-# define VL_DEV_NULL "nul"
-#else  // Linux or compliant Unix flavors
-# define VL_DEV_NULL "/dev/null"
-#endif
-
-//=========================================================================
-// Integer size macros
-
-#define VL_BYTESIZE 8  ///< Bits in a CData / byte
-#define VL_SHORTSIZE 16  ///< Bits in a SData / short
-#define VL_IDATASIZE 32  ///< Bits in an IData / word
-#define VL_QUADSIZE 64  ///< Bits in a QData / quadword
-#define VL_EDATASIZE 32  ///< Bits in an EData (WData entry)
-#define VL_EDATASIZE_LOG2 5  ///< log2(VL_EDATASIZE)
-#define VL_CACHE_LINE_BYTES 64  ///< Bytes in a cache line (for alignment)
-
-#ifndef VL_NO_LEGACY
-# define VL_WORDSIZE VL_IDATASIZE  // Legacy define
-#endif
-
-/// Return number of bytes argument-number of bits needs (1 bit=1 byte)
-#define VL_BYTES_I(nbits) (((nbits) + (VL_BYTESIZE - 1)) / VL_BYTESIZE)
-/// Return Words/EDatas in argument-number of bits needs (1 bit=1 word)
-#define VL_WORDS_I(nbits) (((nbits) + (VL_EDATASIZE - 1)) / VL_EDATASIZE)
-// Number of Words/EDatas a quad requires
-#define VL_WQ_WORDS_E VL_WORDS_I(VL_QUADSIZE)
-
-//=========================================================================
-// Class definition helpers
-
-// Comment tag to indicate a base class, e.g. cannot label "class final"
-#define VL_NOT_FINAL
-
-// Declare a class as uncopyable; put after a private:
-#define VL_UNCOPYABLE(Type) \
-    Type(const Type& other) = delete; \
-    Type& operator=(const Type&) = delete
-
-// Declare a class as unmovable; put after a private:
-#define VL_UNMOVABLE(Type) \
-    Type(Type&& other) = delete; \
-    Type& operator=(Type&&) = delete
-
-//=========================================================================
-// Verilated function size macros
-
-#define VL_MULS_MAX_WORDS 128  ///< Max size in words of MULS operation
-
-#ifndef VL_VALUE_STRING_MAX_WORDS
-    #define VL_VALUE_STRING_MAX_WORDS 64  ///< Max size in words of String conversion operation
-#endif
-#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS) * 4
-
-//=========================================================================
-// Base macros
-
-#define VL_SIZEBITS_I (VL_IDATASIZE - 1)  ///< Bit mask for bits in a word
-#define VL_SIZEBITS_Q (VL_QUADSIZE - 1)  ///< Bit mask for bits in a quad
-#define VL_SIZEBITS_E (VL_EDATASIZE - 1)  ///< Bit mask for bits in a quad
-
-/// Return mask for words with 1's where relevant bits are (0=all bits)
-/// Arguments must not have side effects
-#define VL_MASK_I(nbits) (((nbits) & VL_SIZEBITS_I) ? ((1U << ((nbits) & VL_SIZEBITS_I)) - 1) : ~0)
-/// Return mask for quads with 1's where relevant bits are (0=all bits)
-/// Arguments must not have side effects
-#define VL_MASK_Q(nbits) \
-    (((nbits) & VL_SIZEBITS_Q) ? ((1ULL << ((nbits) & VL_SIZEBITS_Q)) - 1ULL) : ~0ULL)
-/// Return mask for EData with 1's where relevant bits are (0=all bits)
-/// Arguments must not have side effects
-#define VL_MASK_E(nbits) VL_MASK_I(nbits)
-
-#define VL_EUL(n) VL_UL(n)  // Make constant number EData sized
-
-#define VL_BITWORD_I(bit) ((bit) / VL_IDATASIZE)  ///< Word number for sv DPI vectors
-#define VL_BITWORD_E(bit) ((bit) >> VL_EDATASIZE_LOG2)  ///< Word number for a wide quantity
-#define VL_BITBIT_I(bit) ((bit) & VL_SIZEBITS_I)  ///< Bit number for a bit in a long
-#define VL_BITBIT_Q(bit) ((bit) & VL_SIZEBITS_Q)  ///< Bit number for a bit in a quad
-#define VL_BITBIT_E(bit) ((bit) & VL_SIZEBITS_E)  ///< Bit number for a bit in an EData
-
-// Return true if data[bit] set; not 0/1 return, but 0/non-zero return.
-#define VL_BITISSET_I(data, bit) ((data) & (VL_UL(1) << VL_BITBIT_I(bit)))
-#define VL_BITISSET_Q(data, bit) ((data) & (1ULL << VL_BITBIT_Q(bit)))
-#define VL_BITISSET_E(data, bit) ((data) & (VL_EUL(1) << VL_BITBIT_E(bit)))
-#define VL_BITISSET_W(data, bit) ((data)[VL_BITWORD_E(bit)] & (VL_EUL(1) << VL_BITBIT_E(bit)))
-
-//=========================================================================
-// Floating point
-// #defines, to avoid requiring math.h on all compile runs
-
-#ifdef _MSC_VER
-static inline double VL_TRUNC(double n) {
-    return (n < 0) ? std::ceil(n) : std::floor(n);
-}
-static inline double VL_ROUND(double n) {
-    return (n < 0) ? std::ceil(n-0.5) : std::floor(n + 0.5);
-}
-#else
-# define VL_TRUNC(n) std::trunc(n)
-# define VL_ROUND(n) std::round(n)
-#endif
-
-//=========================================================================
-// Performance counters
-
-#if defined(__i386__) || defined(__x86_64__)
-// The uint64_t argument is loaded with a high-performance counter for profiling
-// or 0x0 if not implemented on this platform
-#define VL_GET_CPU_TICK(val) \
-    { \
-        uint32_t hi; \
-        uint32_t lo; \
-        asm volatile("rdtsc" : "=a"(lo), "=d"(hi)); \
-        (val) = ((uint64_t)lo) | (((uint64_t)hi) << 32); \
-    }
-#elif defined(__aarch64__)
-// 1 GHz virtual system timer on SBSA level 5 compliant systems, else often 100 MHz
-# define VL_GET_CPU_TICK(val) \
-    { \
-        asm volatile("isb" : : : "memory"); \
-        asm volatile("mrs %[rt],CNTVCT_EL0" : [rt] "=r"(val)); \
-    }
-#else
-// We just silently ignore unknown OSes, as only leads to missing statistics
-# define VL_GET_CPU_TICK(val) (val) = 0;
-#endif
-
-//=========================================================================
-// Threading related OS-specific functions
-
-#ifdef _WIN32
-# define WIN32_LEAN_AND_MEAN
-# ifndef NOMINMAX
-#  define NOMINMAX
-# endif
-# include "windows.h"
-# define VL_CPU_RELAX() YieldProcessor()
-#elif defined(__i386__) || defined(__x86_64__) || defined(VL_CPPCHECK)
-// For more efficient busy waiting on SMT CPUs, let the processor know
-// we're just waiting so it can let another thread run
-# define VL_CPU_RELAX() asm volatile("rep; nop" ::: "memory")
-#elif defined(__ia64__)
-# define VL_CPU_RELAX() asm volatile("hint @pause" ::: "memory")
-#elif defined(__armel__) || defined(__ARMEL__)  // Arm, but broken, must be before __arm__
-# define VL_CPU_RELAX() asm volatile("nop" ::: "memory");
-#elif defined(__aarch64__) || defined(__arm__)
-# define VL_CPU_RELAX() asm volatile("yield" ::: "memory")
-#elif defined(__hppa__)  // HPPA does not currently have yield/pause
-# define VL_CPU_RELAX() asm volatile("nop" ::: "memory")
-#elif defined(__loongarch__)  // LoongArch does not currently have yield/pause
-# define VL_CPU_RELAX() asm volatile("nop" ::: "memory")
-#elif defined(__mips64el__) || defined(__mips__) || defined(__mips64__) || defined(__mips64)
-# define VL_CPU_RELAX() asm volatile("pause" ::: "memory")
-#elif defined(__POWERPC__) && defined(__APPLE__) // First check for a special case of macOS
-# define VL_CPU_RELAX() asm volatile("or r1, r1, r1; or r2, r2, r2;" ::: "memory")
-#elif defined(__powerpc64__) || defined(__powerpc__) // Generic powerpc
-# define VL_CPU_RELAX() asm volatile("or 1, 1, 1; or 2, 2, 2;" ::: "memory")
-#elif defined(__riscv)  // RiscV does not currently have yield/pause, but one is proposed
-# define VL_CPU_RELAX() asm volatile("nop" ::: "memory")
-#elif defined(__s390x__)
-# define VL_CPU_RELAX() asm volatile("lr 0,0" ::: "memory")
-#elif defined(__sparc__)
-# define VL_CPU_RELAX() asm volatile("rd %%ccr, %%g0" ::: "memory")
-#elif defined(VL_IGNORE_UNKNOWN_ARCH)
-# define VL_CPU_RELAX()
-#else
-# error "Missing VL_CPU_RELAX() definition."
-#endif
-
-//=========================================================================
-// String/time related OS-specific functions
-
-#ifdef _MSC_VER
-# define VL_STRCASECMP _stricmp
-#else
-# define VL_STRCASECMP strcasecmp
-#endif
-
-//=========================================================================
-// Macros controlling target-specific optimizations
-
-// Define VL_PORTABLE_ONLY to disable all target-specific optimizations
-#ifndef VL_PORTABLE_ONLY
-# ifdef __x86_64__
-#  define VL_X86_64 1
-# endif
-#endif  // VL_PORTABLE_ONLY
-// clang-format on
-
-//=========================================================================
-// Stringify macros
-
-#define VL_STRINGIFY(...) VL_STRINGIFY2(__VA_ARGS__)
-#define VL_STRINGIFY2(...) #__VA_ARGS__
-
-//=========================================================================
-// Offset of field in type
-
-// Address zero can cause compiler problems
-#define VL_OFFSETOF(type, field) \
-    (reinterpret_cast(&(reinterpret_cast(0x10000000)->field)) - 0x10000000)
-
-//=========================================================================
-// Time and performance
-
-#include 
-
-namespace VlOs {
-
-/// Get environment variable
-extern std::string getenvStr(const std::string& envvar,
-                             const std::string& defaultValue) VL_MT_SAFE;
-
-/// Return currently executing processor number; may do an OS call underneath so slow
-extern uint16_t getcpu() VL_MT_SAFE;
-
-/// Return number of processors available to the current process. This might be
-/// less than the number of logical processors in the machine, if a processor
-/// affinity mask was used, e.g. via 'numactl -C 0-3'. Returns 0 if cannot
-/// be determiend.
-extern unsigned getProcessAvailableParallelism() VL_MT_SAFE;
-
-/// Return getProcessAvailableParallelism if non-zero, otherwise the number of
-/// hardware threads in the host machine.
-extern unsigned getProcessDefaultParallelism() VL_MT_SAFE;
-
-/// Return memory usage in bytes, or 0 if unknown
-extern void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE;
-
-// Internal: Record CPU time, starting point on construction, and current delta from that
-class DeltaCpuTime final {
-    double m_start{};  // Time constructed at
-    static double gettime() VL_MT_SAFE;
-
-public:
-    // Construct, and if startit is true, start() timer
-    explicit DeltaCpuTime(bool startit) {
-        if (startit) start();
-    }
-    void start() VL_MT_SAFE { m_start = gettime(); }  // Start timer; record current time
-    double deltaTime() const VL_MT_SAFE {  // Return time between now and start()
-        return (m_start == 0.0) ? 0.0 : gettime() - m_start;
-    }
-};
-// Internal: Record wall time, starting point on construction, and current delta from that
-class DeltaWallTime final {
-    double m_start{};  // Time constructed at
-    static double gettime() VL_MT_SAFE;
-
-public:
-    // Construct, and if startit is true, start() timer
-    explicit DeltaWallTime(bool startit) {
-        if (startit) start();
-    }
-    void start() VL_MT_SAFE { m_start = gettime(); }  // Start timer; record current time
-    double deltaTime() const VL_MT_SAFE {  // Return time between now and start()
-        return (m_start == 0.0) ? 0.0 : gettime() - m_start;
-    }
-};
-
-// Used by clang's -fthread-safety, ensures that only one instance of V3ThreadScope
-// is created at a time
-class VL_CAPABILITY("mutex") MtScopeMutex final {
-public:
-    static MtScopeMutex s_haveThreadScope;
-};
-
-}  //namespace VlOs
-
-//=========================================================================
-// Conversions
-
-#include 
-
-namespace vlstd {
-
-template 
-struct reverse_wrapper final {
-    const T& m_v;
-
-    explicit reverse_wrapper(const T& a_v)
-        : m_v(a_v) {}  // Need () constructor
-    auto begin() -> decltype(m_v.rbegin()) { return m_v.rbegin(); }
-    auto end() -> decltype(m_v.rend()) { return m_v.rend(); }
-};
-
-// C++20's std::ranges::reverse_view
-template 
-reverse_wrapper reverse_view(const T& v) {
-    return reverse_wrapper(v);
-}
-
-// C++17's std::as_const
-// `VL_MT_SAFE` annotation only applies to this function.
-// Object that is returned by this function is not considered
-// as MT_SAFE and any function call on this object still
-// needs to be `VL_MT_SAFE`.
-template 
-T const& as_const(T& v) VL_MT_SAFE {
-    return v;
-}
-
-// Utility function
-template 
-inline constexpr size_t roundUpToMultipleOf(size_t value) {
-    static_assert((N & (N - 1)) == 0, "'N' must be a power of 2");
-    return (value + N - 1) & ~(N - 1);
-}
-
-};  // namespace vlstd
-
-//=========================================================================
-
-#endif  // Guard
diff --git a/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos_c.h b/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos_c.h
deleted file mode 100644
index 16279d8c0c7..00000000000
--- a/bsp/xiangshan-verilator/applications/verilator_runtime/verilatedos_c.h
+++ /dev/null
@@ -1,219 +0,0 @@
-// -*- mode: C++; c-file-style: "cc-mode" -*-
-//*************************************************************************
-//
-// Code available from: https://verilator.org
-//
-// This program is free software; you can redistribute it and/or modify it
-// under the terms of either the GNU Lesser General Public License Version 3
-// or the Perl Artistic License Version 2.0.
-// SPDX-FileCopyrightText: 2003-2026 Wilson Snyder
-// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
-//
-//*************************************************************************
-///
-/// \file
-/// \brief Verilated/Verilator common implementation for OS portability
-///
-/// This is compiled as part of other .cpp files to reduce compile time
-/// and as such is a .h file rather than .cpp file.
-///
-//*************************************************************************
-
-#ifndef VL_ALLOW_VERILATEDOS_C
-#error "This file should be included only from V3Os.cpp/Verilated.cpp"
-#endif
-
-#include "verilatedos.h"
-
-#include 
-#include 
-
-// clang-format off
-#if defined(_WIN32) || defined(__MINGW32__)
-# include    // LONG for bcrypt.h on MINGW
-# include   // GetProcessTimes
-# include    // GetProcessMemoryInfo
-#endif
-
-#if defined(__linux)
-# include   // For sched_getcpu()
-#endif
-#if defined(__APPLE__) && !defined(__arm64__) && !defined(__POWERPC__)
-# include   // For __cpuid_count()
-#endif
-#if defined(__FreeBSD__)
-# include   // For pthread_getaffinity_np()
-#endif
-
-#if defined(__APPLE__) && defined(__MACH__)
-# include   // For task_info()
-#endif
-// clang-format on
-
-namespace VlOs {
-
-//=========================================================================
-// VlOs::VlGetCpuTime/VlGetWallTime implementation
-
-double DeltaCpuTime::gettime() VL_MT_SAFE {
-#if defined(_WIN32) || defined(__MINGW32__)
-    FILETIME lpCreationTime, lpExitTime, lpKernelTime, lpUserTime;
-    if (0
-        != GetProcessTimes(GetCurrentProcess(), &lpCreationTime, &lpExitTime, &lpKernelTime,
-                           &lpUserTime)) {
-        return static_cast(static_cast(lpUserTime.dwLowDateTime)
-                                   | static_cast(lpUserTime.dwHighDateTime) << 32ULL)
-               * 1e-7;
-    }
-#else
-    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
-    timespec ts;
-    if (0 == clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts))  // MT-Safe  // LCOV_EXCL_BR_LINE
-        return ts.tv_sec + ts.tv_nsec * 1e-9;
-#endif
-    return 0.0;  // LCOV_EXCL_LINE
-}
-double DeltaWallTime::gettime() VL_MT_SAFE {
-#if defined(_WIN32) || defined(__MINGW32__)
-    FILETIME ft;  // contains number of 0.1us intervals since the beginning of 1601 UTC.
-    GetSystemTimeAsFileTime(&ft);
-    const uint64_t tenthus
-        = ((static_cast(ft.dwHighDateTime) << 32) + ft.dwLowDateTime + 5ULL);
-    return static_cast(tenthus) * 1e-7;
-#else
-    // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
-    timespec ts;
-    if (0 == clock_gettime(CLOCK_MONOTONIC, &ts))  // MT-Safe  // LCOV_EXCL_BR_LINE
-        return ts.tv_sec + ts.tv_nsec * 1e-9;
-    return 0.0;  // LCOV_EXCL_LINE
-#endif
-}
-
-//=============================================================================
-// Vlos::getcpu implementation
-
-uint16_t getcpu() VL_MT_SAFE {
-#if defined(__linux)
-    return sched_getcpu();  // TODO: this is a system call. Not exactly cheap.
-#elif defined(__APPLE__) && !defined(__arm64__) && !defined(__POWERPC__)
-    uint32_t info[4];
-    __cpuid_count(1, 0, info[0], info[1], info[2], info[3]);
-    // info[1] is EBX, bits 24-31 are APIC ID
-    if ((info[3] & (1 << 9)) == 0) {
-        return 0;  // no APIC on chip
-    } else {
-        return (unsigned)info[1] >> 24;
-    }
-#elif defined(_WIN32)
-    return GetCurrentProcessorNumber();
-#else
-    return 0;
-#endif
-}
-
-//=============================================================================
-// Vlos::getProcessAvailableParallelism implementation
-
-unsigned getProcessAvailableParallelism() VL_MT_SAFE {
-#if defined(__linux) || defined(CPU_ZERO)  // Linux-like; assume we have pthreads etc
-    cpu_set_t cpuset;
-    CPU_ZERO(&cpuset);
-    const int rc = pthread_getaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
-    if (rc == 0) {
-        unsigned nCpus = 0;
-        for (int i = 0; i < CPU_SETSIZE; ++i) {
-            if (CPU_ISSET(i, &cpuset)) ++nCpus;
-        }
-        return nCpus;
-    }
-#endif
-    // Cannot determine
-    return 0;
-}
-
-//=============================================================================
-// Vlos::getProcessDefaultParallelism implementation
-
-unsigned getProcessDefaultParallelism() VL_MT_SAFE {
-    const unsigned n = getProcessAvailableParallelism();
-    // cppcheck-suppress knownConditionTrueFalse
-    return n ? n : std::thread::hardware_concurrency();
-}
-
-//=========================================================================
-// VlOs::memPeakUsageBytes implementation
-
-void memUsageBytes(uint64_t& peakr, uint64_t& currentr) VL_MT_SAFE {
-    peakr = 0;
-    currentr = 0;
-#if defined(_WIN32) || defined(__MINGW32__)
-    const HANDLE process = GetCurrentProcess();
-    PROCESS_MEMORY_COUNTERS pmc;
-    if (GetProcessMemoryInfo(process, &pmc, sizeof(pmc))) {
-        // The best we can do using simple Windows APIs is to get the size of the working set.
-        peakr = pmc.PeakWorkingSetSize;
-        currentr = pmc.WorkingSetSize;
-    }
-#elif defined(__APPLE__) && defined(__MACH__)
-    mach_task_basic_info_data_t info;
-    mach_msg_type_number_t count = MACH_TASK_BASIC_INFO_COUNT;
-    const kern_return_t ret
-        = task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &count);
-    if (ret == KERN_SUCCESS && count == MACH_TASK_BASIC_INFO_COUNT) {
-        peakr = info.resident_size_max;
-        currentr = info.resident_size;
-    }
-#else
-    // Highly unportable. Sorry
-    std::ifstream is{"/proc/self/status"};
-    if (!is) return;
-    std::string line;
-    uint64_t vmPeak = 0;
-    uint64_t vmRss = 0;
-    uint64_t vmSwap = 0;
-    std::string field;
-    while (std::getline(is, line)) {
-        if (line.rfind("VmPeak:", 0) == 0) {
-            std::stringstream ss{line};
-            ss >> field >> vmPeak;
-        } else if (line.rfind("VmRSS:", 0) == 0) {
-            std::stringstream ss{line};
-            ss >> field >> vmRss;
-        } else if (line.rfind("VmSwap:", 0) == 0) {
-            std::stringstream ss{line};
-            ss >> field >> vmSwap;
-        }
-    }
-    peakr = vmPeak * 1024;
-    currentr = (vmRss + vmSwap) * 1024;
-#endif
-}
-
-//=========================================================================
-// VlOs::getenvStr implementation
-
-std::string getenvStr(const std::string& envvar, const std::string& defaultValue) VL_MT_SAFE {
-    std::string ret;
-#if defined(_MSC_VER)
-    // Note: MinGW does not offer _dupenv_s
-    const char* envvalue = nullptr;
-    _dupenv_s((char**)&envvalue, nullptr, envvar.c_str());
-    if (envvalue != nullptr) {
-        const std::string result{envvalue};
-        free((void*)envvalue);
-        ret = result;
-    } else {
-        ret = defaultValue;
-    }
-#else
-    if (const char* const envvalue = getenv(envvar.c_str())) {
-        ret = envvalue;
-    } else {
-        ret = defaultValue;
-    }
-#endif
-    return ret;
-}
-
-//=========================================================================
-}  //namespace VlOs

From 962d0c5dd691f24c17ae8a39e05a18a8abc0658e Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:24:13 +0800
Subject: [PATCH 13/18] fix: make clang++ happy

---
 components/libc/compilers/common/cstdlib.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/components/libc/compilers/common/cstdlib.c b/components/libc/compilers/common/cstdlib.c
index d8233227210..523b84faf3d 100644
--- a/components/libc/compilers/common/cstdlib.c
+++ b/components/libc/compilers/common/cstdlib.c
@@ -31,7 +31,7 @@ void __rt_libc_exit(int status)
         if (self->pthread_data != RT_NULL)
         {
             extern void pthread_exit(void *value);
-            pthread_exit((void *)status);
+            pthread_exit((void *)(intptr_t)status);
         }
         else
 #endif

From b8f9c3f46ee7dbd1b159a5a84a8b1f87879609de Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:24:50 +0800
Subject: [PATCH 14/18] build: compile needed verilator runtime for
 VERILATOR_ROOT

---
 .../applications/SConscript                   | 67 ++++++++++++++++++-
 1 file changed, 64 insertions(+), 3 deletions(-)

diff --git a/bsp/xiangshan-verilator/applications/SConscript b/bsp/xiangshan-verilator/applications/SConscript
index 7e4f7d3ac37..268a40666c9 100644
--- a/bsp/xiangshan-verilator/applications/SConscript
+++ b/bsp/xiangshan-verilator/applications/SConscript
@@ -1,12 +1,73 @@
+import os
+import subprocess
+
 from building import *
 
-cwd     = GetCurrentDir()
+
+def _get_verilator_include_dir():
+    try:
+        verilator_root = subprocess.check_output(
+            ['verilator', '--getenv', 'VERILATOR_ROOT'], universal_newlines=True
+        ).strip()
+    except (OSError, subprocess.CalledProcessError) as exc:
+        raise RuntimeError(
+            'Failed to query VERILATOR_ROOT via "verilator --getenv VERILATOR_ROOT"'
+        ) from exc
+
+    if not verilator_root:
+        raise RuntimeError('`verilator --getenv VERILATOR_ROOT` returned an empty path')
+
+    include_dir = os.path.join(verilator_root, 'include')
+    if not os.path.isdir(include_dir):
+        raise RuntimeError('Verilator include directory not found: %s' % include_dir)
+
+    return include_dir
+
+cwd = GetCurrentDir()
+
+# Application sources
 src = Glob('*.c') + Glob('*.cpp')
-CPPPATH = [cwd]
 
-group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH)
+# Verilated DUT and runtime support
+verilated_dut_dir = os.path.join(cwd, 'verilated_dut_150')
+verilator_rt_dir = _get_verilator_include_dir()
+verilator_runtime_build_dir = os.path.join(
+    os.path.dirname(cwd), 'build', 'applications', 'verilator_runtime'
+)
+
+# Only pull in the bits of the Verilated runtime we need for a single-threaded,
+# non-tracing simulation on RT-Thread.
+verilator_runtime_src = [
+    os.path.join(verilator_rt_dir, name)
+    for name in [
+        'verilated.cpp',
+        'verilated_random.cpp',
+        'verilated_threads.cpp',
+    ]
+]
+
+verilator_runtime_objs = [
+    Env.Object(
+        target=os.path.join(
+            verilator_runtime_build_dir,
+            os.path.splitext(os.path.basename(source))[0] + Env['OBJSUFFIX'],
+        ),
+        source=source,
+    )[0]
+    for source in verilator_runtime_src
+]
+
+src += Glob(os.path.join('verilated_dut_150', '*.cpp'))
+
+CPPPATH = [cwd, verilated_dut_dir, verilator_rt_dir]
+CPPDEFINES = ['VL_RT_THREAD']
+
+group = DefineGroup(
+    'Applications', src, depend=[''], CPPPATH=CPPPATH, CPPDEFINES=CPPDEFINES
+)
 
 objs = [group]
+objs += verilator_runtime_objs
 
 list = os.listdir(cwd)
 

From ec135322536e03ee9c5560f49330165c37244bc2 Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Thu, 19 Mar 2026 16:27:47 +0800
Subject: [PATCH 15/18] feat: add verilator dut and readme

---
 .../applications/README.md                    |  46 ++++++
 bsp/xiangshan-verilator/applications/main.c   |  19 ---
 bsp/xiangshan-verilator/applications/main.cpp | 138 ++++++++++++++++++
 .../applications/verilated_dut_150/.gitignore |   2 +
 .../verilator-case/Makefile                   |  24 +++
 .../verilator-case/dut_150.v                  |  46 ++++++
 .../verilator-case/tb_150.cpp                 | 133 +++++++++++++++++
 7 files changed, 389 insertions(+), 19 deletions(-)
 create mode 100644 bsp/xiangshan-verilator/applications/README.md
 delete mode 100644 bsp/xiangshan-verilator/applications/main.c
 create mode 100644 bsp/xiangshan-verilator/applications/main.cpp
 create mode 100644 bsp/xiangshan-verilator/applications/verilated_dut_150/.gitignore
 create mode 100644 bsp/xiangshan-verilator/verilator-case/Makefile
 create mode 100644 bsp/xiangshan-verilator/verilator-case/dut_150.v
 create mode 100644 bsp/xiangshan-verilator/verilator-case/tb_150.cpp

diff --git a/bsp/xiangshan-verilator/applications/README.md b/bsp/xiangshan-verilator/applications/README.md
new file mode 100644
index 00000000000..afa1627391a
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/README.md
@@ -0,0 +1,46 @@
+# Verilated DUT on RT-Thread (qemu-virt64-riscv)
+
+## 目标
+
+在 RT-Thread qemu-virt64-riscv BSP 中运行 Verilator 生成的 `dut_150` 仿真程序,使用 C++ 测试逻辑(`applications/main.cpp`)。
+
+## 关键裁剪点
+
+- **单线程运行**:若运行环境缺少 pthread,需强制 `threads(1)`;本目录已将线程池改为 RT-Thread API,可按需开启多线程。
+- **禁用计时/波形**:去除 coroutine/timing/trace 相关源码与生成选项:`--no-timing --no-trace`。
+- **RT-Thread 适配宏**:使用 `-DVL_RT_THREAD -DVL_MT_DISABLED` 避免调用缺失的 libc/线程特性,使用自带的字符串/数学函数。
+- **运行库精简**:通过 `verilator --getenv VERILATOR_ROOT` 获取 Verilator 安装目录,只从其 `include/` 中编译 `verilated.cpp`、`verilated_random.cpp`、`verilated_threads.cpp` 这 3 个运行时源文件。
+- **运行库来源**:BSP 不再依赖 `applications/verilator_runtime` 这份本地拷贝参与构建,而是直接使用当前系统 Verilator 安装中的 runtime。
+
+## 生成模型
+
+路径:`bsp/xiangshan-verilator/verilator-case/Makefile`
+
+- 默认调用:`verilator -Wall --cc dut_150.v --exe tb_150.cpp -Mdir ../applications/verilated_dut_150 --no-timing --no-trace --threads 1 --CFLAGS "-DVL_RT_THREAD -DVL_MT_DISABLED"`
+- 输出:`applications/verilated_dut_150/` 下的 `Vdut_150.*`。
+
+## 应用构建集成
+
+文件:`applications/SConscript`
+
+- 编译源:`*.c`/`*.cpp` + `verilated_dut_150/*.cpp` + 运行库最小集。
+- `CPPPATH`:应用目录、`verilated_dut_150`、`verilator --getenv VERILATOR_ROOT` 返回目录下的 `include/`。
+- `CPPDEFINES`:包含 `VL_RT_THREAD`。
+
+## 运行入口
+
+文件:`applications/main.cpp`
+
+- 创建上下文后可按需设置线程数,默认跟随 Verilator 生成的 `threads()`。
+- 重置与驱动 DUT 的测试序列,带有 `rt_kprintf`/`std::cout` 日志方便调试。
+- 为避免退出流程触发异常,结尾保持循环休眠(`rt_thread_mdelay`)。
+
+## 构建与运行
+
+- 构建:`scons --exec-path=$HOME/riscv-baremetal/bin --cc-prefix=riscv64-unknown-elf-`(在 `bsp/xiangshan-verilator`)。
+- 运行:`timeout 1 ~/qemu/build/qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin`。
+
+## 已知行为
+
+- 文件系统未挂载会提示 `DFS.fs mount / failed ...`,但不影响仿真测试逻辑。
+- 运行日志会打印 `[TB] ...`,确认 DUT 已构造并执行测试用例。
diff --git a/bsp/xiangshan-verilator/applications/main.c b/bsp/xiangshan-verilator/applications/main.c
deleted file mode 100644
index b55df4da2b1..00000000000
--- a/bsp/xiangshan-verilator/applications/main.c
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * Copyright (c) 2006-2018, RT-Thread Development Team
- *
- * SPDX-License-Identifier: Apache-2.0
- *
- * Change Logs:
- * Date           Author       Notes
- */
-
-#include 
-#include 
-#include 
-
-int main(void)
-{
-    rt_kprintf("Hello RISC-V\n");
-
-    return 0;
-}
diff --git a/bsp/xiangshan-verilator/applications/main.cpp b/bsp/xiangshan-verilator/applications/main.cpp
new file mode 100644
index 00000000000..042146bdc79
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/main.cpp
@@ -0,0 +1,138 @@
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#include "Vdut_150.h"
+
+static inline void tick(Vdut_150 *dut, VerilatedContext *ctx) {
+    dut->clk = 0;
+    dut->eval();
+    ctx->timeInc(1);
+    dut->clk = 1;
+    dut->eval();
+    ctx->timeInc(1);
+}
+
+// g[3:1] packed into low 3 bits of dut->g (bit0=g[1], bit1=g[2], bit2=g[3])
+static inline uint8_t g_bus(Vdut_150 *dut) {
+    return static_cast(dut->g & 0x07u);
+}
+
+int main(int argc, char **argv) {
+    Verilated::commandArgs(argc, argv);
+    auto ctx = std::make_unique();
+    ctx->traceEverOn(false);
+    ctx->threads(1);
+    auto dut = std::make_unique(ctx.get());
+
+    enum { A = 0, B = 1, C = 2, D = 3 };
+    uint8_t state = A;
+
+    auto apply_reset = [&]() {
+        dut->resetn = 0;
+        dut->r = 0;
+        tick(dut.get(), ctx.get());
+        dut->resetn = 1;
+        state = A;
+    };
+
+    auto step = [&](uint8_t r_val, const char *ctx_str) {
+        uint8_t r = r_val & 0x07u;  // r[3:1] in bits [2:0]
+        dut->r = r;
+
+        // Model next_state with r[1:3] mapped to bits 0..2.
+        bool r1 = (r & 0x1u) != 0;
+        bool r2 = (r & 0x2u) != 0;
+        bool r3 = (r & 0x4u) != 0;
+
+        uint8_t next = state;
+        switch (state) {
+            case A:
+                if (r1)                        next = B;
+                else if (!r1 && r2)            next = C;
+                else if (!r1 && !r2 && r3)     next = D;
+                else                           next = A;
+                break;
+            case B:
+                next = r1 ? B : A;
+                break;
+            case C:
+                next = r2 ? C : A;
+                break;
+            case D:
+                next = r3 ? D : A;
+                break;
+        }
+
+        rt_kprintf("[TB] cycle time=%llu state=%u r=%u next=%u g_bus=%u (%s)\n",
+                   static_cast(ctx->time()), state, r, next,
+                   static_cast(g_bus(dut.get())),
+                   ctx_str);
+
+        tick(dut.get(), ctx.get());
+        state = next;
+
+        // Expected g bus: bit0 for B, bit1 for C, bit2 for D.
+        uint8_t g_exp = 0;
+        if (state == B) g_exp = 0x1u;
+        else if (state == C) g_exp = 0x2u;
+        else if (state == D) g_exp = 0x4u;
+
+        uint8_t g_act = g_bus(dut.get());
+        if (g_act != g_exp) {
+            std::cerr << "[TB] dut_150 failed (" << ctx_str << "): "
+                      << "state=" << int(state)
+                      << " r=0x" << std::hex << int(r)
+                      << " expected g=0x" << int(g_exp)
+                      << " got g=0x" << int(g_act) << std::dec << std::endl;
+            std::exit(EXIT_FAILURE);
+        }
+    };
+
+    // Scenario 1: exercise all branches in state A (r1, r2, r3, none).
+    apply_reset();                  // A
+    step(0x00u, "A->A_none");       // else
+    step(0x01u, "A->B_r1");         // if(r1)
+    // Now in B; release back to A
+    step(0x00u, "B->A_release_r1");
+
+    apply_reset();                  // A
+    step(0x02u, "A->C_r2");         // else if(~r1 & r2)
+    step(0x00u, "C->A_release_r2");
+
+    apply_reset();                  // A
+    step(0x04u, "A->D_r3");         // else if(~r1 & ~r2 & r3)
+    step(0x00u, "D->A_release_r3");
+
+    // Scenario 2: exercise B state's if/else.
+    apply_reset();                  // A
+    step(0x01u, "A->B_for_B");      // into B
+    step(0x01u, "B->B_hold_r1");    // if(r1) branch
+    step(0x00u, "B->A_else");       // else branch
+
+    // Scenario 3: C state's branches.
+    apply_reset();                  // A
+    step(0x02u, "A->C_for_C");
+    step(0x02u, "C->C_hold_r2");    // if(r2)
+    step(0x00u, "C->A_else_C");     // else
+
+    // Scenario 4: D state's branches.
+    apply_reset();                  // A
+    step(0x04u, "A->D_for_D");
+    step(0x04u, "D->D_hold_r3");    // if(r3)
+    step(0x00u, "D->A_else_D");     // else
+
+    std::cout << "[TB] dut_150 passed: fixed-priority arbiter full coverage" << std::endl;
+
+#if VM_COVERAGE
+    const char *covPath = std::getenv("VERILATOR_COV_FILE");
+    if (covPath == nullptr || covPath[0] == '\0') {
+        covPath = "coverage.dat";
+    }
+    VerilatedCov::write(covPath);
+#endif
+    return EXIT_SUCCESS;
+}
diff --git a/bsp/xiangshan-verilator/applications/verilated_dut_150/.gitignore b/bsp/xiangshan-verilator/applications/verilated_dut_150/.gitignore
new file mode 100644
index 00000000000..d6b7ef32c84
--- /dev/null
+++ b/bsp/xiangshan-verilator/applications/verilated_dut_150/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/bsp/xiangshan-verilator/verilator-case/Makefile b/bsp/xiangshan-verilator/verilator-case/Makefile
new file mode 100644
index 00000000000..3a481ccc60d
--- /dev/null
+++ b/bsp/xiangshan-verilator/verilator-case/Makefile
@@ -0,0 +1,24 @@
+VERILATOR ?= verilator
+TOP       := dut_150
+TB        := tb_150.cpp
+SRC       := $(TOP).v
+APP_DIR   := ../applications
+OUT_DIR   := $(APP_DIR)/verilated_$(TOP)
+
+# Keep the generated model strictly single-threaded and avoid features
+# that pull in timing/coroutine support which we trimmed from the runtime.
+VERILATOR_FLAGS := --no-timing --no-trace --threads 1
+# Propagate our RT-Thread specific switches into generated code.
+VERILATOR_CFLAGS := -DVL_RT_THREAD -DVL_MT_DISABLED
+
+.PHONY: all clean
+
+all: $(OUT_DIR)/V$(TOP).cpp
+
+$(OUT_DIR)/V$(TOP).cpp: $(SRC) $(TB) Makefile
+	@mkdir -p $(OUT_DIR)
+	$(VERILATOR) -Wall --cc $(SRC) --exe $(TB) -Mdir $(OUT_DIR) \
+		$(VERILATOR_FLAGS) --CFLAGS "$(VERILATOR_CFLAGS)"
+
+clean:
+	@rm -rf $(OUT_DIR)
diff --git a/bsp/xiangshan-verilator/verilator-case/dut_150.v b/bsp/xiangshan-verilator/verilator-case/dut_150.v
new file mode 100644
index 00000000000..6030c2b9d5e
--- /dev/null
+++ b/bsp/xiangshan-verilator/verilator-case/dut_150.v
@@ -0,0 +1,46 @@
+module dut_150 (
+    input clk,
+    input resetn,    // active-low synchronous reset
+    input [3:1] r,   // request
+    output [3:1] g   // grant
+  ); 
+
+    parameter a=2'd0, b=2'd1, c=2'd2, d=2'd3;
+    reg [1:0] state, next_state;
+    
+    always@(*) begin
+        case(state)
+            a: begin
+                if(r[1])    next_state = b;
+                else if(~r[1] & r[2])   next_state = c;
+                else if(~r[1] & ~r[2] & r[3])   next_state = d;
+                else    next_state = a;
+            end
+            b: begin
+                if(r[1])    next_state = b;
+                else        next_state = a;
+            end
+            c: begin
+                if(r[2])    next_state = c;
+                else        next_state = a;
+            end
+            d: begin
+                if(r[3])    next_state = d;
+                else        next_state = a;
+            end
+        endcase
+    end
+
+    always@(posedge clk) begin
+        if(~resetn)
+            state <= a;
+        else
+            state <= next_state;
+    end
+    
+    assign g[1] = (state == b);
+    assign g[2] = (state == c);
+    assign g[3] = (state == d);
+    
+    
+endmodule
diff --git a/bsp/xiangshan-verilator/verilator-case/tb_150.cpp b/bsp/xiangshan-verilator/verilator-case/tb_150.cpp
new file mode 100644
index 00000000000..637881bd91b
--- /dev/null
+++ b/bsp/xiangshan-verilator/verilator-case/tb_150.cpp
@@ -0,0 +1,133 @@
+#include 
+#include 
+#include 
+#include 
+
+#include "verilated.h"
+#include "verilated_cov.h"
+#include "Vdut_150.h"
+
+static inline void tick(Vdut_150 *dut, VerilatedContext *ctx) {
+    dut->clk = 0;
+    dut->eval();
+    ctx->timeInc(1);
+    dut->clk = 1;
+    dut->eval();
+    ctx->timeInc(1);
+}
+
+// g[3:1] packed into low 3 bits of dut->g (bit0=g[1], bit1=g[2], bit2=g[3])
+static inline uint8_t g_bus(Vdut_150 *dut) {
+    return static_cast(dut->g & 0x07u);
+}
+
+int main(int argc, char **argv) {
+    Verilated::commandArgs(argc, argv);
+    auto ctx = std::make_unique();
+    ctx->traceEverOn(false);
+
+    auto dut = std::make_unique(ctx.get());
+
+    enum { A = 0, B = 1, C = 2, D = 3 };
+    uint8_t state = A;
+
+    auto apply_reset = [&]() {
+        dut->resetn = 0;
+        dut->r = 0;
+        tick(dut.get(), ctx.get());
+        dut->resetn = 1;
+        state = A;
+    };
+
+    auto step = [&](uint8_t r_val, const char *ctx_str) {
+        uint8_t r = r_val & 0x07u;  // r[3:1] in bits [2:0]
+        dut->r = r;
+
+        // Model next_state with r[1:3] mapped to bits 0..2.
+        bool r1 = (r & 0x1u) != 0;
+        bool r2 = (r & 0x2u) != 0;
+        bool r3 = (r & 0x4u) != 0;
+
+        uint8_t next = state;
+        switch (state) {
+            case A:
+                if (r1)                        next = B;
+                else if (!r1 && r2)            next = C;
+                else if (!r1 && !r2 && r3)     next = D;
+                else                           next = A;
+                break;
+            case B:
+                next = r1 ? B : A;
+                break;
+            case C:
+                next = r2 ? C : A;
+                break;
+            case D:
+                next = r3 ? D : A;
+                break;
+        }
+
+        tick(dut.get(), ctx.get());
+        state = next;
+
+        // Expected g bus: bit0 for B, bit1 for C, bit2 for D.
+        uint8_t g_exp = 0;
+        if (state == B) g_exp = 0x1u;
+        else if (state == C) g_exp = 0x2u;
+        else if (state == D) g_exp = 0x4u;
+
+        uint8_t g_act = g_bus(dut.get());
+        if (g_act != g_exp) {
+            std::cerr << "[TB] dut_150 failed (" << ctx_str << "): "
+                      << "state=" << int(state)
+                      << " r=0x" << std::hex << int(r)
+                      << " expected g=0x" << int(g_exp)
+                      << " got g=0x" << int(g_act) << std::dec << std::endl;
+            std::exit(EXIT_FAILURE);
+        }
+    };
+
+    // Scenario 1: exercise all branches in state A (r1, r2, r3, none).
+    apply_reset();                  // A
+    step(0x00u, "A->A_none");       // else
+    step(0x01u, "A->B_r1");         // if(r1)
+    // Now in B; release back to A
+    step(0x00u, "B->A_release_r1");
+
+    apply_reset();                  // A
+    step(0x02u, "A->C_r2");         // else if(~r1 & r2)
+    step(0x00u, "C->A_release_r2");
+
+    apply_reset();                  // A
+    step(0x04u, "A->D_r3");         // else if(~r1 & ~r2 & r3)
+    step(0x00u, "D->A_release_r3");
+
+    // Scenario 2: exercise B state's if/else.
+    apply_reset();                  // A
+    step(0x01u, "A->B_for_B");      // into B
+    step(0x01u, "B->B_hold_r1");    // if(r1) branch
+    step(0x00u, "B->A_else");       // else branch
+
+    // Scenario 3: C state's branches.
+    apply_reset();                  // A
+    step(0x02u, "A->C_for_C");
+    step(0x02u, "C->C_hold_r2");    // if(r2)
+    step(0x00u, "C->A_else_C");     // else
+
+    // Scenario 4: D state's branches.
+    apply_reset();                  // A
+    step(0x04u, "A->D_for_D");
+    step(0x04u, "D->D_hold_r3");    // if(r3)
+    step(0x00u, "D->A_else_D");     // else
+
+    std::cout << "[TB] dut_150 passed: fixed-priority arbiter full coverage" << std::endl;
+
+#if VM_COVERAGE
+    const char *covPath = std::getenv("VERILATOR_COV_FILE");
+    if (covPath == nullptr || covPath[0] == '\0') {
+        covPath = "coverage.dat";
+    }
+    VerilatedCov::write(covPath);
+#endif
+    return EXIT_SUCCESS;
+}

From d38c80233f5c78f9cf3bc54bfa9f6926057b5c90 Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Fri, 20 Mar 2026 18:19:07 +0800
Subject: [PATCH 16/18] build: add xiangshan-verilator-xiangshan bsp

---
 .gitignore                                    |    2 +
 bsp/xiangshan-verilator-xiangshan/.config     | 1582 +++++++++++++++++
 bsp/xiangshan-verilator-xiangshan/.gitignore  |    3 +
 bsp/xiangshan-verilator-xiangshan/Kconfig     |   61 +
 bsp/xiangshan-verilator-xiangshan/README.md   |  407 +++++
 .../README_cn.md                              |  411 +++++
 bsp/xiangshan-verilator-xiangshan/SConscript  |   14 +
 bsp/xiangshan-verilator-xiangshan/SConstruct  |   62 +
 .../applications/README.md                    |   46 +
 .../applications/SConscript                   |   78 +
 .../applications/main.cpp                     |  138 ++
 .../applications/test/SConscript              |   17 +
 .../applications/test/test_vector/SConscript  |    9 +
 .../test/test_vector/test_vector.c            |  178 ++
 .../applications/verilated_dut_150/.gitignore |    2 +
 .../driver/Kconfig                            |   43 +
 .../driver/SConscript                         |   19 +
 .../driver/asm/sbiasm.h                       |   10 +
 .../driver/asm/sbidef.h                       |   27 +
 .../driver/board.c                            |  118 ++
 .../driver/board.h                            |   34 +
 .../driver/drv_uart.c                         |  165 ++
 .../driver/drv_uart.h                         |   62 +
 .../driver/drv_virtio.c                       |  100 ++
 .../driver/drv_virtio.h                       |   16 +
 .../driver/virt.h                             |   30 +
 bsp/xiangshan-verilator-xiangshan/link.lds    |  201 +++
 .../link_cpus.lds                             |    1 +
 .../link_smart.lds                            |  202 +++
 .../link_stacksize.lds                        |    1 +
 bsp/xiangshan-verilator-xiangshan/qemu-dbg.sh |   16 +
 .../qemu-dumpdtb.sh                           |    1 +
 .../qemu-nographic.bat                        |    9 +
 .../qemu-rv64ilp32-nographic.sh               |    1 +
 .../qemu-v-dbg.sh                             |    4 +
 .../qemu-v-nographic.sh                       |    9 +
 bsp/xiangshan-verilator-xiangshan/rtconfig.h  |  557 ++++++
 bsp/xiangshan-verilator-xiangshan/rtconfig.py |   52 +
 bsp/xiangshan-verilator-xiangshan/run.sh      |   43 +
 .../smart-env.bat                             |   30 +
 .../verilator-case/Makefile                   |   24 +
 .../verilator-case/dut_150.v                  |   46 +
 .../verilator-case/tb_150.cpp                 |  133 ++
 43 files changed, 4964 insertions(+)
 create mode 100644 bsp/xiangshan-verilator-xiangshan/.config
 create mode 100644 bsp/xiangshan-verilator-xiangshan/.gitignore
 create mode 100644 bsp/xiangshan-verilator-xiangshan/Kconfig
 create mode 100644 bsp/xiangshan-verilator-xiangshan/README.md
 create mode 100644 bsp/xiangshan-verilator-xiangshan/README_cn.md
 create mode 100644 bsp/xiangshan-verilator-xiangshan/SConscript
 create mode 100644 bsp/xiangshan-verilator-xiangshan/SConstruct
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/README.md
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/SConscript
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/main.cpp
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/test/SConscript
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/test/test_vector/SConscript
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/test/test_vector/test_vector.c
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/verilated_dut_150/.gitignore
 create mode 100644 bsp/xiangshan-verilator-xiangshan/driver/Kconfig
 create mode 100644 bsp/xiangshan-verilator-xiangshan/driver/SConscript
 create mode 100644 bsp/xiangshan-verilator-xiangshan/driver/asm/sbiasm.h
 create mode 100644 bsp/xiangshan-verilator-xiangshan/driver/asm/sbidef.h
 create mode 100644 bsp/xiangshan-verilator-xiangshan/driver/board.c
 create mode 100644 bsp/xiangshan-verilator-xiangshan/driver/board.h
 create mode 100644 bsp/xiangshan-verilator-xiangshan/driver/drv_uart.c
 create mode 100644 bsp/xiangshan-verilator-xiangshan/driver/drv_uart.h
 create mode 100644 bsp/xiangshan-verilator-xiangshan/driver/drv_virtio.c
 create mode 100644 bsp/xiangshan-verilator-xiangshan/driver/drv_virtio.h
 create mode 100644 bsp/xiangshan-verilator-xiangshan/driver/virt.h
 create mode 100644 bsp/xiangshan-verilator-xiangshan/link.lds
 create mode 100644 bsp/xiangshan-verilator-xiangshan/link_cpus.lds
 create mode 100644 bsp/xiangshan-verilator-xiangshan/link_smart.lds
 create mode 100644 bsp/xiangshan-verilator-xiangshan/link_stacksize.lds
 create mode 100755 bsp/xiangshan-verilator-xiangshan/qemu-dbg.sh
 create mode 100755 bsp/xiangshan-verilator-xiangshan/qemu-dumpdtb.sh
 create mode 100644 bsp/xiangshan-verilator-xiangshan/qemu-nographic.bat
 create mode 100755 bsp/xiangshan-verilator-xiangshan/qemu-rv64ilp32-nographic.sh
 create mode 100644 bsp/xiangshan-verilator-xiangshan/qemu-v-dbg.sh
 create mode 100644 bsp/xiangshan-verilator-xiangshan/qemu-v-nographic.sh
 create mode 100644 bsp/xiangshan-verilator-xiangshan/rtconfig.h
 create mode 100644 bsp/xiangshan-verilator-xiangshan/rtconfig.py
 create mode 100755 bsp/xiangshan-verilator-xiangshan/run.sh
 create mode 100644 bsp/xiangshan-verilator-xiangshan/smart-env.bat
 create mode 100644 bsp/xiangshan-verilator-xiangshan/verilator-case/Makefile
 create mode 100644 bsp/xiangshan-verilator-xiangshan/verilator-case/dut_150.v
 create mode 100644 bsp/xiangshan-verilator-xiangshan/verilator-case/tb_150.cpp

diff --git a/.gitignore b/.gitignore
index 101a559ab55..61efa3ea3cc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -70,3 +70,5 @@ vdso.lds
 
 # mac
 .DS_Store
+
+.metals/
diff --git a/bsp/xiangshan-verilator-xiangshan/.config b/bsp/xiangshan-verilator-xiangshan/.config
new file mode 100644
index 00000000000..1079575c187
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/.config
@@ -0,0 +1,1582 @@
+
+#
+# RT-Thread Kernel
+#
+
+#
+# klibc options
+#
+
+#
+# rt_vsnprintf options
+#
+# CONFIG_RT_KLIBC_USING_LIBC_VSNPRINTF is not set
+CONFIG_RT_KLIBC_USING_VSNPRINTF_LONGLONG=y
+CONFIG_RT_KLIBC_USING_VSNPRINTF_STANDARD=y
+CONFIG_RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS=y
+CONFIG_RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS=y
+CONFIG_RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER=y
+CONFIG_RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER=y
+# CONFIG_RT_KLIBC_USING_VSNPRINTF_MSVC_STYLE_INTEGER_SPECIFIERS is not set
+CONFIG_RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE=32
+CONFIG_RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE=32
+CONFIG_RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION=6
+CONFIG_RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL=9
+CONFIG_RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS=4
+# end of rt_vsnprintf options
+
+#
+# rt_vsscanf options
+#
+# CONFIG_RT_KLIBC_USING_LIBC_VSSCANF is not set
+# end of rt_vsscanf options
+
+#
+# rt_memset options
+#
+# CONFIG_RT_KLIBC_USING_USER_MEMSET is not set
+# CONFIG_RT_KLIBC_USING_LIBC_MEMSET is not set
+# CONFIG_RT_KLIBC_USING_TINY_MEMSET is not set
+# end of rt_memset options
+
+#
+# rt_memcpy options
+#
+# CONFIG_RT_KLIBC_USING_USER_MEMCPY is not set
+# CONFIG_RT_KLIBC_USING_LIBC_MEMCPY is not set
+# CONFIG_RT_KLIBC_USING_TINY_MEMCPY is not set
+# end of rt_memcpy options
+
+#
+# rt_memmove options
+#
+# CONFIG_RT_KLIBC_USING_USER_MEMMOVE is not set
+# CONFIG_RT_KLIBC_USING_LIBC_MEMMOVE is not set
+# end of rt_memmove options
+
+#
+# rt_memcmp options
+#
+# CONFIG_RT_KLIBC_USING_USER_MEMCMP is not set
+# CONFIG_RT_KLIBC_USING_LIBC_MEMCMP is not set
+# end of rt_memcmp options
+
+#
+# rt_strstr options
+#
+# CONFIG_RT_KLIBC_USING_USER_STRSTR is not set
+# CONFIG_RT_KLIBC_USING_LIBC_STRSTR is not set
+# end of rt_strstr options
+
+#
+# rt_strcasecmp options
+#
+# CONFIG_RT_KLIBC_USING_USER_STRCASECMP is not set
+# end of rt_strcasecmp options
+
+#
+# rt_strncpy options
+#
+# CONFIG_RT_KLIBC_USING_USER_STRNCPY is not set
+# CONFIG_RT_KLIBC_USING_LIBC_STRNCPY is not set
+# end of rt_strncpy options
+
+#
+# rt_strcpy options
+#
+# CONFIG_RT_KLIBC_USING_USER_STRCPY is not set
+# CONFIG_RT_KLIBC_USING_LIBC_STRCPY is not set
+# end of rt_strcpy options
+
+#
+# rt_strncmp options
+#
+# CONFIG_RT_KLIBC_USING_USER_STRNCMP is not set
+# CONFIG_RT_KLIBC_USING_LIBC_STRNCMP is not set
+# end of rt_strncmp options
+
+#
+# rt_strcmp options
+#
+# CONFIG_RT_KLIBC_USING_USER_STRCMP is not set
+# CONFIG_RT_KLIBC_USING_LIBC_STRCMP is not set
+# end of rt_strcmp options
+
+#
+# rt_strlen options
+#
+# CONFIG_RT_KLIBC_USING_USER_STRLEN is not set
+# CONFIG_RT_KLIBC_USING_LIBC_STRLEN is not set
+# end of rt_strlen options
+
+#
+# rt_strnlen options
+#
+# CONFIG_RT_KLIBC_USING_USER_STRNLEN is not set
+# end of rt_strnlen options
+# end of klibc options
+
+CONFIG_RT_NAME_MAX=24
+# CONFIG_RT_USING_ARCH_DATA_TYPE is not set
+# CONFIG_RT_USING_NANO is not set
+# CONFIG_RT_USING_SMART is not set
+# CONFIG_RT_USING_AMP is not set
+# CONFIG_RT_USING_SMP is not set
+CONFIG_RT_CPUS_NR=1
+CONFIG_RT_ALIGN_SIZE=8
+# CONFIG_RT_THREAD_PRIORITY_8 is not set
+CONFIG_RT_THREAD_PRIORITY_32=y
+# CONFIG_RT_THREAD_PRIORITY_256 is not set
+CONFIG_RT_THREAD_PRIORITY_MAX=32
+CONFIG_RT_TICK_PER_SECOND=100
+CONFIG_RT_USING_OVERFLOW_CHECK=y
+CONFIG_RT_USING_HOOK=y
+CONFIG_RT_HOOK_USING_FUNC_PTR=y
+# CONFIG_RT_USING_HOOKLIST is not set
+CONFIG_RT_USING_IDLE_HOOK=y
+CONFIG_RT_IDLE_HOOK_LIST_SIZE=4
+CONFIG_IDLE_THREAD_STACK_SIZE=16384
+CONFIG_RT_USING_TIMER_SOFT=y
+CONFIG_RT_TIMER_THREAD_PRIO=4
+CONFIG_RT_TIMER_THREAD_STACK_SIZE=16384
+# CONFIG_RT_USING_TIMER_ALL_SOFT is not set
+CONFIG_RT_USING_CPU_USAGE_TRACER=y
+
+#
+# kservice options
+#
+# CONFIG_RT_USING_TINY_FFS is not set
+# end of kservice options
+
+CONFIG_RT_USING_DEBUG=y
+CONFIG_RT_DEBUGING_ASSERT=y
+CONFIG_RT_DEBUGING_COLOR=y
+CONFIG_RT_DEBUGING_CONTEXT=y
+# CONFIG_RT_DEBUGING_AUTO_INIT is not set
+# CONFIG_RT_USING_CI_ACTION is not set
+
+#
+# Inter-Thread communication
+#
+CONFIG_RT_USING_SEMAPHORE=y
+CONFIG_RT_USING_MUTEX=y
+CONFIG_RT_USING_EVENT=y
+CONFIG_RT_USING_MAILBOX=y
+CONFIG_RT_USING_MESSAGEQUEUE=y
+# CONFIG_RT_USING_MESSAGEQUEUE_PRIORITY is not set
+CONFIG_RT_USING_SIGNALS=y
+# end of Inter-Thread communication
+
+#
+# Memory Management
+#
+CONFIG_RT_USING_MEMPOOL=y
+# CONFIG_RT_USING_SMALL_MEM is not set
+CONFIG_RT_USING_SLAB=y
+# CONFIG_RT_USING_MEMHEAP is not set
+# CONFIG_RT_USING_SMALL_MEM_AS_HEAP is not set
+# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set
+CONFIG_RT_USING_SLAB_AS_HEAP=y
+# CONFIG_RT_USING_USERHEAP is not set
+# CONFIG_RT_USING_NOHEAP is not set
+CONFIG_RT_USING_MEMTRACE=y
+# CONFIG_RT_USING_HEAP_ISR is not set
+CONFIG_RT_USING_HEAP=y
+# end of Memory Management
+
+CONFIG_RT_USING_DEVICE=y
+CONFIG_RT_USING_DEVICE_OPS=y
+# CONFIG_RT_USING_INTERRUPT_INFO is not set
+# CONFIG_RT_USING_THREADSAFE_PRINTF is not set
+CONFIG_RT_USING_CONSOLE=y
+CONFIG_RT_CONSOLEBUF_SIZE=256
+CONFIG_RT_CONSOLE_DEVICE_NAME="uart0"
+CONFIG_RT_VER_NUM=0x50300
+# CONFIG_RT_USING_STDC_ATOMIC is not set
+CONFIG_RT_BACKTRACE_LEVEL_MAX_NR=32
+# end of RT-Thread Kernel
+
+CONFIG_ARCH_CPU_64BIT=y
+CONFIG_RT_USING_CACHE=y
+CONFIG_ARCH_MM_MMU=y
+CONFIG_ARCH_RISCV=y
+CONFIG_ARCH_RISCV64=y
+CONFIG_ARCH_USING_NEW_CTX_SWITCH=y
+CONFIG_ARCH_USING_RISCV_COMMON64=y
+CONFIG_ARCH_REMAP_KERNEL=y
+
+#
+# RT-Thread Components
+#
+CONFIG_RT_USING_COMPONENTS_INIT=y
+CONFIG_RT_USING_USER_MAIN=y
+CONFIG_RT_MAIN_THREAD_STACK_SIZE=8388608
+CONFIG_RT_MAIN_THREAD_PRIORITY=10
+# CONFIG_RT_USING_LEGACY is not set
+CONFIG_RT_USING_MSH=y
+CONFIG_RT_USING_FINSH=y
+CONFIG_FINSH_USING_MSH=y
+CONFIG_FINSH_THREAD_NAME="tshell"
+CONFIG_FINSH_THREAD_PRIORITY=20
+CONFIG_FINSH_THREAD_STACK_SIZE=16384
+CONFIG_FINSH_USING_HISTORY=y
+CONFIG_FINSH_HISTORY_LINES=10
+# CONFIG_FINSH_USING_WORD_OPERATION is not set
+# CONFIG_FINSH_USING_FUNC_EXT is not set
+CONFIG_FINSH_USING_SYMTAB=y
+CONFIG_FINSH_CMD_SIZE=80
+CONFIG_MSH_USING_BUILT_IN_COMMANDS=y
+CONFIG_FINSH_USING_DESCRIPTION=y
+# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set
+# CONFIG_FINSH_USING_AUTH is not set
+CONFIG_FINSH_ARG_MAX=10
+CONFIG_FINSH_USING_OPTION_COMPLETION=y
+
+#
+# DFS: device virtual file system
+#
+CONFIG_RT_USING_DFS=y
+CONFIG_DFS_USING_POSIX=y
+CONFIG_DFS_USING_WORKDIR=y
+CONFIG_DFS_FD_MAX=32
+# CONFIG_RT_USING_DFS_V1 is not set
+CONFIG_RT_USING_DFS_V2=y
+CONFIG_RT_USING_DFS_ELMFAT=y
+
+#
+# elm-chan's FatFs, Generic FAT Filesystem Module
+#
+CONFIG_RT_DFS_ELM_CODE_PAGE=437
+CONFIG_RT_DFS_ELM_WORD_ACCESS=y
+# CONFIG_RT_DFS_ELM_USE_LFN_0 is not set
+# CONFIG_RT_DFS_ELM_USE_LFN_1 is not set
+# CONFIG_RT_DFS_ELM_USE_LFN_2 is not set
+CONFIG_RT_DFS_ELM_USE_LFN_3=y
+CONFIG_RT_DFS_ELM_USE_LFN=3
+CONFIG_RT_DFS_ELM_LFN_UNICODE_0=y
+# CONFIG_RT_DFS_ELM_LFN_UNICODE_1 is not set
+# CONFIG_RT_DFS_ELM_LFN_UNICODE_2 is not set
+# CONFIG_RT_DFS_ELM_LFN_UNICODE_3 is not set
+CONFIG_RT_DFS_ELM_LFN_UNICODE=0
+CONFIG_RT_DFS_ELM_MAX_LFN=255
+CONFIG_RT_DFS_ELM_DRIVES=2
+CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=512
+# CONFIG_RT_DFS_ELM_USE_ERASE is not set
+CONFIG_RT_DFS_ELM_REENTRANT=y
+CONFIG_RT_DFS_ELM_MUTEX_TIMEOUT=3000
+# CONFIG_RT_DFS_ELM_USE_EXFAT is not set
+# end of elm-chan's FatFs, Generic FAT Filesystem Module
+
+CONFIG_RT_USING_DFS_DEVFS=y
+CONFIG_RT_USING_DFS_ROMFS=y
+# CONFIG_RT_USING_DFS_CROMFS is not set
+# CONFIG_RT_USING_DFS_TMPFS is not set
+# CONFIG_RT_USING_DFS_MQUEUE is not set
+# end of DFS: device virtual file system
+
+# CONFIG_RT_USING_FAL is not set
+
+#
+# Device Drivers
+#
+# CONFIG_RT_USING_DM is not set
+# CONFIG_RT_USING_DEV_BUS is not set
+CONFIG_RT_USING_DEVICE_IPC=y
+CONFIG_RT_UNAMED_PIPE_NUMBER=64
+CONFIG_RT_USING_SYSTEM_WORKQUEUE=y
+CONFIG_RT_SYSTEM_WORKQUEUE_STACKSIZE=8192
+CONFIG_RT_SYSTEM_WORKQUEUE_PRIORITY=23
+CONFIG_RT_USING_SERIAL=y
+CONFIG_RT_USING_SERIAL_V1=y
+# CONFIG_RT_USING_SERIAL_V2 is not set
+CONFIG_RT_SERIAL_USING_DMA=y
+CONFIG_RT_SERIAL_RB_BUFSZ=64
+# CONFIG_RT_USING_SERIAL_BYPASS is not set
+# CONFIG_RT_USING_CAN is not set
+CONFIG_RT_USING_CPUTIME=y
+CONFIG_RT_USING_CPUTIME_RISCV=y
+CONFIG_CPUTIME_TIMER_FREQ=10000000
+# CONFIG_RT_USING_I2C is not set
+# CONFIG_RT_USING_PHY is not set
+# CONFIG_RT_USING_PHY_V2 is not set
+# CONFIG_RT_USING_ADC is not set
+# CONFIG_RT_USING_DAC is not set
+CONFIG_RT_USING_NULL=y
+CONFIG_RT_USING_ZERO=y
+CONFIG_RT_USING_RANDOM=y
+# CONFIG_RT_USING_PWM is not set
+# CONFIG_RT_USING_PULSE_ENCODER is not set
+# CONFIG_RT_USING_INPUT_CAPTURE is not set
+# CONFIG_RT_USING_MTD_NOR is not set
+# CONFIG_RT_USING_MTD_NAND is not set
+# CONFIG_RT_USING_PM is not set
+CONFIG_RT_USING_RTC=y
+# CONFIG_RT_USING_ALARM is not set
+CONFIG_RT_USING_SOFT_RTC=y
+# CONFIG_RT_USING_SDIO is not set
+# CONFIG_RT_USING_SPI is not set
+# CONFIG_RT_USING_WDT is not set
+# CONFIG_RT_USING_AUDIO is not set
+# CONFIG_RT_USING_SENSOR is not set
+# CONFIG_RT_USING_TOUCH is not set
+# CONFIG_RT_USING_LCD is not set
+# CONFIG_RT_USING_HWCRYPTO is not set
+# CONFIG_RT_USING_WIFI is not set
+# CONFIG_RT_USING_BLK is not set
+# CONFIG_RT_USING_VIRTIO is not set
+# CONFIG_RT_USING_VIRTIO_MMIO_ALIGN is not set
+# CONFIG_RT_USING_PIN is not set
+CONFIG_RT_USING_KTIME=y
+# CONFIG_RT_USING_HWTIMER is not set
+# CONFIG_RT_USING_CHERRYUSB is not set
+# end of Device Drivers
+
+#
+# C/C++ and POSIX layer
+#
+
+#
+# ISO-ANSI C layer
+#
+
+#
+# Timezone and Daylight Saving Time
+#
+# CONFIG_RT_LIBC_USING_FULL_TZ_DST is not set
+CONFIG_RT_LIBC_USING_LIGHT_TZ_DST=y
+CONFIG_RT_LIBC_TZ_DEFAULT_HOUR=8
+CONFIG_RT_LIBC_TZ_DEFAULT_MIN=0
+CONFIG_RT_LIBC_TZ_DEFAULT_SEC=0
+# end of Timezone and Daylight Saving Time
+# end of ISO-ANSI C layer
+
+#
+# POSIX (Portable Operating System Interface) layer
+#
+CONFIG_RT_USING_POSIX_FS=y
+CONFIG_RT_USING_POSIX_DEVIO=y
+CONFIG_RT_USING_POSIX_STDIO=y
+CONFIG_RT_USING_POSIX_POLL=y
+CONFIG_RT_USING_POSIX_SELECT=y
+# CONFIG_RT_USING_POSIX_EVENTFD is not set
+# CONFIG_RT_USING_POSIX_TIMERFD is not set
+# CONFIG_RT_USING_POSIX_SOCKET is not set
+CONFIG_RT_USING_POSIX_TERMIOS=y
+CONFIG_RT_USING_POSIX_AIO=y
+CONFIG_RT_USING_POSIX_MMAN=y
+CONFIG_RT_USING_POSIX_DELAY=y
+CONFIG_RT_USING_POSIX_CLOCK=y
+CONFIG_RT_USING_POSIX_TIMER=y
+CONFIG_RT_USING_PTHREADS=y
+CONFIG_PTHREAD_NUM_MAX=8
+# CONFIG_RT_USING_MODULE is not set
+
+#
+# Interprocess Communication (IPC)
+#
+CONFIG_RT_USING_POSIX_PIPE=y
+CONFIG_RT_USING_POSIX_PIPE_SIZE=512
+# CONFIG_RT_USING_POSIX_MESSAGE_QUEUE is not set
+# CONFIG_RT_USING_POSIX_MESSAGE_SEMAPHORE is not set
+
+#
+# Socket is in the 'Network' category
+#
+# end of Interprocess Communication (IPC)
+# end of POSIX (Portable Operating System Interface) layer
+
+CONFIG_RT_USING_CPLUSPLUS=y
+CONFIG_RT_USING_CPLUSPLUS11=y
+CONFIG_RT_USING_CPP_WRAPPER=y
+# CONFIG_RT_USING_CPP_EXCEPTIONS is not set
+# end of C/C++ and POSIX layer
+
+#
+# Network
+#
+CONFIG_RT_USING_SAL=y
+CONFIG_SAL_INTERNET_CHECK=y
+CONFIG_SOCKET_TABLE_STEP_LEN=4
+
+#
+# Docking with protocol stacks
+#
+CONFIG_SAL_USING_LWIP=y
+# CONFIG_SAL_USING_AT is not set
+# CONFIG_SAL_USING_TLS is not set
+# end of Docking with protocol stacks
+
+CONFIG_SAL_USING_POSIX=y
+CONFIG_RT_USING_NETDEV=y
+CONFIG_NETDEV_USING_IFCONFIG=y
+CONFIG_NETDEV_USING_PING=y
+CONFIG_NETDEV_USING_NETSTAT=y
+CONFIG_NETDEV_USING_AUTO_DEFAULT=y
+# CONFIG_NETDEV_USING_LINK_STATUS_CALLBACK is not set
+# CONFIG_NETDEV_USING_IPV6 is not set
+CONFIG_NETDEV_IPV4=1
+CONFIG_NETDEV_IPV6=0
+CONFIG_RT_USING_LWIP=y
+# CONFIG_RT_USING_LWIP_LOCAL_VERSION is not set
+# CONFIG_RT_USING_LWIP141 is not set
+CONFIG_RT_USING_LWIP203=y
+# CONFIG_RT_USING_LWIP212 is not set
+# CONFIG_RT_USING_LWIP_LATEST is not set
+CONFIG_RT_USING_LWIP_VER_NUM=0x20003
+# CONFIG_RT_USING_LWIP_IPV6 is not set
+CONFIG_RT_LWIP_MEM_ALIGNMENT=4
+CONFIG_RT_LWIP_IGMP=y
+CONFIG_RT_LWIP_ICMP=y
+# CONFIG_RT_LWIP_SNMP is not set
+CONFIG_RT_LWIP_DNS=y
+CONFIG_RT_LWIP_DHCP=y
+CONFIG_IP_SOF_BROADCAST=1
+CONFIG_IP_SOF_BROADCAST_RECV=1
+
+#
+# Static IPv4 Address
+#
+CONFIG_RT_LWIP_IPADDR="192.168.1.30"
+CONFIG_RT_LWIP_GWADDR="192.168.1.1"
+CONFIG_RT_LWIP_MSKADDR="255.255.255.0"
+# end of Static IPv4 Address
+
+CONFIG_RT_LWIP_UDP=y
+CONFIG_RT_LWIP_TCP=y
+CONFIG_RT_LWIP_RAW=y
+# CONFIG_RT_LWIP_PPP is not set
+CONFIG_RT_MEMP_NUM_NETCONN=8
+CONFIG_RT_LWIP_PBUF_NUM=16
+CONFIG_RT_LWIP_RAW_PCB_NUM=4
+CONFIG_RT_LWIP_UDP_PCB_NUM=4
+CONFIG_RT_LWIP_TCP_PCB_NUM=4
+CONFIG_RT_LWIP_TCP_SEG_NUM=40
+CONFIG_RT_LWIP_TCP_SND_BUF=8196
+CONFIG_RT_LWIP_TCP_WND=8196
+CONFIG_RT_LWIP_TCPTHREAD_PRIORITY=10
+CONFIG_RT_LWIP_TCPTHREAD_MBOX_SIZE=8
+CONFIG_RT_LWIP_TCPTHREAD_STACKSIZE=8192
+# CONFIG_LWIP_NO_RX_THREAD is not set
+# CONFIG_LWIP_NO_TX_THREAD is not set
+CONFIG_RT_LWIP_ETHTHREAD_PRIORITY=12
+CONFIG_RT_LWIP_ETHTHREAD_STACKSIZE=8192
+CONFIG_RT_LWIP_ETHTHREAD_MBOX_SIZE=8
+# CONFIG_RT_LWIP_REASSEMBLY_FRAG is not set
+CONFIG_LWIP_NETIF_STATUS_CALLBACK=1
+CONFIG_LWIP_NETIF_LINK_CALLBACK=1
+CONFIG_RT_LWIP_NETIF_NAMESIZE=6
+CONFIG_SO_REUSE=1
+CONFIG_LWIP_SO_RCVTIMEO=1
+CONFIG_LWIP_SO_SNDTIMEO=1
+CONFIG_LWIP_SO_RCVBUF=1
+CONFIG_LWIP_SO_LINGER=0
+# CONFIG_RT_LWIP_NETIF_LOOPBACK is not set
+CONFIG_LWIP_NETIF_LOOPBACK=0
+# CONFIG_RT_LWIP_STATS is not set
+# CONFIG_RT_LWIP_USING_HW_CHECKSUM is not set
+CONFIG_RT_LWIP_USING_PING=y
+# CONFIG_LWIP_USING_DHCPD is not set
+# CONFIG_RT_LWIP_ENABLE_USER_HOOKS is not set
+# CONFIG_RT_LWIP_DEBUG is not set
+# CONFIG_RT_USING_AT is not set
+# end of Network
+
+#
+# Memory protection
+#
+# CONFIG_RT_USING_MEM_PROTECTION is not set
+# CONFIG_RT_USING_HW_STACK_GUARD is not set
+# end of Memory protection
+
+#
+# Utilities
+#
+# CONFIG_RT_USING_RYM is not set
+# CONFIG_RT_USING_ULOG is not set
+CONFIG_RT_USING_UTEST=y
+CONFIG_UTEST_THR_STACK_SIZE=4096
+CONFIG_UTEST_THR_PRIORITY=20
+# CONFIG_RT_UTEST_USING_AUTO_RUN is not set
+CONFIG_RT_UTEST_MAX_OPTIONS=64
+# CONFIG_RT_USING_VAR_EXPORT is not set
+CONFIG_RT_USING_RESOURCE_ID=y
+CONFIG_RT_USING_ADT=y
+CONFIG_RT_USING_ADT_AVL=y
+CONFIG_RT_USING_ADT_BITMAP=y
+CONFIG_RT_USING_ADT_HASHMAP=y
+CONFIG_RT_USING_ADT_REF=y
+# CONFIG_RT_USING_RT_LINK is not set
+# end of Utilities
+
+#
+# Memory management
+#
+# CONFIG_RT_PAGE_MPR_SIZE_DYNAMIC is not set
+CONFIG_RT_PAGE_AFFINITY_BLOCK_SIZE=0x1000
+CONFIG_RT_PAGE_MAX_ORDER=11
+# CONFIG_RT_USING_MEMBLOCK is not set
+
+#
+# Debugging
+#
+# CONFIG_RT_DEBUGGING_ALIASING is not set
+# CONFIG_RT_DEBUGING_PAGE_LEAK is not set
+# CONFIG_RT_DEBUGGING_PAGE_POISON is not set
+# end of Debugging
+# end of Memory management
+
+#
+# Using USB legacy version
+#
+# CONFIG_RT_USING_USB_HOST is not set
+# CONFIG_RT_USING_USB_DEVICE is not set
+# end of Using USB legacy version
+
+# CONFIG_RT_USING_FDT is not set
+# CONFIG_RT_USING_RUST is not set
+# end of RT-Thread Components
+
+#
+# RT-Thread Utestcases
+#
+# CONFIG_RT_USING_UTESTCASES is not set
+# end of RT-Thread Utestcases
+
+#
+# RT-Thread online packages
+#
+
+#
+# IoT - internet of things
+#
+# CONFIG_PKG_USING_LORAWAN_DRIVER is not set
+# CONFIG_PKG_USING_PAHOMQTT is not set
+# CONFIG_PKG_USING_UMQTT is not set
+# CONFIG_PKG_USING_WEBCLIENT is not set
+# CONFIG_PKG_USING_WEBNET is not set
+# CONFIG_PKG_USING_MONGOOSE is not set
+# CONFIG_PKG_USING_MYMQTT is not set
+# CONFIG_PKG_USING_KAWAII_MQTT is not set
+# CONFIG_PKG_USING_BC28_MQTT is not set
+# CONFIG_PKG_USING_WEBTERMINAL is not set
+# CONFIG_PKG_USING_FREEMODBUS is not set
+# CONFIG_PKG_USING_NANOPB is not set
+# CONFIG_PKG_USING_WIFI_HOST_DRIVER is not set
+# CONFIG_PKG_USING_ESP_HOSTED is not set
+
+#
+# Wi-Fi
+#
+
+#
+# Marvell WiFi
+#
+# CONFIG_PKG_USING_WLANMARVELL is not set
+# end of Marvell WiFi
+
+#
+# Wiced WiFi
+#
+# CONFIG_PKG_USING_WLAN_WICED is not set
+# end of Wiced WiFi
+
+# CONFIG_PKG_USING_RW007 is not set
+
+#
+# CYW43012 WiFi
+#
+# CONFIG_PKG_USING_WLAN_CYW43012 is not set
+# end of CYW43012 WiFi
+
+#
+# BL808 WiFi
+#
+# CONFIG_PKG_USING_WLAN_BL808 is not set
+# end of BL808 WiFi
+
+#
+# CYW43439 WiFi
+#
+# CONFIG_PKG_USING_WLAN_CYW43439 is not set
+# end of CYW43439 WiFi
+# end of Wi-Fi
+
+# CONFIG_PKG_USING_COAP is not set
+# CONFIG_PKG_USING_NOPOLL is not set
+# CONFIG_PKG_USING_NETUTILS is not set
+# CONFIG_PKG_USING_CMUX is not set
+# CONFIG_PKG_USING_PPP_DEVICE is not set
+# CONFIG_PKG_USING_AT_DEVICE is not set
+# CONFIG_PKG_USING_ATSRV_SOCKET is not set
+# CONFIG_PKG_USING_WIZNET is not set
+# CONFIG_PKG_USING_ZB_COORDINATOR is not set
+
+#
+# IoT Cloud
+#
+# CONFIG_PKG_USING_ONENET is not set
+# CONFIG_PKG_USING_GAGENT_CLOUD is not set
+# CONFIG_PKG_USING_ALI_IOTKIT is not set
+# CONFIG_PKG_USING_AZURE is not set
+# CONFIG_PKG_USING_TENCENT_IOT_EXPLORER is not set
+# CONFIG_PKG_USING_JIOT-C-SDK is not set
+# CONFIG_PKG_USING_UCLOUD_IOT_SDK is not set
+# CONFIG_PKG_USING_JOYLINK is not set
+# CONFIG_PKG_USING_IOTSHARP_SDK is not set
+# end of IoT Cloud
+
+# CONFIG_PKG_USING_NIMBLE is not set
+# CONFIG_PKG_USING_LLSYNC_SDK_ADAPTER is not set
+# CONFIG_PKG_USING_OTA_DOWNLOADER is not set
+# CONFIG_PKG_USING_IPMSG is not set
+# CONFIG_PKG_USING_LSSDP is not set
+# CONFIG_PKG_USING_AIRKISS_OPEN is not set
+# CONFIG_PKG_USING_LIBRWS is not set
+# CONFIG_PKG_USING_TCPSERVER is not set
+# CONFIG_PKG_USING_PROTOBUF_C is not set
+# CONFIG_PKG_USING_DLT645 is not set
+# CONFIG_PKG_USING_QXWZ is not set
+# CONFIG_PKG_USING_SMTP_CLIENT is not set
+# CONFIG_PKG_USING_ABUP_FOTA is not set
+# CONFIG_PKG_USING_LIBCURL2RTT is not set
+# CONFIG_PKG_USING_CAPNP is not set
+# CONFIG_PKG_USING_AGILE_TELNET is not set
+# CONFIG_PKG_USING_NMEALIB is not set
+# CONFIG_PKG_USING_PDULIB is not set
+# CONFIG_PKG_USING_BTSTACK is not set
+# CONFIG_PKG_USING_BT_CYW43012 is not set
+# CONFIG_PKG_USING_CYW43XX is not set
+# CONFIG_PKG_USING_LORAWAN_ED_STACK is not set
+# CONFIG_PKG_USING_WAYZ_IOTKIT is not set
+# CONFIG_PKG_USING_MAVLINK is not set
+# CONFIG_PKG_USING_BSAL is not set
+# CONFIG_PKG_USING_AGILE_MODBUS is not set
+# CONFIG_PKG_USING_AGILE_FTP is not set
+# CONFIG_PKG_USING_EMBEDDEDPROTO is not set
+# CONFIG_PKG_USING_RT_LINK_HW is not set
+# CONFIG_PKG_USING_RYANMQTT is not set
+# CONFIG_PKG_USING_RYANW5500 is not set
+# CONFIG_PKG_USING_LORA_PKT_FWD is not set
+# CONFIG_PKG_USING_LORA_GW_DRIVER_LIB is not set
+# CONFIG_PKG_USING_LORA_PKT_SNIFFER is not set
+# CONFIG_PKG_USING_HM is not set
+# CONFIG_PKG_USING_SMALL_MODBUS is not set
+# CONFIG_PKG_USING_NET_SERVER is not set
+# CONFIG_PKG_USING_ZFTP is not set
+# CONFIG_PKG_USING_WOL is not set
+# CONFIG_PKG_USING_ZEPHYR_POLLING is not set
+# CONFIG_PKG_USING_MATTER_ADAPTATION_LAYER is not set
+# CONFIG_PKG_USING_LHC_MODBUS is not set
+# CONFIG_PKG_USING_QMODBUS is not set
+# CONFIG_PKG_USING_PNET is not set
+# CONFIG_PKG_USING_OPENER is not set
+# CONFIG_PKG_USING_FREEMQTT is not set
+# end of IoT - internet of things
+
+#
+# security packages
+#
+# CONFIG_PKG_USING_MBEDTLS is not set
+# CONFIG_PKG_USING_LIBSODIUM is not set
+# CONFIG_PKG_USING_LIBHYDROGEN is not set
+# CONFIG_PKG_USING_TINYCRYPT is not set
+# CONFIG_PKG_USING_TFM is not set
+# CONFIG_PKG_USING_YD_CRYPTO is not set
+# end of security packages
+
+#
+# language packages
+#
+
+#
+# JSON: JavaScript Object Notation, a lightweight data-interchange format
+#
+# CONFIG_PKG_USING_CJSON is not set
+# CONFIG_PKG_USING_LJSON is not set
+# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set
+# CONFIG_PKG_USING_RAPIDJSON is not set
+# CONFIG_PKG_USING_JSMN is not set
+# CONFIG_PKG_USING_AGILE_JSMN is not set
+# CONFIG_PKG_USING_PARSON is not set
+# CONFIG_PKG_USING_RYAN_JSON is not set
+# end of JSON: JavaScript Object Notation, a lightweight data-interchange format
+
+#
+# XML: Extensible Markup Language
+#
+# CONFIG_PKG_USING_SIMPLE_XML is not set
+# CONFIG_PKG_USING_EZXML is not set
+# end of XML: Extensible Markup Language
+
+# CONFIG_PKG_USING_LUATOS_SOC is not set
+# CONFIG_PKG_USING_LUA is not set
+# CONFIG_PKG_USING_JERRYSCRIPT is not set
+# CONFIG_PKG_USING_MICROPYTHON is not set
+# CONFIG_PKG_USING_PIKASCRIPT is not set
+# CONFIG_PKG_USING_RTT_RUST is not set
+# end of language packages
+
+#
+# multimedia packages
+#
+
+#
+# LVGL: powerful and easy-to-use embedded GUI library
+#
+# CONFIG_PKG_USING_LVGL is not set
+# CONFIG_PKG_USING_LV_MUSIC_DEMO is not set
+# CONFIG_PKG_USING_GUI_GUIDER_DEMO is not set
+# end of LVGL: powerful and easy-to-use embedded GUI library
+
+#
+# u8g2: a monochrome graphic library
+#
+# CONFIG_PKG_USING_U8G2_OFFICIAL is not set
+# CONFIG_PKG_USING_U8G2 is not set
+# end of u8g2: a monochrome graphic library
+
+# CONFIG_PKG_USING_OPENMV is not set
+# CONFIG_PKG_USING_MUPDF is not set
+# CONFIG_PKG_USING_STEMWIN is not set
+# CONFIG_PKG_USING_WAVPLAYER is not set
+# CONFIG_PKG_USING_TJPGD is not set
+# CONFIG_PKG_USING_PDFGEN is not set
+# CONFIG_PKG_USING_HELIX is not set
+# CONFIG_PKG_USING_AZUREGUIX is not set
+# CONFIG_PKG_USING_TOUCHGFX2RTT is not set
+# CONFIG_PKG_USING_NUEMWIN is not set
+# CONFIG_PKG_USING_MP3PLAYER is not set
+# CONFIG_PKG_USING_TINYJPEG is not set
+# CONFIG_PKG_USING_UGUI is not set
+# CONFIG_PKG_USING_MCURSES is not set
+# CONFIG_PKG_USING_TERMBOX is not set
+# CONFIG_PKG_USING_VT100 is not set
+# CONFIG_PKG_USING_QRCODE is not set
+# CONFIG_PKG_USING_GUIENGINE is not set
+# CONFIG_PKG_USING_3GPP_AMRNB is not set
+# end of multimedia packages
+
+#
+# tools packages
+#
+# CONFIG_PKG_USING_CMBACKTRACE is not set
+# CONFIG_PKG_USING_MCOREDUMP is not set
+# CONFIG_PKG_USING_EASYFLASH is not set
+# CONFIG_PKG_USING_EASYLOGGER is not set
+# CONFIG_PKG_USING_SYSTEMVIEW is not set
+# CONFIG_PKG_USING_SEGGER_RTT is not set
+# CONFIG_PKG_USING_RTT_AUTO_EXE_CMD is not set
+# CONFIG_PKG_USING_RDB is not set
+# CONFIG_PKG_USING_ULOG_EASYFLASH is not set
+# CONFIG_PKG_USING_LOGMGR is not set
+# CONFIG_PKG_USING_ADBD is not set
+# CONFIG_PKG_USING_COREMARK is not set
+# CONFIG_PKG_USING_DHRYSTONE is not set
+# CONFIG_PKG_USING_MEMORYPERF is not set
+# CONFIG_PKG_USING_NR_MICRO_SHELL is not set
+# CONFIG_PKG_USING_CHINESE_FONT_LIBRARY is not set
+# CONFIG_PKG_USING_LUNAR_CALENDAR is not set
+# CONFIG_PKG_USING_BS8116A is not set
+# CONFIG_PKG_USING_GPS_RMC is not set
+# CONFIG_PKG_USING_URLENCODE is not set
+# CONFIG_PKG_USING_UMCN is not set
+# CONFIG_PKG_USING_LWRB2RTT is not set
+# CONFIG_PKG_USING_CPU_USAGE is not set
+# CONFIG_PKG_USING_GBK2UTF8 is not set
+# CONFIG_PKG_USING_VCONSOLE is not set
+# CONFIG_PKG_USING_KDB is not set
+# CONFIG_PKG_USING_WAMR is not set
+# CONFIG_PKG_USING_MICRO_XRCE_DDS_CLIENT is not set
+# CONFIG_PKG_USING_LWLOG is not set
+# CONFIG_PKG_USING_ANV_TRACE is not set
+# CONFIG_PKG_USING_ANV_MEMLEAK is not set
+# CONFIG_PKG_USING_ANV_TESTSUIT is not set
+# CONFIG_PKG_USING_ANV_BENCH is not set
+# CONFIG_PKG_USING_DEVMEM is not set
+# CONFIG_PKG_USING_REGEX is not set
+# CONFIG_PKG_USING_MEM_SANDBOX is not set
+# CONFIG_PKG_USING_SOLAR_TERMS is not set
+# CONFIG_PKG_USING_GAN_ZHI is not set
+# CONFIG_PKG_USING_FDT is not set
+# CONFIG_PKG_USING_CBOX is not set
+# CONFIG_PKG_USING_SNOWFLAKE is not set
+# CONFIG_PKG_USING_HASH_MATCH is not set
+# CONFIG_PKG_USING_ARMV7M_DWT_TOOL is not set
+# CONFIG_PKG_USING_VOFA_PLUS is not set
+# CONFIG_PKG_USING_ZDEBUG is not set
+# CONFIG_PKG_USING_RVBACKTRACE is not set
+# CONFIG_PKG_USING_HPATCHLITE is not set
+# CONFIG_PKG_USING_THREAD_METRIC is not set
+# end of tools packages
+
+#
+# system packages
+#
+
+#
+# enhanced kernel services
+#
+# CONFIG_PKG_USING_RT_MEMCPY_CM is not set
+# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set
+# end of enhanced kernel services
+
+# CONFIG_PKG_USING_AUNITY is not set
+
+#
+# acceleration: Assembly language or algorithmic acceleration packages
+#
+# CONFIG_PKG_USING_QFPLIB_M0_FULL is not set
+# CONFIG_PKG_USING_QFPLIB_M0_TINY is not set
+# CONFIG_PKG_USING_QFPLIB_M3 is not set
+# end of acceleration: Assembly language or algorithmic acceleration packages
+
+#
+# CMSIS: ARM Cortex-M Microcontroller Software Interface Standard
+#
+# CONFIG_PKG_USING_CMSIS_5 is not set
+# CONFIG_PKG_USING_CMSIS_CORE is not set
+# CONFIG_PKG_USING_CMSIS_NN is not set
+# CONFIG_PKG_USING_CMSIS_RTOS1 is not set
+# CONFIG_PKG_USING_CMSIS_RTOS2 is not set
+# end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard
+
+#
+# Micrium: Micrium software products porting for RT-Thread
+#
+# CONFIG_PKG_USING_UCOSIII_WRAPPER is not set
+# CONFIG_PKG_USING_UCOSII_WRAPPER is not set
+# CONFIG_PKG_USING_UC_CRC is not set
+# CONFIG_PKG_USING_UC_CLK is not set
+# CONFIG_PKG_USING_UC_COMMON is not set
+# CONFIG_PKG_USING_UC_MODBUS is not set
+# end of Micrium: Micrium software products porting for RT-Thread
+
+# CONFIG_PKG_USING_FREERTOS_WRAPPER is not set
+# CONFIG_PKG_USING_LITEOS_SDK is not set
+# CONFIG_PKG_USING_TZ_DATABASE is not set
+# CONFIG_PKG_USING_CAIRO is not set
+# CONFIG_PKG_USING_PIXMAN is not set
+# CONFIG_PKG_USING_PARTITION is not set
+# CONFIG_PKG_USING_PERF_COUNTER is not set
+# CONFIG_PKG_USING_FILEX is not set
+# CONFIG_PKG_USING_LEVELX is not set
+# CONFIG_PKG_USING_FLASHDB is not set
+# CONFIG_PKG_USING_SQLITE is not set
+# CONFIG_PKG_USING_RTI is not set
+# CONFIG_PKG_USING_DFS_YAFFS is not set
+# CONFIG_PKG_USING_LITTLEFS is not set
+# CONFIG_PKG_USING_DFS_JFFS2 is not set
+# CONFIG_PKG_USING_DFS_UFFS is not set
+# CONFIG_PKG_USING_LWEXT4 is not set
+# CONFIG_PKG_USING_THREAD_POOL is not set
+# CONFIG_PKG_USING_ROBOTS is not set
+# CONFIG_PKG_USING_EV is not set
+# CONFIG_PKG_USING_SYSWATCH is not set
+# CONFIG_PKG_USING_SYS_LOAD_MONITOR is not set
+# CONFIG_PKG_USING_PLCCORE is not set
+# CONFIG_PKG_USING_RAMDISK is not set
+# CONFIG_PKG_USING_MININI is not set
+# CONFIG_PKG_USING_QBOOT is not set
+# CONFIG_PKG_USING_PPOOL is not set
+# CONFIG_PKG_USING_OPENAMP is not set
+# CONFIG_PKG_USING_RPMSG_LITE is not set
+# CONFIG_PKG_USING_LPM is not set
+# CONFIG_PKG_USING_TLSF is not set
+# CONFIG_PKG_USING_EVENT_RECORDER is not set
+# CONFIG_PKG_USING_ARM_2D is not set
+# CONFIG_PKG_USING_MCUBOOT is not set
+# CONFIG_PKG_USING_TINYUSB is not set
+# CONFIG_PKG_USING_KMULTI_RTIMER is not set
+# CONFIG_PKG_USING_TFDB is not set
+# CONFIG_PKG_USING_QPC is not set
+# CONFIG_PKG_USING_AGILE_UPGRADE is not set
+# CONFIG_PKG_USING_FLASH_BLOB is not set
+# CONFIG_PKG_USING_MLIBC is not set
+# CONFIG_PKG_USING_TASK_MSG_BUS is not set
+# CONFIG_PKG_USING_UART_FRAMEWORK is not set
+# CONFIG_PKG_USING_SFDB is not set
+# CONFIG_PKG_USING_RTP is not set
+# CONFIG_PKG_USING_REB is not set
+# CONFIG_PKG_USING_RMP is not set
+# CONFIG_PKG_USING_R_RHEALSTONE is not set
+# CONFIG_PKG_USING_HEARTBEAT is not set
+# CONFIG_PKG_USING_MICRO_ROS_RTTHREAD_PACKAGE is not set
+# end of system packages
+
+#
+# peripheral libraries and drivers
+#
+
+#
+# HAL & SDK Drivers
+#
+
+#
+# STM32 HAL & SDK Drivers
+#
+# CONFIG_PKG_USING_STM32F0_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32F0_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32F1_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32F1_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32F2_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32F2_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32F3_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32F3_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32F4_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32F4_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32F7_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32F7_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32G0_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32G0_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32G4_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32G4_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32H5_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32H5_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32H7_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32H7_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32H7RS_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32H7RS_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32L0_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32L0_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32L4_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32L4_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32L5_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32L5_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32U5_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32U5_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32WB55_SDK is not set
+# CONFIG_PKG_USING_STM32_SDIO is not set
+# CONFIG_PKG_USING_STM32WL_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32WL_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32WB_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32WB_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_STM32MP1_M4_HAL_DRIVER is not set
+# CONFIG_PKG_USING_STM32MP1_M4_CMSIS_DRIVER is not set
+# end of STM32 HAL & SDK Drivers
+
+#
+# Infineon HAL Packages
+#
+# CONFIG_PKG_USING_INFINEON_CAT1CM0P is not set
+# CONFIG_PKG_USING_INFINEON_CMSIS is not set
+# CONFIG_PKG_USING_INFINEON_CORE_LIB is not set
+# CONFIG_PKG_USING_INFINEON_MTB_HAL_CAT1 is not set
+# CONFIG_PKG_USING_INFINEON_MTB_PDL_CAT1 is not set
+# CONFIG_PKG_USING_INFINEON_RETARGET_IO is not set
+# CONFIG_PKG_USING_INFINEON_CAPSENSE is not set
+# CONFIG_PKG_USING_INFINEON_CSDIDAC is not set
+# CONFIG_PKG_USING_INFINEON_SERIAL_FLASH is not set
+# CONFIG_PKG_USING_INFINEON_USBDEV is not set
+# end of Infineon HAL Packages
+
+# CONFIG_PKG_USING_BLUETRUM_SDK is not set
+# CONFIG_PKG_USING_EMBARC_BSP is not set
+# CONFIG_PKG_USING_ESP_IDF is not set
+
+#
+# Kendryte SDK
+#
+# CONFIG_PKG_USING_K210_SDK is not set
+# CONFIG_PKG_USING_KENDRYTE_SDK is not set
+# end of Kendryte SDK
+
+# CONFIG_PKG_USING_NRF5X_SDK is not set
+# CONFIG_PKG_USING_NRFX is not set
+# CONFIG_PKG_USING_NUCLEI_SDK is not set
+# CONFIG_PKG_USING_RASPBERRYPI_PICO_RP2350_SDK is not set
+# CONFIG_PKG_USING_RASPBERRYPI_PICO_SDK is not set
+# CONFIG_PKG_USING_MM32 is not set
+
+#
+# WCH HAL & SDK Drivers
+#
+# CONFIG_PKG_USING_CH32V20x_SDK is not set
+# CONFIG_PKG_USING_CH32V307_SDK is not set
+# end of WCH HAL & SDK Drivers
+
+#
+# AT32 HAL & SDK Drivers
+#
+# CONFIG_PKG_USING_AT32A403A_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32A403A_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_AT32A423_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32A423_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_AT32F45x_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32F45x_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_AT32F402_405_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32F402_405_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_AT32F403A_407_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32F403A_407_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_AT32F413_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32F413_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_AT32F415_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32F415_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_AT32F421_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32F421_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_AT32F423_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32F423_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_AT32F425_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32F425_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_AT32F435_437_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32F435_437_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_AT32M412_416_HAL_DRIVER is not set
+# CONFIG_PKG_USING_AT32M412_416_CMSIS_DRIVER is not set
+# end of AT32 HAL & SDK Drivers
+
+#
+# HC32 DDL Drivers
+#
+# CONFIG_PKG_USING_HC32F3_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_HC32F3_SERIES_DRIVER is not set
+# CONFIG_PKG_USING_HC32F4_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_HC32F4_SERIES_DRIVER is not set
+# end of HC32 DDL Drivers
+
+#
+# NXP HAL & SDK Drivers
+#
+# CONFIG_PKG_USING_NXP_MCX_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_NXP_MCX_SERIES_DRIVER is not set
+# CONFIG_PKG_USING_NXP_LPC_DRIVER is not set
+# CONFIG_PKG_USING_NXP_LPC55S_DRIVER is not set
+# CONFIG_PKG_USING_NXP_IMX6SX_DRIVER is not set
+# CONFIG_PKG_USING_NXP_IMX6UL_DRIVER is not set
+# CONFIG_PKG_USING_NXP_IMXRT_DRIVER is not set
+# end of NXP HAL & SDK Drivers
+
+#
+# NUVOTON Drivers
+#
+# CONFIG_PKG_USING_NUVOTON_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_NUVOTON_SERIES_DRIVER is not set
+# CONFIG_PKG_USING_NUVOTON_ARM926_LIB is not set
+# end of NUVOTON Drivers
+
+#
+# GD32 Drivers
+#
+# CONFIG_PKG_USING_GD32_ARM_CMSIS_DRIVER is not set
+# CONFIG_PKG_USING_GD32_ARM_SERIES_DRIVER is not set
+# end of GD32 Drivers
+# end of HAL & SDK Drivers
+
+#
+# sensors drivers
+#
+# CONFIG_PKG_USING_LSM6DSM is not set
+# CONFIG_PKG_USING_LSM6DSL is not set
+# CONFIG_PKG_USING_LPS22HB is not set
+# CONFIG_PKG_USING_HTS221 is not set
+# CONFIG_PKG_USING_LSM303AGR is not set
+# CONFIG_PKG_USING_BME280 is not set
+# CONFIG_PKG_USING_BME680 is not set
+# CONFIG_PKG_USING_BMA400 is not set
+# CONFIG_PKG_USING_BMI160_BMX160 is not set
+# CONFIG_PKG_USING_SPL0601 is not set
+# CONFIG_PKG_USING_MS5805 is not set
+# CONFIG_PKG_USING_DA270 is not set
+# CONFIG_PKG_USING_DF220 is not set
+# CONFIG_PKG_USING_HSHCAL001 is not set
+# CONFIG_PKG_USING_BH1750 is not set
+# CONFIG_PKG_USING_MPU6XXX is not set
+# CONFIG_PKG_USING_AHT10 is not set
+# CONFIG_PKG_USING_AP3216C is not set
+# CONFIG_PKG_USING_TSL4531 is not set
+# CONFIG_PKG_USING_DS18B20 is not set
+# CONFIG_PKG_USING_DHT11 is not set
+# CONFIG_PKG_USING_DHTXX is not set
+# CONFIG_PKG_USING_GY271 is not set
+# CONFIG_PKG_USING_GP2Y10 is not set
+# CONFIG_PKG_USING_SGP30 is not set
+# CONFIG_PKG_USING_HDC1000 is not set
+# CONFIG_PKG_USING_BMP180 is not set
+# CONFIG_PKG_USING_BMP280 is not set
+# CONFIG_PKG_USING_SHTC1 is not set
+# CONFIG_PKG_USING_BMI088 is not set
+# CONFIG_PKG_USING_HMC5883 is not set
+# CONFIG_PKG_USING_MAX6675 is not set
+# CONFIG_PKG_USING_MAX31855 is not set
+# CONFIG_PKG_USING_TMP1075 is not set
+# CONFIG_PKG_USING_SR04 is not set
+# CONFIG_PKG_USING_CCS811 is not set
+# CONFIG_PKG_USING_PMSXX is not set
+# CONFIG_PKG_USING_RT3020 is not set
+# CONFIG_PKG_USING_MLX90632 is not set
+# CONFIG_PKG_USING_MLX90382 is not set
+# CONFIG_PKG_USING_MLX90393 is not set
+# CONFIG_PKG_USING_MLX90392 is not set
+# CONFIG_PKG_USING_MLX90394 is not set
+# CONFIG_PKG_USING_MLX90397 is not set
+# CONFIG_PKG_USING_MS5611 is not set
+# CONFIG_PKG_USING_MAX31865 is not set
+# CONFIG_PKG_USING_VL53L0X is not set
+# CONFIG_PKG_USING_INA260 is not set
+# CONFIG_PKG_USING_MAX30102 is not set
+# CONFIG_PKG_USING_INA226 is not set
+# CONFIG_PKG_USING_LIS2DH12 is not set
+# CONFIG_PKG_USING_HS300X is not set
+# CONFIG_PKG_USING_ZMOD4410 is not set
+# CONFIG_PKG_USING_ISL29035 is not set
+# CONFIG_PKG_USING_MMC3680KJ is not set
+# CONFIG_PKG_USING_QMP6989 is not set
+# CONFIG_PKG_USING_BALANCE is not set
+# CONFIG_PKG_USING_SHT2X is not set
+# CONFIG_PKG_USING_SHT3X is not set
+# CONFIG_PKG_USING_SHT4X is not set
+# CONFIG_PKG_USING_AD7746 is not set
+# CONFIG_PKG_USING_ADT74XX is not set
+# CONFIG_PKG_USING_MAX17048 is not set
+# CONFIG_PKG_USING_AS7341 is not set
+# CONFIG_PKG_USING_CW2015 is not set
+# CONFIG_PKG_USING_ICM20608 is not set
+# CONFIG_PKG_USING_PAJ7620 is not set
+# CONFIG_PKG_USING_STHS34PF80 is not set
+# CONFIG_PKG_USING_P3T1755 is not set
+# CONFIG_PKG_USING_QMI8658 is not set
+# CONFIG_PKG_USING_ICM20948 is not set
+# end of sensors drivers
+
+#
+# touch drivers
+#
+# CONFIG_PKG_USING_GT9147 is not set
+# CONFIG_PKG_USING_GT1151 is not set
+# CONFIG_PKG_USING_GT917S is not set
+# CONFIG_PKG_USING_GT911 is not set
+# CONFIG_PKG_USING_FT6206 is not set
+# CONFIG_PKG_USING_FT5426 is not set
+# CONFIG_PKG_USING_FT6236 is not set
+# CONFIG_PKG_USING_XPT2046_TOUCH is not set
+# CONFIG_PKG_USING_CST816X is not set
+# CONFIG_PKG_USING_CST812T is not set
+# end of touch drivers
+
+# CONFIG_PKG_USING_REALTEK_AMEBA is not set
+# CONFIG_PKG_USING_BUTTON is not set
+# CONFIG_PKG_USING_PCF8574 is not set
+# CONFIG_PKG_USING_SX12XX is not set
+# CONFIG_PKG_USING_SIGNAL_LED is not set
+# CONFIG_PKG_USING_LEDBLINK is not set
+# CONFIG_PKG_USING_LITTLED is not set
+# CONFIG_PKG_USING_LKDGUI is not set
+# CONFIG_PKG_USING_INFRARED is not set
+# CONFIG_PKG_USING_MULTI_INFRARED is not set
+# CONFIG_PKG_USING_AGILE_BUTTON is not set
+# CONFIG_PKG_USING_AGILE_LED is not set
+# CONFIG_PKG_USING_AT24CXX is not set
+# CONFIG_PKG_USING_MOTIONDRIVER2RTT is not set
+# CONFIG_PKG_USING_PCA9685 is not set
+# CONFIG_PKG_USING_ILI9341 is not set
+# CONFIG_PKG_USING_I2C_TOOLS is not set
+# CONFIG_PKG_USING_NRF24L01 is not set
+# CONFIG_PKG_USING_RPLIDAR is not set
+# CONFIG_PKG_USING_AS608 is not set
+# CONFIG_PKG_USING_RC522 is not set
+# CONFIG_PKG_USING_WS2812B is not set
+# CONFIG_PKG_USING_EXTERN_RTC_DRIVERS is not set
+# CONFIG_PKG_USING_MULTI_RTIMER is not set
+# CONFIG_PKG_USING_MAX7219 is not set
+# CONFIG_PKG_USING_BEEP is not set
+# CONFIG_PKG_USING_EASYBLINK is not set
+# CONFIG_PKG_USING_PMS_SERIES is not set
+# CONFIG_PKG_USING_CAN_YMODEM is not set
+# CONFIG_PKG_USING_LORA_RADIO_DRIVER is not set
+# CONFIG_PKG_USING_QLED is not set
+# CONFIG_PKG_USING_AGILE_CONSOLE is not set
+# CONFIG_PKG_USING_LD3320 is not set
+# CONFIG_PKG_USING_WK2124 is not set
+# CONFIG_PKG_USING_LY68L6400 is not set
+# CONFIG_PKG_USING_DM9051 is not set
+# CONFIG_PKG_USING_SSD1306 is not set
+# CONFIG_PKG_USING_QKEY is not set
+# CONFIG_PKG_USING_RS485 is not set
+# CONFIG_PKG_USING_RS232 is not set
+# CONFIG_PKG_USING_NES is not set
+# CONFIG_PKG_USING_VIRTUAL_SENSOR is not set
+# CONFIG_PKG_USING_VDEVICE is not set
+# CONFIG_PKG_USING_SGM706 is not set
+# CONFIG_PKG_USING_RDA58XX is not set
+# CONFIG_PKG_USING_LIBNFC is not set
+# CONFIG_PKG_USING_MFOC is not set
+# CONFIG_PKG_USING_TMC51XX is not set
+# CONFIG_PKG_USING_TCA9534 is not set
+# CONFIG_PKG_USING_KOBUKI is not set
+# CONFIG_PKG_USING_ROSSERIAL is not set
+# CONFIG_PKG_USING_MICRO_ROS is not set
+# CONFIG_PKG_USING_MCP23008 is not set
+# CONFIG_PKG_USING_MISAKA_AT24CXX is not set
+# CONFIG_PKG_USING_MISAKA_RGB_BLING is not set
+# CONFIG_PKG_USING_LORA_MODEM_DRIVER is not set
+# CONFIG_PKG_USING_SOFT_SERIAL is not set
+# CONFIG_PKG_USING_MB85RS16 is not set
+# CONFIG_PKG_USING_RFM300 is not set
+# CONFIG_PKG_USING_IO_INPUT_FILTER is not set
+# CONFIG_PKG_USING_LRF_NV7LIDAR is not set
+# CONFIG_PKG_USING_AIP650 is not set
+# CONFIG_PKG_USING_FINGERPRINT is not set
+# CONFIG_PKG_USING_BT_ECB02C is not set
+# CONFIG_PKG_USING_UAT is not set
+# CONFIG_PKG_USING_ST7789 is not set
+# CONFIG_PKG_USING_VS1003 is not set
+# CONFIG_PKG_USING_X9555 is not set
+# CONFIG_PKG_USING_SYSTEM_RUN_LED is not set
+# CONFIG_PKG_USING_BT_MX01 is not set
+# CONFIG_PKG_USING_RGPOWER is not set
+# CONFIG_PKG_USING_BT_MX02 is not set
+# CONFIG_PKG_USING_GC9A01 is not set
+# CONFIG_PKG_USING_IK485 is not set
+# CONFIG_PKG_USING_SERVO is not set
+# CONFIG_PKG_USING_SEAN_WS2812B is not set
+# CONFIG_PKG_USING_IC74HC165 is not set
+# CONFIG_PKG_USING_IST8310 is not set
+# CONFIG_PKG_USING_ST7789_SPI is not set
+# CONFIG_PKG_USING_SPI_TOOLS is not set
+# end of peripheral libraries and drivers
+
+#
+# AI packages
+#
+# CONFIG_PKG_USING_LIBANN is not set
+# CONFIG_PKG_USING_NNOM is not set
+# CONFIG_PKG_USING_ONNX_BACKEND is not set
+# CONFIG_PKG_USING_ONNX_PARSER is not set
+# CONFIG_PKG_USING_TENSORFLOWLITEMICRO is not set
+# CONFIG_PKG_USING_ELAPACK is not set
+# CONFIG_PKG_USING_ULAPACK is not set
+# CONFIG_PKG_USING_QUEST is not set
+# CONFIG_PKG_USING_NAXOS is not set
+# CONFIG_PKG_USING_R_TINYMAIX is not set
+# CONFIG_PKG_USING_LLMCHAT is not set
+# end of AI packages
+
+#
+# Signal Processing and Control Algorithm Packages
+#
+# CONFIG_PKG_USING_APID is not set
+# CONFIG_PKG_USING_FIRE_PID_CURVE is not set
+# CONFIG_PKG_USING_QPID is not set
+# CONFIG_PKG_USING_UKAL is not set
+# CONFIG_PKG_USING_DIGITALCTRL is not set
+# CONFIG_PKG_USING_KISSFFT is not set
+# end of Signal Processing and Control Algorithm Packages
+
+#
+# miscellaneous packages
+#
+
+#
+# project laboratory
+#
+# end of project laboratory
+
+#
+# samples: kernel and components samples
+#
+# CONFIG_PKG_USING_KERNEL_SAMPLES is not set
+# CONFIG_PKG_USING_FILESYSTEM_SAMPLES is not set
+# CONFIG_PKG_USING_NETWORK_SAMPLES is not set
+# CONFIG_PKG_USING_PERIPHERAL_SAMPLES is not set
+# end of samples: kernel and components samples
+
+#
+# entertainment: terminal games and other interesting software packages
+#
+# CONFIG_PKG_USING_CMATRIX is not set
+# CONFIG_PKG_USING_SL is not set
+# CONFIG_PKG_USING_CAL is not set
+# CONFIG_PKG_USING_ACLOCK is not set
+# CONFIG_PKG_USING_THREES is not set
+# CONFIG_PKG_USING_2048 is not set
+# CONFIG_PKG_USING_SNAKE is not set
+# CONFIG_PKG_USING_TETRIS is not set
+# CONFIG_PKG_USING_DONUT is not set
+# CONFIG_PKG_USING_COWSAY is not set
+# CONFIG_PKG_USING_MORSE is not set
+# end of entertainment: terminal games and other interesting software packages
+
+# CONFIG_PKG_USING_LIBCSV is not set
+# CONFIG_PKG_USING_OPTPARSE is not set
+# CONFIG_PKG_USING_FASTLZ is not set
+# CONFIG_PKG_USING_MINILZO is not set
+# CONFIG_PKG_USING_QUICKLZ is not set
+# CONFIG_PKG_USING_LZMA is not set
+# CONFIG_PKG_USING_RALARAM is not set
+# CONFIG_PKG_USING_MULTIBUTTON is not set
+# CONFIG_PKG_USING_FLEXIBLE_BUTTON is not set
+# CONFIG_PKG_USING_CANFESTIVAL is not set
+# CONFIG_PKG_USING_ZLIB is not set
+# CONFIG_PKG_USING_MINIZIP is not set
+# CONFIG_PKG_USING_HEATSHRINK is not set
+# CONFIG_PKG_USING_DSTR is not set
+# CONFIG_PKG_USING_TINYFRAME is not set
+# CONFIG_PKG_USING_KENDRYTE_DEMO is not set
+# CONFIG_PKG_USING_UPACKER is not set
+# CONFIG_PKG_USING_UPARAM is not set
+# CONFIG_PKG_USING_HELLO is not set
+# CONFIG_PKG_USING_VI is not set
+# CONFIG_PKG_USING_KI is not set
+# CONFIG_PKG_USING_ARMv7M_DWT is not set
+# CONFIG_PKG_USING_CRCLIB is not set
+# CONFIG_PKG_USING_LIBCRC is not set
+# CONFIG_PKG_USING_LWGPS is not set
+# CONFIG_PKG_USING_STATE_MACHINE is not set
+# CONFIG_PKG_USING_DESIGN_PATTERN is not set
+# CONFIG_PKG_USING_CONTROLLER is not set
+# CONFIG_PKG_USING_PHASE_LOCKED_LOOP is not set
+# CONFIG_PKG_USING_MFBD is not set
+# CONFIG_PKG_USING_SLCAN2RTT is not set
+# CONFIG_PKG_USING_SOEM is not set
+# CONFIG_PKG_USING_QPARAM is not set
+# CONFIG_PKG_USING_CorevMCU_CLI is not set
+# CONFIG_PKG_USING_DRMP is not set
+# end of miscellaneous packages
+
+#
+# Arduino libraries
+#
+# CONFIG_PKG_USING_RTDUINO is not set
+
+#
+# Projects and Demos
+#
+# CONFIG_PKG_USING_ARDUINO_MSGQ_C_CPP_DEMO is not set
+# CONFIG_PKG_USING_ARDUINO_SKETCH_LOADER_DEMO is not set
+# CONFIG_PKG_USING_ARDUINO_ULTRASOUND_RADAR is not set
+# CONFIG_PKG_USING_ARDUINO_RTDUINO_SENSORFUSION_SHIELD is not set
+# CONFIG_PKG_USING_ARDUINO_NINEINONE_SENSOR_SHIELD is not set
+# CONFIG_PKG_USING_ARDUINO_SENSOR_KIT is not set
+# CONFIG_PKG_USING_ARDUINO_MATLAB_SUPPORT is not set
+# end of Projects and Demos
+
+#
+# Sensors
+#
+# CONFIG_PKG_USING_ARDUINO_SENSOR_DEVICE_DRIVERS is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSOR is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SENSORLAB is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL375 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L0X is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL53L1X is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VL6180X is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31855 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31865 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX31856 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX6675 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90614 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS1 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AHTX0 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM9DS0 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP280 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADT7410 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME680 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9808 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4728 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA219 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR390 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL345 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DHT is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP9600 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM6DS is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO055 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MAX1704X is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMC56X3 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90393 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90395 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ICM20X is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DPS310 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTS221 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT4X is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHT31 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADXL343 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BME280 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS726X is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AMG88XX is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2320 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AM2315 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LTR329_LTR303 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP085_UNIFIED is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP183_UNIFIED is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BMP3XX is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MS8607 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3MDL is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MLX90640 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MMA8451 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MSA301 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL115A2 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BNO08X_RVC is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS2MDL is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303DLH_MAG is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LC709203F is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CAP1188 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_CCS811 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_NAU7802 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS331 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS2X is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LPS35HW is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LSM303_ACCEL is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_LIS3DH is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8591 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPL3115A2 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPR121 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPRLS is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MPU6050 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCT2075 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PM25AQI is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_EMC2101 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXAS21002C is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SCD30 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_FXOS8700 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HMC5883_UNIFIED is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP30 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP006 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TLA202X is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCS34725 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI7021 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI1145 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SGP40 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SHTC3 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HDC1000 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU21DF is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AS7341 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_HTU31D is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_INA260 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP007_LIBRARY is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_L3GD20 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TMP117 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSC2007 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2561 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TSL2591_LIBRARY is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VCNL4040 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6070 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML6075 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_VEML7700 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_LIS3DHTR is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_DHT is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL335 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_ADXL345 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_BME280 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_BMP280 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_H3LIS331DL is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_MMA7660 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_TSL2561 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_PAJ7620 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_VL53L0X is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_ITG3200 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_SHT31 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_HP20X is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_DRV2605L is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_BBM150 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_HMC5883L is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_LSM303DLH is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_TCS3414CS is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_MP503 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_BMP085 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_HIGHTEMP is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_VEML6070 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_SI1145 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_SHT35 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_AT42QT1070 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_LSM6DS3 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_HDC1000 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_HM3301 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_MCP9600 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_LTC2941 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_LDC1612 is not set
+# CONFIG_PKG_USING_ARDUINO_CAPACITIVESENSOR is not set
+# CONFIG_PKG_USING_ARDUINO_JARZEBSKI_MPU6050 is not set
+# end of Sensors
+
+#
+# Display
+#
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_GFX_LIBRARY is not set
+# CONFIG_PKG_USING_ARDUINO_U8G2 is not set
+# CONFIG_PKG_USING_ARDUINO_TFT_ESPI is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ST7735 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SSD1306 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ILI9341 is not set
+# CONFIG_PKG_USING_SEEED_TM1637 is not set
+# end of Display
+
+#
+# Timing
+#
+# CONFIG_PKG_USING_ARDUINO_RTCLIB is not set
+# CONFIG_PKG_USING_ARDUINO_MSTIMER2 is not set
+# CONFIG_PKG_USING_ARDUINO_TICKER is not set
+# CONFIG_PKG_USING_ARDUINO_TASKSCHEDULER is not set
+# end of Timing
+
+#
+# Data Processing
+#
+# CONFIG_PKG_USING_ARDUINO_KALMANFILTER is not set
+# CONFIG_PKG_USING_ARDUINO_ARDUINOJSON is not set
+# CONFIG_PKG_USING_ARDUINO_TENSORFLOW_LITE_MICRO is not set
+# CONFIG_PKG_USING_ARDUINO_RUNNINGMEDIAN is not set
+# end of Data Processing
+
+#
+# Data Storage
+#
+
+#
+# Communication
+#
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PN532 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI4713 is not set
+# end of Communication
+
+#
+# Device Control
+#
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCF8574 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_PCA9685 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TPA2016 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DRV2605 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS1841 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_DS3502 is not set
+# CONFIG_PKG_USING_ARDUINO_SEEED_PCF85063TP is not set
+# end of Device Control
+
+#
+# Other
+#
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MFRC630 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_SI5351 is not set
+# end of Other
+
+#
+# Signal IO
+#
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BUSIO is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_TCA8418 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP23017 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_ADS1X15 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_AW9523 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP3008 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_MCP4725 is not set
+# CONFIG_PKG_USING_ARDUINO_ADAFRUIT_BD3491FS is not set
+# end of Signal IO
+
+#
+# Uncategorized
+#
+# end of Arduino libraries
+# end of RT-Thread online packages
+
+#
+# XiangShan configs
+#
+# CONFIG_BSP_USING_VIRTIO is not set
+# end of XiangShan configs
+
+CONFIG_BOARD_XIANGSHAN=y
+# CONFIG_ENABLE_FPU is not set
+# CONFIG_ENABLE_VECTOR is not set
+# CONFIG_RT_USING_USERSPACE_32BIT_LIMIT is not set
+CONFIG_PLIC_BASE=0x3c000000
+CONFIG___STACKSIZE__=16384
diff --git a/bsp/xiangshan-verilator-xiangshan/.gitignore b/bsp/xiangshan-verilator-xiangshan/.gitignore
new file mode 100644
index 00000000000..341f703a73f
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/.gitignore
@@ -0,0 +1,3 @@
+mnt.c
+romfs_data.c
+opensbi
diff --git a/bsp/xiangshan-verilator-xiangshan/Kconfig b/bsp/xiangshan-verilator-xiangshan/Kconfig
new file mode 100644
index 00000000000..b68f17f9c83
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/Kconfig
@@ -0,0 +1,61 @@
+mainmenu "RT-Thread Project Configuration"
+
+BSP_DIR := .
+
+RTT_DIR := ../../
+
+PKGS_DIR := packages
+
+source "$(RTT_DIR)/Kconfig"
+osource "$PKGS_DIR/Kconfig"
+rsource "driver/Kconfig"
+
+config BOARD_XIANGSHAN
+    bool
+    select ARCH_RISCV64
+    select ARCH_USING_RISCV_COMMON64
+    select RT_USING_COMPONENTS_INIT
+    select RT_USING_USER_MAIN
+    select RT_USING_CACHE
+    select ARCH_MM_MMU
+    select ARCH_REMAP_KERNEL
+    default y
+
+config ENABLE_FPU
+    bool "Enable FPU"
+    select ARCH_RISCV_FPU
+    default n
+
+config ENABLE_VECTOR
+    bool "Using RISC-V Vector Extension"
+    select ARCH_RISCV_VECTOR
+    default n
+
+if ENABLE_VECTOR
+    choice
+    prompt "Vector Registers Length in Bits"
+    default ARCH_VECTOR_VLEN_128
+
+    config ARCH_VECTOR_VLEN_128
+        bool "128"
+
+    config ARCH_VECTOR_VLEN_256
+        bool "256"
+    endchoice
+endif
+
+config RT_USING_USERSPACE_32BIT_LIMIT
+    bool "Enable userspace 32bit limit"
+    default n
+
+config RT_USING_VIRTIO_MMIO_ALIGN
+    bool "Open packed attribution, this may caused an error on virtio"
+    default n
+
+config PLIC_BASE
+    hex "PLIC base address"
+    default 0x3c000000
+
+config __STACKSIZE__
+    int "stack size for interrupt"
+    default 4096
diff --git a/bsp/xiangshan-verilator-xiangshan/README.md b/bsp/xiangshan-verilator-xiangshan/README.md
new file mode 100644
index 00000000000..5a41a1f6080
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/README.md
@@ -0,0 +1,407 @@
+**XiangShan/RISCV64 Board Support Package User Guide**
+
+English | [中文](./README_cn.md)
+
+
+
+- [1. Introduction](#1-introduction)
+- [2. Building](#2-building)
+	- [2.1. Installing the toolchain](#21-installing-the-toolchain)
+	- [2.2. Setting RT-Thread toolchain environment variables](#22-setting-rt-thread-toolchain-environment-variables)
+	- [2.3. Downloading the kernel](#23-downloading-the-kernel)
+	- [2.4. Configuring the kernel](#24-configuring-the-kernel)
+	- [2.5. Compiling the kernel](#25-compiling-the-kernel)
+- [3. Running](#3-running)
+	- [3.1. Installing QEMU](#31-installing-qemu)
+	- [3.2. Running QEMU](#32-running-qemu)
+		- [3.2.1. Running RT-Thread Standard Edition](#321-running-rt-thread-standard-edition)
+		- [3.2.2. Running RT-Thread Smart version](#322-running-rt-thread-smart-version)
+		- [3.2.3. Running RT-Thread Smart version + Root file-system](#323-running-rt-thread-smart-version--root-file-system)
+- [4. How to use rv64ilp32 toolchain](#4-how-to-use-rv64ilp32-toolchain)
+- [5. Contact information](#5-contact-information)
+
+
+
+# 1. Introduction
+
+RISC-V is an open and free instruction set architecture (ISA). This BSP targets the XiangShan environment and is derived from the RISCV64 VIRT BSP.
+
+> Note: The QEMU-specific build/run steps below are inherited from the original BSP and may not apply to XiangShan. Please adapt them to your XiangShan runtime environment.
+
+This project supports the world's first rv64ilp32 product-level open source toolchain jointly launched by the Xuantie team and the Institute of Software of the Chinese Academy of Sciences.
+
+# 2. Building
+
+Working system: take Ubuntu 22.04 as an example:
+
+```shell
+$ lsb_release -a
+No LSB modules are available.
+Distributor ID: Ubuntu
+Description: Ubuntu 22.04.2 LTS
+Release: 22.04
+Codename: jammy
+```
+
+## 2.1. Installing the toolchain
+
+The specific toolchain used is consistent with the official RT-Thread. For the specific toolchain version, please refer to the file  in the RT-Thread repository.
+
+```yaml
+    - name: Install RISC-V ToolChains
+      if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST != 'rtsmart/riscv64' && success() }}
+      run: |
+        wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.4/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz
+        sudo tar zxvf riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz -C /opt
+        /opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gcc --version
+        echo "RTT_EXEC_PATH=/opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin" >> $GITHUB_ENV
+
+    - name: Install RISC-V Musl ToolChains
+      if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST == 'rtsmart/riscv64' && success() }}
+      shell: bash
+      run: |
+        wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.7/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2
+        sudo tar xjf riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 -C /opt
+        /opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin/riscv64-unknown-linux-musl-gcc --version
+        echo "RTT_EXEC_PATH=/opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin" >> $GITHUB_ENV
+        echo "RTT_CC_PREFIX=riscv64-unknown-linux-musl-" >> $GITHUB_ENV
+```
+
+Among them, `riscv64-unknown-elf-gcc` is used to build the RT-Thread Standard version, and `riscv64-unknown-linux-musl-gcc` is used to build the RT-Thread Smart version. Download them to your local computer according to the links shown above and decompress them.
+
+## 2.2. Setting RT-Thread toolchain environment variables
+
+There are three environment variables related to the RT-Thread toolchain
+
+- `RTT_CC` is the toolchain name, which is `"gcc"` here
+- `RTT_CC_PREFIX`: is the toolchain prefix, which is `"riscv64-unknown-elf-"` for the Standard version and `"riscv64-unknown-linux-musl-"` for the Smart version.
+- `RTT_EXEC_PATH`: the path where the bin folder of the toolchain is located, such as `"$HOME/tools/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin"`. This is set according to the actual path after personal download and decompression. Note that the toolchains of the RT-Thread standard version and the Smart version are two different versions, and the path name of `RTT_EXEC_PATH` must be set to `bin`.
+
+If you use them all the time, it is recommended to export these three environment variables in the `.bashrc` file.
+
+## 2.3. Downloading the kernel
+
+Assume that the working path is `$WORKSPACE`.
+
+```shell
+$ cd $WORKSPACE
+$ git clone git@github.com:RT-Thread/rt-thread.git
+```
+
+Enter the BSP directory where xiangshan is located. The following operations will not be introduced separately. By default, it is in this directory.
+
+```shell
+$ cd $WORKSPACE/rt-thread/bsp/xiangshan
+```
+
+## 2.4. Configuring the kernel
+
+Refresh the configuration file before compiling for the first time.
+
+```shell
+$ scons --menuconfig
+```
+
+The default configuration is the RT-Thread standard version, so if you don't have any special requirements, don't change anything, just save and exit.
+
+If you want to use the RT-Thread Smart version, at least turn on the `RT_USING_SMART` option after entering the configuration menu (see the figure below), and the rest depends on your needs.
+
+```
+(Top) → RT-Thread Kernel
+RT-Thread Project Configuration
+(24) The maximal size of kernel object name
+[ ] Use the data types defined in ARCH_CPU
+[*] Enable RT-Thread Smart (microkernel on kernel/userland)
+[ ] Enable RT-Thread Nano
+...
+```
+
+Save and exit after modification.
+
+## 2.5. Compiling the kernel
+
+If you have compiled before, you can clean it up:
+
+```shell
+$ scons --clean
+```
+
+Or compile directly:
+
+```shell
+$ scons -j$(nproc)
+```
+
+The kernel binary file `rtthread.bin` will be generated in the `$WORKSPACE/rt-thread/bsp/xiangshan`.
+
+# 3. Running
+
+## 3.1. Installing QEMU
+
+```shell
+$ sudo apt update
+$ sudo apt install qemu-system-misc
+```
+
+After the installation is complete, you can check the version.
+
+```shell
+$ qemu-system-riscv64 --version
+QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.24)
+Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers
+```
+
+## 3.2. Running QEMU
+
+The repository has provided a ready-made execution script, which can be executed directly:
+
+```shell
+$ ./run.sh
+```
+
+### 3.2.1. Running RT-Thread Standard Edition
+
+The following is an example:
+
+```shell
+$ ./run.sh 
+
+OpenSBI v0.9
+   ____                    _____ ____ _____
+  / __ \                  / ____|  _ \_   _|
+ | |  | |_ __   ___ _ __ | (___ | |_) || |
+ | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
+ | |__| | |_) |  __/ | | |____) | |_) || |_
+  \____/| .__/ \___|_| |_|_____/|____/_____|
+        | |
+        |_|
+
+Platform Name             : riscv-virtio,qemu
+Platform Features         : timer,mfdeleg
+Platform HART Count       : 1
+Firmware Base             : 0x80000000
+Firmware Size             : 100 KB
+Runtime SBI Version       : 0.2
+
+Domain0 Name              : root
+Domain0 Boot HART         : 0
+Domain0 HARTs             : 0*
+Domain0 Region00          : 0x0000000080000000-0x000000008001ffff ()
+Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
+Domain0 Next Address      : 0x0000000080200000
+Domain0 Next Arg1         : 0x000000008f000000
+Domain0 Next Mode         : S-mode
+Domain0 SysReset          : yes
+
+Boot HART ID              : 0
+Boot HART Domain          : root
+Boot HART ISA             : rv64imafdcsu
+Boot HART Features        : scounteren,mcounteren,time
+Boot HART PMP Count       : 16
+Boot HART PMP Granularity : 4
+Boot HART PMP Address Bits: 54
+Boot HART MHPM Count      : 0
+Boot HART MHPM Count      : 0
+Boot HART MIDELEG         : 0x0000000000000222
+Boot HART MEDELEG         : 0x000000000000b109
+heap: [0x8028d8a8 - 0x8428d8a8]
+
+ \ | /
+- RT -     Thread Operating System
+ / | \     5.2.0 build Nov 14 2024 15:41:57
+ 2006 - 2024 Copyright by RT-Thread team
+lwIP-2.0.3 initialized!
+[I/sal.skt] Socket Abstraction Layer initialize success.
+[I/utest] utest is initialize success.
+[I/utest] total utest testcase num: (0)
+file system initialization done!
+Hello RISC-V
+msh />
+```
+### 3.2.2. Running RT-Thread Smart version
+
+The following is an example:
+
+```shell
+$ ./run.sh 
+
+OpenSBI v0.9
+   ____                    _____ ____ _____
+  / __ \                  / ____|  _ \_   _|
+ | |  | |_ __   ___ _ __ | (___ | |_) || |
+ | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
+ | |__| | |_) |  __/ | | |____) | |_) || |_
+  \____/| .__/ \___|_| |_|_____/|____/_____|
+        | |
+        |_|
+
+Platform Name             : riscv-virtio,qemu
+Platform Features         : timer,mfdeleg
+Platform HART Count       : 1
+Firmware Base             : 0x80000000
+Firmware Size             : 100 KB
+Runtime SBI Version       : 0.2
+
+Domain0 Name              : root
+Domain0 Boot HART         : 0
+Domain0 HARTs             : 0*
+Domain0 Region00          : 0x0000000080000000-0x000000008001ffff ()
+Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
+Domain0 Next Address      : 0x0000000080200000
+Domain0 Next Arg1         : 0x000000008f000000
+Domain0 Next Mode         : S-mode
+Domain0 SysReset          : yes
+
+Boot HART ID              : 0
+Boot HART Domain          : root
+Boot HART ISA             : rv64imafdcsu
+Boot HART Features        : scounteren,mcounteren,time
+Boot HART PMP Count       : 16
+Boot HART PMP Granularity : 4
+Boot HART PMP Address Bits: 54
+Boot HART MHPM Count      : 0
+Boot HART MHPM Count      : 0
+Boot HART MIDELEG         : 0x0000000000000222
+Boot HART MEDELEG         : 0x000000000000b109
+heap: [0x002ef030 - 0x042ef030]
+
+ \ | /
+- RT -     Thread Smart Operating System
+ / | \     5.2.0 build Nov 14 2024 15:48:43
+ 2006 - 2024 Copyright by RT-Thread team
+lwIP-2.0.3 initialized!
+[I/sal.skt] Socket Abstraction Layer initialize success.
+[I/utest] utest is initialize success.
+[I/utest] total utest testcase num: (0)
+[I/drivers.serial] Using /dev/ttyS0 as default console
+file system initialization done!
+Hello RISC-V
+msh />
+```
+
+### 3.2.3. Running RT-Thread Smart version + Root file-system
+
+For the Smart version of the kernel, you can also specify the path of the root file-system image file when executing the `run.sh` script to mount the root file-system during the startup process.
+
+It should be noted that the kernel supports fat by default. If you want to mount the ext4 file-system, you need to install the lwext4 package additionally, i.e. to enable the `PKG_USING_LWEXT4` option (the specific menuconfig path is (Top) -> RT-Thread online packages -> system packages -> lwext4: an excellent choice of ext2/3/4 filesystem for microcontrollers.). If you can't find the item in the menu, you can exit menuconfig and execute `pkgs --upgrade` to update the package index and then try to enable the package.
+
+After checking this option, you also need to perform the following operations to update the software and install the source code to the packages directory of bsp (this operation only needs to be performed once):
+
+```shell
+$ source ~/.env/env.sh
+$ pkgs --update
+```
+
+Save and recompile the kernel.
+
+For how to make a root file-system, please refer to , which will not be repeated here.
+
+The example is as follows:
+
+```shell
+$ ./run.sh /home/u/ws/duo/userapps/apps/build/ext4.img 
+
+OpenSBI v0.9
+   ____                    _____ ____ _____
+  / __ \                  / ____|  _ \_   _|
+ | |  | |_ __   ___ _ __ | (___ | |_) || |
+ | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
+ | |__| | |_) |  __/ | | |____) | |_) || |_
+  \____/| .__/ \___|_| |_|_____/|____/_____|
+        | |
+        |_|
+
+Platform Name             : riscv-virtio,qemu
+Platform Features         : timer,mfdeleg
+Platform HART Count       : 1
+Firmware Base             : 0x80000000
+Firmware Size             : 100 KB
+Runtime SBI Version       : 0.2
+
+Domain0 Name              : root
+Domain0 Boot HART         : 0
+Domain0 HARTs             : 0*
+Domain0 Region00          : 0x0000000080000000-0x000000008001ffff ()
+Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
+Domain0 Next Address      : 0x0000000080200000
+Domain0 Next Arg1         : 0x000000008f000000
+Domain0 Next Mode         : S-mode
+Domain0 SysReset          : yes
+
+Boot HART ID              : 0
+Boot HART Domain          : root
+Boot HART ISA             : rv64imafdcsu
+Boot HART Features        : scounteren,mcounteren,time
+Boot HART PMP Count       : 16
+Boot HART PMP Granularity : 4
+Boot HART PMP Address Bits: 54
+Boot HART MHPM Count      : 0
+Boot HART MHPM Count      : 0
+Boot HART MIDELEG         : 0x0000000000000222
+Boot HART MEDELEG         : 0x000000000000b109
+heap: [0x00326438 - 0x04326438]
+
+ \ | /
+- RT -     Thread Smart Operating System
+ / | \     5.2.0 build Dec 17 2024 11:49:39
+ 2006 - 2024 Copyright by RT-Thread team
+lwIP-2.0.3 initialized!
+[I/sal.skt] Socket Abstraction Layer initialize success.
+[I/utest] utest is initialize success.
+[I/utest] total utest testcase num: (0)
+[I/drivers.serial] Using /dev/ttyS0 as default console
+[W/DFS.fs] mount / failed with file system type: elm
+file system initialization done!
+Hello RISC-V
+msh />[E/sal.skt] not find network interface device by protocol family(1).
+[E/sal.skt] SAL socket protocol family input failed, return error -3.
+/ # ls
+bin         lib         proc        sbin        tmp
+dev         lost+found  root        services    usr
+etc         mnt         run         tc          var
+/ # 
+```
+
+# 4. How to use rv64ilp32 toolchain
+
+- Toolchain address: 
+
+- Usage:
+
+  - Configure toolchain path
+
+  - Modify ABI parameter to: `-mabi=ilp32d`
+
+  - Then perform regular compilation
+
+  - Use [script](./qemu-rv64ilp32-nographic.sh) to start QEMU (INFO: QEMU binary is also in the toolchain directory)
+
+- Compare the firmware size of the same project compiled using the traditional 64-bit toolchain and the new 32-bit toolchain:
+
+  Traditional 64-bit toolchain firmware size:
+
+  ```bash
+  Memory region         Used Size  Region Size  %age Used
+              SRAM:      225856 B        16 MB      1.35%
+  riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin
+  riscv64-unknown-elf-size rtthread.elf
+     text    data     bss     dec     hex filename
+   150907    3664   71268  225839   3722f rtthread.elf
+  ```
+  
+  New 32-bit toolchain firmware size:
+
+  ```bash
+  Memory region         Used Size  Region Size  %age Used
+              SRAM:      209376 B        16 MB      1.25%
+  riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin
+  riscv64-unknown-elf-size rtthread.elf
+     text    data     bss     dec     hex filename
+   138739    1356   69276  209371   331db rtthread.elf
+  ```
+
+# 5. Contact information
+
+Maintainer: [bernard][1]
+
+[1]: https://github.com/BernardXiong
\ No newline at end of file
diff --git a/bsp/xiangshan-verilator-xiangshan/README_cn.md b/bsp/xiangshan-verilator-xiangshan/README_cn.md
new file mode 100644
index 00000000000..5990ab316dc
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/README_cn.md
@@ -0,0 +1,411 @@
+**XiangShan/RISCV64 板级支持包使用说明**
+
+中文页 | [English](./README.md)
+
+
+
+- [1. 简介](#1-简介)
+- [2. 构建](#2-构建)
+	- [2.1. 安装工具链](#21-安装工具链)
+	- [2.2. 设置 RT-Thread 工具链环境变量](#22-设置-rt-thread-工具链环境变量)
+	- [2.3. 下载内核](#23-下载内核)
+	- [2.4. 配置内核](#24-配置内核)
+	- [2.5. 编译内核](#25-编译内核)
+- [3. 运行](#3-运行)
+	- [3.1. 安装 QEMU](#31-安装-qemu)
+	- [3.2. 运行 QEMU](#32-运行-qemu)
+		- [3.2.1. 运行 RT-Thread 标准版](#321-运行-rt-thread-标准版)
+		- [3.2.2. 运行 RT-Thread Smart 版](#322-运行-rt-thread-smart-版)
+		- [3.2.3. 运行 RT-Thread Smart 版 + 根文件系统](#323-运行-rt-thread-smart-版--根文件系统)
+- [4. 如何使用 rv64ilp32 工具链](#4-如何使用-rv64ilp32-工具链)
+- [5. 联系人信息](#5-联系人信息)
+
+
+
+# 1. 简介
+
+RISC-V 是一种开放和免费的指令集体系结构 (ISA)。本 BSP 面向 XiangShan 环境,派生自 RISCV64 VIRT BSP。
+
+> 说明:下方的 QEMU 构建/运行步骤继承自原 BSP,可能不适用于 XiangShan,请根据 XiangShan 运行环境自行调整。
+
+本工程支持玄铁团队联合中科院软件所共同推出的全球首款 rv64ilp32 产品级开源工具链。
+
+# 2. 构建
+
+工作系统:以 Ubuntu 22.04 为例:
+
+```shell
+$ lsb_release -a
+No LSB modules are available.
+Distributor ID: Ubuntu
+Description:    Ubuntu 22.04.2 LTS
+Release:        22.04
+Codename:       jammy
+```
+
+## 2.1. 安装工具链
+
+具体使用的工具链,和 RT-Thread 官方保持一致,具体的工具链版本可以参考 RT-Thread 仓库的  这个文件。
+
+```yaml
+    - name: Install RISC-V ToolChains
+      if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST != 'rtsmart/riscv64' && success() }}
+      run: |
+        wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.4/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz
+        sudo tar zxvf riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14.tar.gz -C /opt
+        /opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin/riscv64-unknown-elf-gcc --version
+        echo "RTT_EXEC_PATH=/opt/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin" >> $GITHUB_ENV
+
+    - name: Install RISC-V Musl ToolChains
+      if: ${{ matrix.legs.QEMU_ARCH == 'riscv64' && matrix.legs.UTEST == 'rtsmart/riscv64' && success() }}
+      shell: bash
+      run: |
+        wget -q https://github.com/RT-Thread/toolchains-ci/releases/download/v1.7/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2
+        sudo tar xjf riscv64-linux-musleabi_for_x86_64-pc-linux-gnu_latest.tar.bz2 -C /opt
+        /opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin/riscv64-unknown-linux-musl-gcc --version
+        echo "RTT_EXEC_PATH=/opt/riscv64-linux-musleabi_for_x86_64-pc-linux-gnu/bin" >> $GITHUB_ENV
+        echo "RTT_CC_PREFIX=riscv64-unknown-linux-musl-" >> $GITHUB_ENV
+```
+
+其中 `riscv64-unknown-elf-gcc` 用于构建 RT-Thread 标准版,`riscv64-unknown-linux-musl-gcc` 用于构建 RT-Thread Smart 版。根据上面所示链接分别下载到本地后解压缩。
+
+## 2.2. 设置 RT-Thread 工具链环境变量
+
+和 RT-Thread 工具链相关的环境变量有三个
+
+- `RTT_CC` 为工具链名称, 这里统一为 `"gcc"`
+- `RTT_CC_PREFIX`: 为工具链前缀, 这里对于标准版是 `"riscv64-unknown-elf-"`,对于 Smart 版是 `"riscv64-unknown-linux-musl-"`。
+- `RTT_EXEC_PATH`: 工具链的 bin 文件夹所在路径, 如 `"$HOME/tools/riscv64-unknown-elf-toolchain-10.2.0-2020.12.8-x86_64-linux-ubuntu14/bin"`, 这个根据个人下载解压后的实际路径进行设置,注意 RT-Thread 标准版和 Smart 版本的工具链是两套不同的版本,而且设置 `RTT_EXEC_PATH` 的路径名时要一直到 `bin`。
+
+如果一直使用的话,建议将这三个环境变量在 `.bashrc` 文件中 export。
+
+## 2.3. 下载内核
+
+假设工作路径是 `$WORKSPACE`。
+
+```shell
+$ cd $WORKSPACE
+$ git clone git@github.com:RT-Thread/rt-thread.git
+```
+
+进入 xiangshan 所在 BSP 目录,后面的操作不做另外介绍,默认就在这个目录下。
+
+```shell
+$ cd $WORKSPACE/rt-thread/bsp/xiangshan
+```
+
+## 2.4. 配置内核
+
+第一次编译前先刷新一下配置文件。
+
+```shell
+$ scons --menuconfig
+```
+
+默认配置就是 RT-Thread 标准版,所以如果没有什么特别需求,什么都不要改动,直接保存退出即可。
+
+如果要使用 RT-Thread Smart 版,进入配置菜单后至少要打开 `RT_USING_SMART` 这个选项(见下图),其他的看自己的需求。
+
+```
+(Top) → RT-Thread Kernel
+                                                                RT-Thread Project Configuration
+(24) The maximal size of kernel object name
+[ ] Use the data types defined in ARCH_CPU
+[*] Enable RT-Thread Smart (microkernel on kernel/userland)
+[ ] Enable RT-Thread Nano
+...
+```
+
+修改后保存退出。
+
+## 2.5. 编译内核
+
+如果以前编译后,可以清理一下:
+
+```shell
+$ scons --clean
+```
+
+或者直接编译:
+
+```shell
+$ scons -j$(nproc)
+```
+
+在 `$WORKSPACE/rt-thread/bsp/xiangshan` 路径下会生成内核的二进制文件 `rtthread.bin`。
+
+# 3. 运行
+
+## 3.1. 安装 QEMU
+
+```shell
+$ sudo apt update
+$ sudo apt install qemu-system-misc
+```
+
+安装完毕后可以看一下版本。
+
+```shell
+$ qemu-system-riscv64 --version
+QEMU emulator version 6.2.0 (Debian 1:6.2+dfsg-2ubuntu6.24)
+Copyright (c) 2003-2021 Fabrice Bellard and the QEMU Project developers
+```
+
+## 3.2. 运行 QEMU
+
+仓库里已经提供了现成的执行脚本,可以直接执行:
+
+```shell
+$ ./run.sh
+```
+
+### 3.2.1. 运行 RT-Thread 标准版
+
+示例如下:
+
+```shell
+$ ./run.sh
+
+OpenSBI v0.9
+   ____                    _____ ____ _____
+  / __ \                  / ____|  _ \_   _|
+ | |  | |_ __   ___ _ __ | (___ | |_) || |
+ | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
+ | |__| | |_) |  __/ | | |____) | |_) || |_
+  \____/| .__/ \___|_| |_|_____/|____/_____|
+        | |
+        |_|
+
+Platform Name             : riscv-virtio,qemu
+Platform Features         : timer,mfdeleg
+Platform HART Count       : 1
+Firmware Base             : 0x80000000
+Firmware Size             : 100 KB
+Runtime SBI Version       : 0.2
+
+Domain0 Name              : root
+Domain0 Boot HART         : 0
+Domain0 HARTs             : 0*
+Domain0 Region00          : 0x0000000080000000-0x000000008001ffff ()
+Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
+Domain0 Next Address      : 0x0000000080200000
+Domain0 Next Arg1         : 0x000000008f000000
+Domain0 Next Mode         : S-mode
+Domain0 SysReset          : yes
+
+Boot HART ID              : 0
+Boot HART Domain          : root
+Boot HART ISA             : rv64imafdcsu
+Boot HART Features        : scounteren,mcounteren,time
+Boot HART PMP Count       : 16
+Boot HART PMP Granularity : 4
+Boot HART PMP Address Bits: 54
+Boot HART MHPM Count      : 0
+Boot HART MHPM Count      : 0
+Boot HART MIDELEG         : 0x0000000000000222
+Boot HART MEDELEG         : 0x000000000000b109
+heap: [0x8028d8a8 - 0x8428d8a8]
+
+ \ | /
+- RT -     Thread Operating System
+ / | \     5.2.0 build Nov 14 2024 15:41:57
+ 2006 - 2024 Copyright by RT-Thread team
+lwIP-2.0.3 initialized!
+[I/sal.skt] Socket Abstraction Layer initialize success.
+[I/utest] utest is initialize success.
+[I/utest] total utest testcase num: (0)
+file system initialization done!
+Hello RISC-V
+msh />
+```
+
+### 3.2.2. 运行 RT-Thread Smart 版
+
+示例如下:
+
+```shell
+$ ./run.sh
+
+OpenSBI v0.9
+   ____                    _____ ____ _____
+  / __ \                  / ____|  _ \_   _|
+ | |  | |_ __   ___ _ __ | (___ | |_) || |
+ | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
+ | |__| | |_) |  __/ | | |____) | |_) || |_
+  \____/| .__/ \___|_| |_|_____/|____/_____|
+        | |
+        |_|
+
+Platform Name             : riscv-virtio,qemu
+Platform Features         : timer,mfdeleg
+Platform HART Count       : 1
+Firmware Base             : 0x80000000
+Firmware Size             : 100 KB
+Runtime SBI Version       : 0.2
+
+Domain0 Name              : root
+Domain0 Boot HART         : 0
+Domain0 HARTs             : 0*
+Domain0 Region00          : 0x0000000080000000-0x000000008001ffff ()
+Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
+Domain0 Next Address      : 0x0000000080200000
+Domain0 Next Arg1         : 0x000000008f000000
+Domain0 Next Mode         : S-mode
+Domain0 SysReset          : yes
+
+Boot HART ID              : 0
+Boot HART Domain          : root
+Boot HART ISA             : rv64imafdcsu
+Boot HART Features        : scounteren,mcounteren,time
+Boot HART PMP Count       : 16
+Boot HART PMP Granularity : 4
+Boot HART PMP Address Bits: 54
+Boot HART MHPM Count      : 0
+Boot HART MHPM Count      : 0
+Boot HART MIDELEG         : 0x0000000000000222
+Boot HART MEDELEG         : 0x000000000000b109
+heap: [0x002ef030 - 0x042ef030]
+
+ \ | /
+- RT -     Thread Smart Operating System
+ / | \     5.2.0 build Nov 14 2024 15:48:43
+ 2006 - 2024 Copyright by RT-Thread team
+lwIP-2.0.3 initialized!
+[I/sal.skt] Socket Abstraction Layer initialize success.
+[I/utest] utest is initialize success.
+[I/utest] total utest testcase num: (0)
+[I/drivers.serial] Using /dev/ttyS0 as default console
+file system initialization done!
+Hello RISC-V
+msh />
+```
+
+### 3.2.3. 运行 RT-Thread Smart 版 + 根文件系统
+
+对于 Smart 版本的内核,也可以在执行 `run.sh` 脚本时指定根文件系统镜像文件的路径在启动过程中挂载根文件系统。
+
+需要注意的是,内核默认支持 fat, 如果要挂载 ext4 的文件系统,则还需要额外安装 lwext4 软件包,即使能 `PKG_USING_LWEXT4`(具体 menuconfig 路径是 (Top) -> RT-Thread online packages -> system packages ->  lwext4: an excellent choice of ext2/3/4 filesystem for microcontrollers.)。如果在菜单中找不到该软件包,可以退出 menuconfig 并执行 `pkgs --upgrade` 更新软件包索引后再尝试使能软件包。
+
+勾选该选项后还需要执行如下操作更新软件并安装源码到 bsp 的 packages 目录下(该操作只要执行一次即可):
+
+```shell
+$ source ~/.env/env.sh
+$ pkgs --update
+```
+
+保存后重新编译内核。
+
+有关如何制作根文件系统,请参考 ,这里不再赘述。
+
+示例如下:
+
+```shell
+$ ./run.sh /home/u/ws/duo/userapps/apps/build/ext4.img 
+
+OpenSBI v0.9
+   ____                    _____ ____ _____
+  / __ \                  / ____|  _ \_   _|
+ | |  | |_ __   ___ _ __ | (___ | |_) || |
+ | |  | | '_ \ / _ \ '_ \ \___ \|  _ < | |
+ | |__| | |_) |  __/ | | |____) | |_) || |_
+  \____/| .__/ \___|_| |_|_____/|____/_____|
+        | |
+        |_|
+
+Platform Name             : riscv-virtio,qemu
+Platform Features         : timer,mfdeleg
+Platform HART Count       : 1
+Firmware Base             : 0x80000000
+Firmware Size             : 100 KB
+Runtime SBI Version       : 0.2
+
+Domain0 Name              : root
+Domain0 Boot HART         : 0
+Domain0 HARTs             : 0*
+Domain0 Region00          : 0x0000000080000000-0x000000008001ffff ()
+Domain0 Region01          : 0x0000000000000000-0xffffffffffffffff (R,W,X)
+Domain0 Next Address      : 0x0000000080200000
+Domain0 Next Arg1         : 0x000000008f000000
+Domain0 Next Mode         : S-mode
+Domain0 SysReset          : yes
+
+Boot HART ID              : 0
+Boot HART Domain          : root
+Boot HART ISA             : rv64imafdcsu
+Boot HART Features        : scounteren,mcounteren,time
+Boot HART PMP Count       : 16
+Boot HART PMP Granularity : 4
+Boot HART PMP Address Bits: 54
+Boot HART MHPM Count      : 0
+Boot HART MHPM Count      : 0
+Boot HART MIDELEG         : 0x0000000000000222
+Boot HART MEDELEG         : 0x000000000000b109
+heap: [0x00326438 - 0x04326438]
+
+ \ | /
+- RT -     Thread Smart Operating System
+ / | \     5.2.0 build Dec 17 2024 11:49:39
+ 2006 - 2024 Copyright by RT-Thread team
+lwIP-2.0.3 initialized!
+[I/sal.skt] Socket Abstraction Layer initialize success.
+[I/utest] utest is initialize success.
+[I/utest] total utest testcase num: (0)
+[I/drivers.serial] Using /dev/ttyS0 as default console
+[W/DFS.fs] mount / failed with file system type: elm
+file system initialization done!
+Hello RISC-V
+msh />[E/sal.skt] not find network interface device by protocol family(1).
+[E/sal.skt] SAL socket protocol family input failed, return error -3.
+/ # ls
+bin         lib         proc        sbin        tmp
+dev         lost+found  root        services    usr
+etc         mnt         run         tc          var
+/ # 
+```
+
+# 4. 如何使用 rv64ilp32 工具链
+
+- 工具链地址:https://github.com/ruyisdk/riscv-gnu-toolchain-rv64ilp32/tags
+
+- 使用方法:
+
+  - 配置工具链路径
+
+  - 修改ABI参数为:`-mabi=ilp32d`
+
+  - 然后执行常规编译
+
+  - 使用 [脚本](./qemu-rv64ilp32-nographic.sh) 启动 QEMU (INFO: QEMU 二进制同样在工具链目录)
+
+- 使用传统 64 位工具链与使用新 32 位工具链编译相同工程的固件大小对比:
+
+  传统 64 位工具链固件大小:
+
+  ```bash
+  Memory region         Used Size  Region Size  %age Used
+              SRAM:      225856 B        16 MB      1.35%
+  riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin
+  riscv64-unknown-elf-size rtthread.elf
+     text    data     bss     dec     hex filename
+   150907    3664   71268  225839   3722f rtthread.elf
+  ```
+
+  新 32 位工具链固件大小:
+
+  ```bash
+  Memory region         Used Size  Region Size  %age Used
+              SRAM:      209376 B        16 MB      1.25%
+  riscv64-unknown-elf-objcopy -O binary rtthread.elf rtthread.bin
+  riscv64-unknown-elf-size rtthread.elf
+     text    data     bss     dec     hex filename
+   138739    1356   69276  209371   331db rtthread.elf
+  ```
+
+# 5. 联系人信息
+
+维护人:[bernard][1]
+
+[1]: https://github.com/BernardXiong
+
+
+
diff --git a/bsp/xiangshan-verilator-xiangshan/SConscript b/bsp/xiangshan-verilator-xiangshan/SConscript
new file mode 100644
index 00000000000..c7ef7659ece
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/SConscript
@@ -0,0 +1,14 @@
+# for module compiling
+import os
+from building import *
+
+cwd = GetCurrentDir()
+objs = []
+list = os.listdir(cwd)
+
+for d in list:
+    path = os.path.join(cwd, d)
+    if os.path.isfile(os.path.join(path, 'SConscript')):
+        objs = objs + SConscript(os.path.join(d, 'SConscript'))
+
+Return('objs')
diff --git a/bsp/xiangshan-verilator-xiangshan/SConstruct b/bsp/xiangshan-verilator-xiangshan/SConstruct
new file mode 100644
index 00000000000..ae0e3375e11
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/SConstruct
@@ -0,0 +1,62 @@
+import os
+import sys
+import rtconfig
+
+from rtconfig import RTT_ROOT
+
+sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')]
+from building import *
+
+TARGET = 'rtthread.' + rtconfig.TARGET_EXT
+
+DefaultEnvironment(tools=[])
+env = Environment(tools = ['mingw'],
+    AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS,
+    CC = rtconfig.CC, CCFLAGS = rtconfig.CFLAGS,
+    CXX = rtconfig.CXX, CXXFLAGS = rtconfig.CXXFLAGS,
+    AR = rtconfig.AR, ARFLAGS = '-rc',
+    LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS)
+env.PrependENVPath('PATH', rtconfig.EXEC_PATH)
+env['ASCOM'] = env['ASPPCOM']
+
+Export('RTT_ROOT')
+Export('rtconfig')
+rtconfig.CPU='virt64'
+rtconfig.ARCH='risc-v'
+
+# prepare building environment
+objs = PrepareBuilding(env, RTT_ROOT, has_libcpu = False)
+
+stack_size = 4096
+
+if GetDepend('RT_USING_SMART'):
+    # use smart link.lds
+    env['LINKFLAGS'] = env['LINKFLAGS'].replace('link.lds', 'link_smart.lds')
+
+stack_lds = open('link_stacksize.lds', 'w')
+if GetDepend('__STACKSIZE__'): stack_size = GetDepend('__STACKSIZE__')
+stack_lds.write('__STACKSIZE__ = %d;\n' % stack_size)
+stack_lds.close()
+
+# Obtain the number of harts from rtconfig.h and write 
+# it into link_cpus.lds for the linker script
+try:
+    with open('rtconfig.h', 'r') as f:
+        rtconfig_content = f.readlines()
+except FileNotFoundError:
+    cpus_nr = 1
+else:
+    cpus_nr = 1  # default value
+    for line in rtconfig_content:
+        line = line.strip()
+        if line.startswith('#define') and 'RT_CPUS_NR' in line:
+            parts = line.split()
+            if len(parts) >= 3 and parts[2].isdigit():
+                cpus_nr = int(parts[2])
+                break 
+
+with open('link_cpus.lds', 'w') as cpus_lds:
+    cpus_lds.write(f'RT_CPUS_NR = {cpus_nr};\n')
+
+# make a building
+DoBuilding(TARGET, objs)
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/README.md b/bsp/xiangshan-verilator-xiangshan/applications/README.md
new file mode 100644
index 00000000000..afa1627391a
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/README.md
@@ -0,0 +1,46 @@
+# Verilated DUT on RT-Thread (qemu-virt64-riscv)
+
+## 目标
+
+在 RT-Thread qemu-virt64-riscv BSP 中运行 Verilator 生成的 `dut_150` 仿真程序,使用 C++ 测试逻辑(`applications/main.cpp`)。
+
+## 关键裁剪点
+
+- **单线程运行**:若运行环境缺少 pthread,需强制 `threads(1)`;本目录已将线程池改为 RT-Thread API,可按需开启多线程。
+- **禁用计时/波形**:去除 coroutine/timing/trace 相关源码与生成选项:`--no-timing --no-trace`。
+- **RT-Thread 适配宏**:使用 `-DVL_RT_THREAD -DVL_MT_DISABLED` 避免调用缺失的 libc/线程特性,使用自带的字符串/数学函数。
+- **运行库精简**:通过 `verilator --getenv VERILATOR_ROOT` 获取 Verilator 安装目录,只从其 `include/` 中编译 `verilated.cpp`、`verilated_random.cpp`、`verilated_threads.cpp` 这 3 个运行时源文件。
+- **运行库来源**:BSP 不再依赖 `applications/verilator_runtime` 这份本地拷贝参与构建,而是直接使用当前系统 Verilator 安装中的 runtime。
+
+## 生成模型
+
+路径:`bsp/xiangshan-verilator/verilator-case/Makefile`
+
+- 默认调用:`verilator -Wall --cc dut_150.v --exe tb_150.cpp -Mdir ../applications/verilated_dut_150 --no-timing --no-trace --threads 1 --CFLAGS "-DVL_RT_THREAD -DVL_MT_DISABLED"`
+- 输出:`applications/verilated_dut_150/` 下的 `Vdut_150.*`。
+
+## 应用构建集成
+
+文件:`applications/SConscript`
+
+- 编译源:`*.c`/`*.cpp` + `verilated_dut_150/*.cpp` + 运行库最小集。
+- `CPPPATH`:应用目录、`verilated_dut_150`、`verilator --getenv VERILATOR_ROOT` 返回目录下的 `include/`。
+- `CPPDEFINES`:包含 `VL_RT_THREAD`。
+
+## 运行入口
+
+文件:`applications/main.cpp`
+
+- 创建上下文后可按需设置线程数,默认跟随 Verilator 生成的 `threads()`。
+- 重置与驱动 DUT 的测试序列,带有 `rt_kprintf`/`std::cout` 日志方便调试。
+- 为避免退出流程触发异常,结尾保持循环休眠(`rt_thread_mdelay`)。
+
+## 构建与运行
+
+- 构建:`scons --exec-path=$HOME/riscv-baremetal/bin --cc-prefix=riscv64-unknown-elf-`(在 `bsp/xiangshan-verilator`)。
+- 运行:`timeout 1 ~/qemu/build/qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin`。
+
+## 已知行为
+
+- 文件系统未挂载会提示 `DFS.fs mount / failed ...`,但不影响仿真测试逻辑。
+- 运行日志会打印 `[TB] ...`,确认 DUT 已构造并执行测试用例。
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/SConscript b/bsp/xiangshan-verilator-xiangshan/applications/SConscript
new file mode 100644
index 00000000000..268a40666c9
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/SConscript
@@ -0,0 +1,78 @@
+import os
+import subprocess
+
+from building import *
+
+
+def _get_verilator_include_dir():
+    try:
+        verilator_root = subprocess.check_output(
+            ['verilator', '--getenv', 'VERILATOR_ROOT'], universal_newlines=True
+        ).strip()
+    except (OSError, subprocess.CalledProcessError) as exc:
+        raise RuntimeError(
+            'Failed to query VERILATOR_ROOT via "verilator --getenv VERILATOR_ROOT"'
+        ) from exc
+
+    if not verilator_root:
+        raise RuntimeError('`verilator --getenv VERILATOR_ROOT` returned an empty path')
+
+    include_dir = os.path.join(verilator_root, 'include')
+    if not os.path.isdir(include_dir):
+        raise RuntimeError('Verilator include directory not found: %s' % include_dir)
+
+    return include_dir
+
+cwd = GetCurrentDir()
+
+# Application sources
+src = Glob('*.c') + Glob('*.cpp')
+
+# Verilated DUT and runtime support
+verilated_dut_dir = os.path.join(cwd, 'verilated_dut_150')
+verilator_rt_dir = _get_verilator_include_dir()
+verilator_runtime_build_dir = os.path.join(
+    os.path.dirname(cwd), 'build', 'applications', 'verilator_runtime'
+)
+
+# Only pull in the bits of the Verilated runtime we need for a single-threaded,
+# non-tracing simulation on RT-Thread.
+verilator_runtime_src = [
+    os.path.join(verilator_rt_dir, name)
+    for name in [
+        'verilated.cpp',
+        'verilated_random.cpp',
+        'verilated_threads.cpp',
+    ]
+]
+
+verilator_runtime_objs = [
+    Env.Object(
+        target=os.path.join(
+            verilator_runtime_build_dir,
+            os.path.splitext(os.path.basename(source))[0] + Env['OBJSUFFIX'],
+        ),
+        source=source,
+    )[0]
+    for source in verilator_runtime_src
+]
+
+src += Glob(os.path.join('verilated_dut_150', '*.cpp'))
+
+CPPPATH = [cwd, verilated_dut_dir, verilator_rt_dir]
+CPPDEFINES = ['VL_RT_THREAD']
+
+group = DefineGroup(
+    'Applications', src, depend=[''], CPPPATH=CPPPATH, CPPDEFINES=CPPDEFINES
+)
+
+objs = [group]
+objs += verilator_runtime_objs
+
+list = os.listdir(cwd)
+
+for item in list:
+    if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
+        objs = objs + SConscript(os.path.join(item, 'SConscript'))
+
+Return('objs')
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/main.cpp b/bsp/xiangshan-verilator-xiangshan/applications/main.cpp
new file mode 100644
index 00000000000..042146bdc79
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/main.cpp
@@ -0,0 +1,138 @@
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#include "Vdut_150.h"
+
+static inline void tick(Vdut_150 *dut, VerilatedContext *ctx) {
+    dut->clk = 0;
+    dut->eval();
+    ctx->timeInc(1);
+    dut->clk = 1;
+    dut->eval();
+    ctx->timeInc(1);
+}
+
+// g[3:1] packed into low 3 bits of dut->g (bit0=g[1], bit1=g[2], bit2=g[3])
+static inline uint8_t g_bus(Vdut_150 *dut) {
+    return static_cast(dut->g & 0x07u);
+}
+
+int main(int argc, char **argv) {
+    Verilated::commandArgs(argc, argv);
+    auto ctx = std::make_unique();
+    ctx->traceEverOn(false);
+    ctx->threads(1);
+    auto dut = std::make_unique(ctx.get());
+
+    enum { A = 0, B = 1, C = 2, D = 3 };
+    uint8_t state = A;
+
+    auto apply_reset = [&]() {
+        dut->resetn = 0;
+        dut->r = 0;
+        tick(dut.get(), ctx.get());
+        dut->resetn = 1;
+        state = A;
+    };
+
+    auto step = [&](uint8_t r_val, const char *ctx_str) {
+        uint8_t r = r_val & 0x07u;  // r[3:1] in bits [2:0]
+        dut->r = r;
+
+        // Model next_state with r[1:3] mapped to bits 0..2.
+        bool r1 = (r & 0x1u) != 0;
+        bool r2 = (r & 0x2u) != 0;
+        bool r3 = (r & 0x4u) != 0;
+
+        uint8_t next = state;
+        switch (state) {
+            case A:
+                if (r1)                        next = B;
+                else if (!r1 && r2)            next = C;
+                else if (!r1 && !r2 && r3)     next = D;
+                else                           next = A;
+                break;
+            case B:
+                next = r1 ? B : A;
+                break;
+            case C:
+                next = r2 ? C : A;
+                break;
+            case D:
+                next = r3 ? D : A;
+                break;
+        }
+
+        rt_kprintf("[TB] cycle time=%llu state=%u r=%u next=%u g_bus=%u (%s)\n",
+                   static_cast(ctx->time()), state, r, next,
+                   static_cast(g_bus(dut.get())),
+                   ctx_str);
+
+        tick(dut.get(), ctx.get());
+        state = next;
+
+        // Expected g bus: bit0 for B, bit1 for C, bit2 for D.
+        uint8_t g_exp = 0;
+        if (state == B) g_exp = 0x1u;
+        else if (state == C) g_exp = 0x2u;
+        else if (state == D) g_exp = 0x4u;
+
+        uint8_t g_act = g_bus(dut.get());
+        if (g_act != g_exp) {
+            std::cerr << "[TB] dut_150 failed (" << ctx_str << "): "
+                      << "state=" << int(state)
+                      << " r=0x" << std::hex << int(r)
+                      << " expected g=0x" << int(g_exp)
+                      << " got g=0x" << int(g_act) << std::dec << std::endl;
+            std::exit(EXIT_FAILURE);
+        }
+    };
+
+    // Scenario 1: exercise all branches in state A (r1, r2, r3, none).
+    apply_reset();                  // A
+    step(0x00u, "A->A_none");       // else
+    step(0x01u, "A->B_r1");         // if(r1)
+    // Now in B; release back to A
+    step(0x00u, "B->A_release_r1");
+
+    apply_reset();                  // A
+    step(0x02u, "A->C_r2");         // else if(~r1 & r2)
+    step(0x00u, "C->A_release_r2");
+
+    apply_reset();                  // A
+    step(0x04u, "A->D_r3");         // else if(~r1 & ~r2 & r3)
+    step(0x00u, "D->A_release_r3");
+
+    // Scenario 2: exercise B state's if/else.
+    apply_reset();                  // A
+    step(0x01u, "A->B_for_B");      // into B
+    step(0x01u, "B->B_hold_r1");    // if(r1) branch
+    step(0x00u, "B->A_else");       // else branch
+
+    // Scenario 3: C state's branches.
+    apply_reset();                  // A
+    step(0x02u, "A->C_for_C");
+    step(0x02u, "C->C_hold_r2");    // if(r2)
+    step(0x00u, "C->A_else_C");     // else
+
+    // Scenario 4: D state's branches.
+    apply_reset();                  // A
+    step(0x04u, "A->D_for_D");
+    step(0x04u, "D->D_hold_r3");    // if(r3)
+    step(0x00u, "D->A_else_D");     // else
+
+    std::cout << "[TB] dut_150 passed: fixed-priority arbiter full coverage" << std::endl;
+
+#if VM_COVERAGE
+    const char *covPath = std::getenv("VERILATOR_COV_FILE");
+    if (covPath == nullptr || covPath[0] == '\0') {
+        covPath = "coverage.dat";
+    }
+    VerilatedCov::write(covPath);
+#endif
+    return EXIT_SUCCESS;
+}
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/test/SConscript b/bsp/xiangshan-verilator-xiangshan/applications/test/SConscript
new file mode 100644
index 00000000000..2597249cbd9
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/test/SConscript
@@ -0,0 +1,17 @@
+from building import *
+
+cwd     = GetCurrentDir()
+src = Glob('*.c') + Glob('*.cpp')
+CPPPATH = [cwd]
+
+group = DefineGroup('Testcase', src, depend = [''], CPPPATH = CPPPATH)
+
+list = os.listdir(cwd)
+
+objs = [group]
+
+for item in list:
+    if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
+        objs = objs + SConscript(os.path.join(item, 'SConscript'))
+
+Return('objs')
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/test/test_vector/SConscript b/bsp/xiangshan-verilator-xiangshan/applications/test/test_vector/SConscript
new file mode 100644
index 00000000000..0827b048b28
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/test/test_vector/SConscript
@@ -0,0 +1,9 @@
+from building import *
+
+cwd     = GetCurrentDir()
+src = Glob('*.c') + Glob('*.cpp')
+CPPPATH = [cwd]
+
+group = DefineGroup('Vector', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/test/test_vector/test_vector.c b/bsp/xiangshan-verilator-xiangshan/applications/test/test_vector/test_vector.c
new file mode 100644
index 00000000000..50aae802480
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/test/test_vector/test_vector.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ */
+#include 
+#include 
+#include 
+#include 
+
+#if defined(RT_USING_UTEST) && defined(ENABLE_VECTOR)
+#include 
+#include 
+
+void rt_hw_vector_ctx_restore(void *buf);
+void rt_hw_vector_ctx_save(void *buf);
+
+/**
+ * ==============================================================
+ * TEST FEATURE
+ * Use libc `memcpy` which employing V extension codes
+ * to test V extension features
+ * ==============================================================
+ */
+static char *constant = "hello,it's a nice day and i'm happy to see you\n";
+#define ARR_SIZE 4096
+static char array[ARR_SIZE];
+
+static void test_feature(void)
+{
+    memcpy(array, constant, sizeof array);
+    char *src = constant;
+    char *dst = array;
+    int error = 0;
+
+    for (size_t i = 0; i < ARR_SIZE; i++)
+    {
+        if (src[i] != dst[i])
+        {
+            error = 1;
+            break;
+        }
+    }
+
+    uassert_false(error);
+}
+
+/**
+ * ==============================================================
+ * TEST CONTEXT SAVING
+ * Create 2 threads employing V extension, verify V states are
+ * not modified by each other
+ * ==============================================================
+ */
+#define TEST_THREAD 2
+#define VECTOR_CTX_BYTES (CTX_VECTOR_REG_NR * REGBYTES)
+void *ctx_vector[TEST_THREAD * 2];
+
+static rt_sem_t sem;
+
+void dump_frame(void *frame)
+{
+    uint64_t *content = frame;
+    for (size_t i = 0; i < VECTOR_CTX_BYTES / 8; i++)
+    {
+        rt_kprintf("%x ", content[i]);
+    }
+    rt_kprintf("\n");
+}
+
+static void vector_child(void *param)
+{
+    void **ctx = param;
+    uint64_t *reg = ctx[0];
+    uint64_t vtype;
+    uint64_t vl;
+
+    rt_sem_release(sem);
+
+    rt_hw_vector_ctx_restore(ctx[0]);
+
+    /* STAGE 2, save t2 context */
+    test_feature();
+
+    /**
+     * @brief vtype & vl will be modified after context saving,
+     * it's ok because it will be recover after context restoring
+     * We restore these states manually here.
+     */
+    asm volatile("csrr %0, vtype":"=r"(vtype));
+    asm volatile("csrr %0, vl":"=r"(vl));
+    rt_hw_vector_ctx_save(ctx[0]);
+
+    rt_memcpy(ctx[1], ctx[0], VECTOR_CTX_BYTES);
+
+    rt_thread_yield();
+
+    asm volatile("vsetvl x0, %0, %1"::"r"(vl), "r"(vtype));
+    rt_hw_vector_ctx_save(ctx[0]);
+
+    uassert_false(rt_memcmp(ctx[1], ctx[0], VECTOR_CTX_BYTES));
+}
+
+/**
+ * @brief Test if context save/restore codes work properly
+ */
+static void test_context()
+{
+    rt_thread_t child;
+    uint64_t vtype;
+    uint64_t vl;
+
+    for (size_t i = 0; i < TEST_THREAD; i++)
+    {
+        ctx_vector[i * 2] = calloc(VECTOR_CTX_BYTES, 1);
+        ctx_vector[i * 2 + 1] = calloc(VECTOR_CTX_BYTES, 1);
+    }
+    rt_hw_vector_ctx_restore(ctx_vector[0]);
+
+    child = rt_thread_create("test_vector_child", vector_child, &ctx_vector[2], 4096, 10, 20);
+
+    /* STAGE 1, save t1 context */
+    /* assuming that rt libc memcpy do not use vector instruction */
+    asm volatile("csrr %0, vtype":"=r"(vtype));
+    asm volatile("csrr %0, vl":"=r"(vl));
+    rt_hw_vector_ctx_save(ctx_vector[0]);
+
+    rt_memcpy(ctx_vector[1], ctx_vector[0], VECTOR_CTX_BYTES);
+
+    rt_thread_startup(child);
+    rt_sem_take(sem, 0);
+
+    /* STAGE 3, verify t1 context */
+    asm volatile("vsetvl x0, %0, %1"::"r"(vl), "r"(vtype));
+    rt_hw_vector_ctx_save(ctx_vector[0]);
+    uassert_false(rt_memcmp(ctx_vector[1], ctx_vector[0], VECTOR_CTX_BYTES));
+
+    rt_thread_yield();
+}
+
+/**
+ * ==============================================================
+ * TEST NO VECTOR raise error and recover
+ * ==============================================================
+ */
+
+static void test_no_vector()
+{
+    asm volatile ("li t0, 0x600\n"
+                "csrc sstatus, t0");
+    test_feature();
+    uassert_true(1);
+}
+
+static rt_err_t utest_tc_init(void)
+{
+    sem = rt_sem_create("test_ctx", 0, RT_IPC_FLAG_FIFO);
+    return RT_EOK;
+}
+
+static rt_err_t utest_tc_cleanup(void)
+{
+    rt_sem_delete(sem);
+    return RT_EOK;
+}
+
+static void testcase(void)
+{
+    UTEST_UNIT_RUN(test_feature);
+    UTEST_UNIT_RUN(test_context);
+    UTEST_UNIT_RUN(test_no_vector);
+}
+
+UTEST_TC_EXPORT(testcase, "testcases.libcpu.vector", utest_tc_init, utest_tc_cleanup, 10);
+#endif /* RT_USING_UTEST && ENABLE_VECTOR */
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/verilated_dut_150/.gitignore b/bsp/xiangshan-verilator-xiangshan/applications/verilated_dut_150/.gitignore
new file mode 100644
index 00000000000..d6b7ef32c84
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/verilated_dut_150/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/Kconfig b/bsp/xiangshan-verilator-xiangshan/driver/Kconfig
new file mode 100644
index 00000000000..e0df212a98b
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/driver/Kconfig
@@ -0,0 +1,43 @@
+menu "XiangShan configs"
+
+config BSP_USING_VIRTIO
+    bool "Using VirtIO"
+    default n
+    depends on RT_USING_DEVICE_OPS
+
+config BSP_USING_VIRTIO_BLK
+    bool "Using VirtIO BLK"
+    select RT_USING_VIRTIO
+    select RT_USING_VIRTIO_BLK
+    default n
+    depends on BSP_USING_VIRTIO
+
+config BSP_USING_VIRTIO_NET
+    bool "Using VirtIO NET"
+    select RT_USING_VIRTIO
+    select RT_USING_VIRTIO_NET
+    default n
+    depends on BSP_USING_VIRTIO
+
+config BSP_USING_VIRTIO_CONSOLE
+    bool "Using VirtIO Console"
+    select RT_USING_VIRTIO
+    select RT_USING_VIRTIO_CONSOLE
+    default n
+    depends on BSP_USING_VIRTIO
+
+config BSP_USING_VIRTIO_GPU
+    bool "Using VirtIO GPU"
+    select RT_USING_VIRTIO
+    select RT_USING_VIRTIO_GPU
+    default n
+    depends on BSP_USING_VIRTIO
+
+config BSP_USING_VIRTIO_INPUT
+    bool "Using VirtIO Input"
+    select RT_USING_VIRTIO
+    select RT_USING_VIRTIO_INPUT
+    default n
+    depends on BSP_USING_VIRTIO
+
+endmenu
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/SConscript b/bsp/xiangshan-verilator-xiangshan/driver/SConscript
new file mode 100644
index 00000000000..faea9c1bd9b
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/driver/SConscript
@@ -0,0 +1,19 @@
+# RT-Thread building script for component
+
+from building import *
+
+cwd     = GetCurrentDir()
+src     = Glob('*.c')
+CPPPATH = [cwd]
+
+group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH)
+
+objs = [group]
+
+list = os.listdir(cwd)
+
+for item in list:
+    if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
+        objs = objs + SConscript(os.path.join(item, 'SConscript'))
+
+Return('objs')
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/asm/sbiasm.h b/bsp/xiangshan-verilator-xiangshan/driver/asm/sbiasm.h
new file mode 100644
index 00000000000..4639fba68cf
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/driver/asm/sbiasm.h
@@ -0,0 +1,10 @@
+#ifndef _SBI_ASM_H
+#define _SBI_ASM_H
+
+.macro SBI_CALL which
+    li a7, \which
+    ecall
+    nop
+.endm
+
+#endif /* _SBI_ASM_H */
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/asm/sbidef.h b/bsp/xiangshan-verilator-xiangshan/driver/asm/sbidef.h
new file mode 100644
index 00000000000..5bcf58ade7c
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/driver/asm/sbidef.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2019-2020, Xim
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ */
+#ifndef _ASM_SBI_DEF_H
+#define _ASM_SBI_DEF_H
+
+#define SBI_SET_TIMER 0
+#define SBI_CONSOLE_PUTCHAR 1
+#define SBI_CONSOLE_GETCHAR 2
+#define SBI_CLEAR_IPI 3
+#define SBI_SEND_IPI 4
+#define SBI_REMOTE_FENCE_I 5
+#define SBI_REMOTE_SFENCE_VMA 6
+#define SBI_REMOTE_SFENCE_VMA_ASID 7
+#define SBI_SHUTDOWN 8
+
+#define SBI_CONSOLE_PUTSTR 9
+
+#define SBI_SD_WRITE 10
+#define SBI_SD_READ 11
+#define SBI_NET_WRITE 12
+#define SBI_NET_READ 13
+
+#endif /* _ASM_SBI_DEF_H */
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/board.c b/bsp/xiangshan-verilator-xiangshan/driver/board.c
new file mode 100644
index 00000000000..092244278ed
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/driver/board.c
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2021-01-30     lizhirui     first version
+ */
+
+#include 
+#include 
+#include 
+
+#include "board.h"
+#include "mm_aspace.h"
+#include "tick.h"
+
+#include "drv_uart.h"
+#include "encoding.h"
+#include "stack.h"
+#include "sbi.h"
+#include "riscv.h"
+#include "plic.h"
+#include "stack.h"
+
+#ifdef RT_USING_SMP
+#include "interrupt.h"
+#endif /* RT_USING_SMP */
+
+#ifdef RT_USING_SMART
+#include "riscv_mmu.h"
+#include "mmu.h"
+#include "page.h"
+#include "lwp_arch.h"
+
+rt_region_t init_page_region = {(rt_size_t)RT_HW_PAGE_START, (rt_size_t)RT_HW_PAGE_END};
+
+extern size_t MMUTable[];
+
+struct mem_desc platform_mem_desc[] = {
+    {KERNEL_VADDR_START, (rt_size_t)RT_HW_PAGE_END - 1, (rt_size_t)ARCH_MAP_FAILED, NORMAL_MEM},
+};
+
+#define NUM_MEM_DESC (sizeof(platform_mem_desc) / sizeof(platform_mem_desc[0]))
+
+#endif
+
+void primary_cpu_entry(void)
+{
+    /* disable global interrupt */
+    rt_hw_interrupt_disable();
+
+    entry();
+}
+
+#define IOREMAP_SIZE (1ul << 30)
+
+#ifndef ARCH_REMAP_KERNEL
+#define IOREMAP_VEND USER_VADDR_START
+#else
+#define IOREMAP_VEND 0ul
+#endif /* ARCH_REMAP_KERNEL */
+
+void rt_hw_board_init(void)
+{
+#ifdef RT_USING_SMART
+    /* init data structure */
+    rt_hw_mmu_map_init(&rt_kernel_space, (void *)(IOREMAP_VEND - IOREMAP_SIZE), IOREMAP_SIZE, (rt_size_t *)MMUTable, PV_OFFSET);
+
+    /* init page allocator */
+    rt_page_init(init_page_region);
+
+    /* setup region, and enable MMU */
+    rt_hw_mmu_setup(&rt_kernel_space, platform_mem_desc, NUM_MEM_DESC);
+#endif
+
+#ifdef RT_USING_HEAP
+    /* initialize memory system */
+    rt_system_heap_init(RT_HW_HEAP_BEGIN, RT_HW_HEAP_END);
+#endif
+
+    plic_init();
+
+    rt_hw_interrupt_init();
+
+    rt_hw_uart_init();
+
+#ifdef RT_USING_CONSOLE
+    /* set console device */
+    rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
+#endif /* RT_USING_CONSOLE */
+
+    rt_hw_tick_init();
+
+#ifdef RT_USING_SMP
+    /* ipi init */
+    rt_hw_ipi_init();
+#endif /* RT_USING_SMP */
+
+#ifdef RT_USING_COMPONENTS_INIT
+    rt_components_board_init();
+#endif
+
+#ifdef RT_USING_HEAP
+    rt_kprintf("heap: [0x%08x - 0x%08x]\n", (rt_ubase_t)RT_HW_HEAP_BEGIN, (rt_ubase_t)RT_HW_HEAP_END);
+#endif /* RT_USING_HEAP */
+}
+
+void rt_hw_cpu_reset(void)
+{
+    sbi_shutdown();
+
+    while (1)
+        ;
+}
+MSH_CMD_EXPORT_ALIAS(rt_hw_cpu_reset, reboot, reset machine);
+
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/board.h b/bsp/xiangshan-verilator-xiangshan/driver/board.h
new file mode 100644
index 00000000000..9c74ae6b267
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/driver/board.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2021-01-30     lizhirui     first version
+ */
+
+#ifndef BOARD_H__
+#define BOARD_H__
+
+#include 
+
+extern unsigned int __bss_start;
+extern unsigned int __bss_end;
+
+#ifndef RT_USING_SMART
+#define KERNEL_VADDR_START 0x0
+#endif
+
+#define VIRT64_SBI_MEMSZ (0x200000)
+
+#define RT_HW_HEAP_BEGIN ((void *)&__bss_end)
+#define RT_HW_HEAP_END   ((void *)(RT_HW_HEAP_BEGIN + 64 * 1024 * 1024))
+#define RT_HW_PAGE_START RT_HW_HEAP_END
+#define RT_HW_PAGE_END   ((void *)(KERNEL_VADDR_START + (128 * 1024 * 1024 - VIRT64_SBI_MEMSZ)))
+
+void rt_hw_board_init(void);
+void rt_init_user_mem(struct rt_thread *thread, const char *name,
+                      unsigned long *entry);
+
+#endif
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/drv_uart.c b/bsp/xiangshan-verilator-xiangshan/driver/drv_uart.c
new file mode 100644
index 00000000000..11755761e75
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/driver/drv_uart.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ */
+
+#include 
+#include 
+#include 
+
+#include "board.h"
+#include "drv_uart.h"
+
+#include 
+#ifdef RT_USING_SMART
+#include 
+#endif
+#include "sbi.h"
+
+struct device_uart
+{
+    rt_ubase_t hw_base;
+    rt_uint32_t irqno;
+};
+
+void *uart0_base = (void*)0x310b0000;
+struct rt_serial_device serial0;
+struct device_uart uart0;
+
+void uart_init(void)
+{
+    rt_uint32_t div = UART_REFERENCE_CLOCK / (UART_DEFAULT_BAUDRATE * 16);
+
+    write8_uart0(UART_IER, 0x00);
+    write8_uart0(UART_LCR, UART_LCR_BAUD_LATCH);
+
+    // LSB
+    write8_uart0(0, div & 0xff);
+    // MSB
+    write8_uart0((1 << 2), (div >> 8) & 0xff);
+
+    // set word length to 8 bits, no parity
+    write8_uart0(UART_LCR, UART_LCR_EIGHT_BITS);
+
+    write8_uart0(UART_FCR, UART_FCR_FIFO_ENABLE | UART_FCR_FIFO_CLEAR);
+
+    return;
+}
+
+static rt_err_t _uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
+{
+    return (RT_EOK);
+}
+
+static rt_err_t _uart_control(struct rt_serial_device *serial, int cmd, void *arg)
+{
+    struct device_uart *uart = (struct device_uart*)serial->parent.user_data;
+
+    switch (cmd)
+    {
+    case RT_DEVICE_CTRL_CLR_INT:
+        if ((size_t)arg == RT_DEVICE_FLAG_INT_RX)
+        {
+            rt_uint8_t value = read8_uart0(UART_IER);
+            write8_uart0(UART_IER, value & ~UART_IER_RX_ENABLE);
+        }
+        break;
+
+    case RT_DEVICE_CTRL_SET_INT:
+        if ((size_t)arg == RT_DEVICE_FLAG_INT_RX)
+        {
+            rt_uint8_t value = read8_uart0(UART_IER);
+            write8_uart0(UART_IER, value | UART_IER_RX_ENABLE);
+        }
+        break;
+    }
+
+    return (RT_EOK);
+}
+
+static int _uart_putc(struct rt_serial_device *serial, char c)
+{
+    struct device_uart *uart;
+    uart = (struct device_uart*)serial->parent.user_data;
+
+    // wait for Transmit Holding Empty to be set in LSR.
+    while((read8_uart0(UART_LSR) & UART_LSR_TX_IDLE) == 0)
+        ;
+    write8_uart0(UART_THR, c);
+
+    return (1);
+}
+
+static int _uart_getc(struct rt_serial_device *serial)
+{
+    struct device_uart *uart;
+    volatile rt_uint32_t lsr;
+    int ch = -1;
+
+    uart = (struct device_uart*)serial->parent.user_data;
+    lsr = read8_uart0(UART_LSR);
+
+    if (lsr & UART_LSR_RX_READY)
+    {
+        ch = read8_uart0(UART_RHR);
+    }
+    return ch;
+}
+
+const struct rt_uart_ops _uart_ops = {
+    _uart_configure,
+    _uart_control,
+    _uart_putc,
+    _uart_getc,
+    // TODO: add DMA support
+    RT_NULL};
+
+static void rt_hw_uart_isr(int irqno, void *param)
+{
+    rt_ubase_t level = rt_hw_interrupt_disable();
+
+    struct rt_serial_device *serial = (struct rt_serial_device *)param;
+
+    rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
+
+    rt_hw_interrupt_enable(level);
+}
+
+/*
+ * UART Initiation
+ */
+int rt_hw_uart_init(void)
+{
+    struct rt_serial_device *serial;
+    struct device_uart *uart;
+    struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
+#ifdef RT_USING_SMART
+    uart0_base = rt_ioremap(uart0_base, 4096);
+#endif
+    // register device
+    serial = &serial0;
+    uart = &uart0;
+
+    serial->ops = &_uart_ops;
+    serial->config = config;
+    serial->config.baud_rate = UART_DEFAULT_BAUDRATE;
+    uart->hw_base = (rt_ubase_t)uart0_base;
+    uart->irqno = 0x0a;
+
+    rt_hw_serial_register(serial,
+                          RT_CONSOLE_DEVICE_NAME,
+                          RT_DEVICE_FLAG_STREAM | RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
+                          uart);
+    rt_hw_interrupt_install(uart->irqno, rt_hw_uart_isr, serial, RT_CONSOLE_DEVICE_NAME);
+    rt_hw_interrupt_umask(uart->irqno);
+    return 0;
+}
+
+/* WEAK for SDK 0.5.6 */
+rt_weak void uart_debug_init(int uart_channel)
+{
+}
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/drv_uart.h b/bsp/xiangshan-verilator-xiangshan/driver/drv_uart.h
new file mode 100644
index 00000000000..d40295d8188
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/driver/drv_uart.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2006-2020, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ */
+
+#ifndef __DRV_UART_H__
+#define __DRV_UART_H__
+
+#include "riscv_io.h"
+
+/**
+ * uart ns16550a
+ * http://byterunner.com/16550.html
+ */
+
+/* TRANSMIT AND RECEIVE HOLDING REGISTER */
+#define UART_RHR 0
+#define UART_THR 0
+
+/* INTERRUPT ENABLE REGISTER */
+#define UART_IER (1 << 2)
+#define UART_IER_RX_ENABLE (1 << 0)
+#define UART_IER_TX_ENABLE (1 << 1)
+
+/* FIFO CONTROL REGISTER */
+#define UART_FCR (2 << 2)
+#define UART_FCR_FIFO_ENABLE (1 << 0)
+#define UART_FCR_FIFO_CLEAR (3 << 1)
+
+/* INTERRUPT STATUS REGISTER */
+#define UART_ISR (2 << 2)
+
+/* LINE CONTROL REGISTER */
+#define UART_LCR (3 << 2)
+#define UART_LCR_EIGHT_BITS (3 << 0)
+// special mode to set baud rate
+#define UART_LCR_BAUD_LATCH (1 << 7)
+
+/* LINE STATUS REGISTER */
+#define UART_LSR (5 << 2)
+// input is waiting to be read from RHR
+#define UART_LSR_RX_READY (1 << 0)
+// THR can accept another character to send
+#define UART_LSR_TX_IDLE (1 << 5)
+
+#define UART_REFERENCE_CLOCK  1843200
+#define UART_DEFAULT_BAUDRATE 115200
+
+extern void *uart0_base;
+
+#define write8_uart0(idx, value) __raw_writeb(((rt_uint8_t)value), (void*)((size_t)uart0_base + (idx)))
+#define read8_uart0(idx) __raw_readb((void*)((size_t)uart0_base + (idx)))
+
+void rt_hw_uart_start_rx_thread();
+int rt_hw_uart_init(void);
+void drv_uart_puts(char *str); // for syscall
+
+#endif /* __DRV_UART_H__ */
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/drv_virtio.c b/bsp/xiangshan-verilator-xiangshan/driver/drv_virtio.c
new file mode 100644
index 00000000000..e1289863b7e
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/driver/drv_virtio.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2021-11-11     GuEe-GUI     the first version
+ */
+
+#include 
+#include 
+
+#ifdef BSP_USING_VIRTIO
+
+#include 
+#ifdef BSP_USING_VIRTIO_BLK
+#include 
+#endif
+#ifdef BSP_USING_VIRTIO_NET
+#include 
+#endif
+#ifdef BSP_USING_VIRTIO_CONSOLE
+#include 
+#endif
+#ifdef BSP_USING_VIRTIO_GPU
+#include 
+#endif
+#ifdef BSP_USING_VIRTIO_INPUT
+#include 
+#endif
+
+#include 
+
+static virtio_device_init_handler virtio_device_init_handlers[] =
+{
+#ifdef BSP_USING_VIRTIO_BLK
+    [VIRTIO_DEVICE_ID_BLOCK]    = rt_virtio_blk_init,
+#endif
+#ifdef BSP_USING_VIRTIO_NET
+    [VIRTIO_DEVICE_ID_NET]      = rt_virtio_net_init,
+#endif
+#ifdef BSP_USING_VIRTIO_CONSOLE
+    [VIRTIO_DEVICE_ID_CONSOLE]  = rt_virtio_console_init,
+#endif
+#ifdef BSP_USING_VIRTIO_GPU
+    [VIRTIO_DEVICE_ID_GPU]      = rt_virtio_gpu_init,
+#endif
+#ifdef BSP_USING_VIRTIO_INPUT
+    [VIRTIO_DEVICE_ID_INPUT]    = rt_virtio_input_init,
+#endif
+    [VIRTIO_DEVICE_TYPE_SIZE]   = RT_NULL
+};
+
+int rt_virtio_devices_init(void)
+{
+    int i;
+    rt_uint32_t irq = VIRTIO_IRQ_BASE;
+    rt_ubase_t mmio_base = VIRTIO_MMIO_BASE;
+    struct virtio_mmio_config *mmio_config;
+    virtio_device_init_handler init_handler;
+
+    if (sizeof(virtio_device_init_handlers) == 0)
+    {
+        /* The compiler will optimize the codes after here. */
+        return 0;
+    }
+
+#ifdef RT_USING_SMART
+    mmio_base = (rt_ubase_t)rt_ioremap((void *)mmio_base, VIRTIO_MMIO_SIZE * VIRTIO_MAX_NR);
+
+    if (mmio_base == RT_NULL)
+    {
+        return -RT_ERROR;
+    }
+#endif
+
+    for (i = 0; i < VIRTIO_MAX_NR; ++i, ++irq, mmio_base += VIRTIO_MMIO_SIZE)
+    {
+        mmio_config = (struct virtio_mmio_config *)mmio_base;
+
+        if (mmio_config->magic != VIRTIO_MAGIC_VALUE ||
+            mmio_config->version != RT_USING_VIRTIO_VERSION ||
+            mmio_config->vendor_id != VIRTIO_VENDOR_ID)
+        {
+            continue;
+        }
+
+        init_handler = virtio_device_init_handlers[mmio_config->device_id];
+
+        if (init_handler != RT_NULL)
+        {
+            init_handler((rt_ubase_t *)mmio_base, irq);
+        }
+    }
+
+    return 0;
+}
+INIT_DEVICE_EXPORT(rt_virtio_devices_init);
+#endif  /* BSP_USING_VIRTIO */
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/drv_virtio.h b/bsp/xiangshan-verilator-xiangshan/driver/drv_virtio.h
new file mode 100644
index 00000000000..954338a3864
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/driver/drv_virtio.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2021-11-11     GuEe-GUI     the first version
+ */
+
+#ifndef __DRV_VIRTIO_H__
+#define __DRV_VIRTIO_H__
+
+int rt_virtio_devices_init(void);
+
+#endif /* __DRV_VIRTIO_H__ */
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/virt.h b/bsp/xiangshan-verilator-xiangshan/driver/virt.h
new file mode 100644
index 00000000000..f059feb50e6
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/driver/virt.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2021-02-17     GuEe-GUI     the first version
+ */
+
+#ifndef VIRT_H__
+#define VIRT_H__
+
+#include 
+
+#ifdef RT_USING_SMART
+#include 
+#include 
+
+#endif
+
+/* VirtIO */
+#define VIRTIO_MMIO_BASE    0x10001000
+#define VIRTIO_MMIO_SIZE    0x00001000
+#define VIRTIO_MAX_NR       8
+#define VIRTIO_IRQ_BASE     1
+#define VIRTIO_VENDOR_ID    0x554d4551  /* "QEMU" */
+
+#define MAX_HANDLERS        128
+#endif
diff --git a/bsp/xiangshan-verilator-xiangshan/link.lds b/bsp/xiangshan-verilator-xiangshan/link.lds
new file mode 100644
index 00000000000..da750aca9bb
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/link.lds
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2020/12/12     bernard      The first version
+ */
+
+INCLUDE "link_stacksize.lds"
+INCLUDE "link_cpus.lds"
+
+OUTPUT_ARCH( "riscv" )
+
+/*
+ * Memory layout:
+ * 0x80000000 - 0x80200000: SBI
+ * 0x80200000 - 0x81200000: Kernel
+ */
+
+MEMORY
+{
+   SRAM : ORIGIN = 0x80200000, LENGTH = 0x1000000
+}
+
+ENTRY(_start)
+SECTIONS
+{
+    . = 0x80200000 ;
+
+    /* __STACKSIZE__ = 4096; */
+    __text_start = .;
+    .start :
+    {
+        *(.start);
+    } > SRAM
+
+    . = ALIGN(8);
+
+    .text :
+    {
+        *(.text)                        /* remaining code */
+        *(.text.*)                      /* remaining code */
+        *(.rodata)                      /* read-only data (constants) */
+        *(.rodata*)
+        *(.glue_7)
+        *(.glue_7t)
+        *(.gnu.linkonce.t*)
+
+        /* section information for finsh shell */
+        . = ALIGN(8);
+        __fsymtab_start = .;
+        KEEP(*(FSymTab))
+        __fsymtab_end = .;
+        . = ALIGN(8);
+        __vsymtab_start = .;
+        KEEP(*(VSymTab))
+        __vsymtab_end = .;
+        . = ALIGN(8);
+
+        /* section information for initial. */
+        . = ALIGN(8);
+        __rt_init_start = .;
+        KEEP(*(SORT(.rti_fn*)))
+        __rt_init_end = .;
+        . = ALIGN(8);
+
+        __rt_utest_tc_tab_start = .;
+        KEEP(*(UtestTcTab))
+        __rt_utest_tc_tab_end = .;
+
+        . = ALIGN(8);
+        _etext = .;
+    } > SRAM
+
+    .eh_frame_hdr :
+    {
+         *(.eh_frame_hdr)
+         *(.eh_frame_entry)
+    } > SRAM
+    .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } > SRAM
+
+    . = ALIGN(8);
+    __text_end = .;
+    __text_size = __text_end - __text_start;
+
+    .data :
+    {
+        *(.data)
+        *(.data.*)
+
+        *(.data1)
+        *(.data1.*)
+
+        . = ALIGN(8);
+        PROVIDE( __global_pointer$ = . + 0x800 );
+
+        *(.sdata)
+        *(.sdata.*)
+    } > SRAM
+
+    . = ALIGN(8);
+    .ctors :
+    {
+        PROVIDE(__ctors_start__ = .);
+        KEEP(*(SORT(.init_array.*)))
+        KEEP(*(.init_array))
+        PROVIDE(__ctors_end__ = .);
+    } > SRAM
+
+    .dtors :
+    {
+        PROVIDE(__dtors_start__ = .);
+        KEEP(*(SORT(.fini_array.*)))
+        KEEP(*(.fini_array))
+        PROVIDE(__dtors_end__ = .);
+    } > SRAM
+
+    /* stack for dual core */
+    .stack :
+    {
+        . = ALIGN(64);
+        __stack_start__ = .;
+        /* Dynamically allocate stack areas according to RT_CPUS_NR */
+        . += (__STACKSIZE__ * RT_CPUS_NR);
+        __stack_end__ = .;
+    } > SRAM
+
+    .sbss :
+    {
+    __bss_start = .;
+        *(.sbss)
+        *(.sbss.*)
+        *(.dynsbss)
+        *(.scommon)
+    } > SRAM
+
+    .percpu (NOLOAD) :
+    {
+        /* 2MB Align for MMU early map */
+        . = ALIGN(0x200000);
+        PROVIDE(__percpu_start = .);
+
+        *(.percpu)
+
+        /* 2MB Align for MMU early map */
+        . = ALIGN(0x200000);
+
+        PROVIDE(__percpu_end = .);
+
+        /* Clone the area */
+        . = __percpu_end + (__percpu_end - __percpu_start) * (RT_CPUS_NR - 1);
+        PROVIDE(__percpu_real_end = .);
+    } > SRAM
+    
+    .bss :
+    {
+        *(.bss)
+        *(.bss.*)
+        *(.dynbss)
+        *(COMMON)
+    __bss_end = .;
+    } > SRAM
+
+    _end = .;
+
+    /* Stabs debugging sections.  */
+    .stab          0 : { *(.stab) }
+    .stabstr       0 : { *(.stabstr) }
+    .stab.excl     0 : { *(.stab.excl) }
+    .stab.exclstr  0 : { *(.stab.exclstr) }
+    .stab.index    0 : { *(.stab.index) }
+    .stab.indexstr 0 : { *(.stab.indexstr) }
+    .comment       0 : { *(.comment) }
+    /* DWARF debug sections.
+     * Symbols in the DWARF debugging sections are relative to the beginning
+     * of the section so we begin them at 0.  */
+    /* DWARF 1 */
+    .debug          0 : { *(.debug) }
+    .line           0 : { *(.line) }
+    /* GNU DWARF 1 extensions */
+    .debug_srcinfo  0 : { *(.debug_srcinfo) }
+    .debug_sfnames  0 : { *(.debug_sfnames) }
+    /* DWARF 1.1 and DWARF 2 */
+    .debug_aranges  0 : { *(.debug_aranges) }
+    .debug_pubnames 0 : { *(.debug_pubnames) }
+    /* DWARF 2 */
+    .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+    .debug_abbrev   0 : { *(.debug_abbrev) }
+    .debug_line     0 : { *(.debug_line) }
+    .debug_frame    0 : { *(.debug_frame) }
+    .debug_str      0 : { *(.debug_str) }
+    .debug_loc      0 : { *(.debug_loc) }
+    .debug_macinfo  0 : { *(.debug_macinfo) }
+    /* SGI/MIPS DWARF 2 extensions */
+    .debug_weaknames 0 : { *(.debug_weaknames) }
+    .debug_funcnames 0 : { *(.debug_funcnames) }
+    .debug_typenames 0 : { *(.debug_typenames) }
+    .debug_varnames  0 : { *(.debug_varnames) }
+}
diff --git a/bsp/xiangshan-verilator-xiangshan/link_cpus.lds b/bsp/xiangshan-verilator-xiangshan/link_cpus.lds
new file mode 100644
index 00000000000..e4cd5b88712
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/link_cpus.lds
@@ -0,0 +1 @@
+RT_CPUS_NR = 1;
diff --git a/bsp/xiangshan-verilator-xiangshan/link_smart.lds b/bsp/xiangshan-verilator-xiangshan/link_smart.lds
new file mode 100644
index 00000000000..29d33fdbb1c
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/link_smart.lds
@@ -0,0 +1,202 @@
+/*
+ * Copyright (c) 2006-2023, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2020/12/12     bernard      The first version
+ */
+
+INCLUDE "link_stacksize.lds"
+INCLUDE "link_cpus.lds"
+
+OUTPUT_ARCH( "riscv" )
+
+/*
+ * Memory layout:
+ * 0x80000000 - 0x80200000: SBI
+ * 0x80200000 - 0x81200000: Kernel
+ */
+
+MEMORY
+{
+   SRAM : ORIGIN = 0xFFFFFFC000200000, LENGTH = 0x1000000 - 0x200000
+}
+
+ENTRY(_start)
+SECTIONS
+{
+    /* . = 0x80200000 ; */
+    . = 0xFFFFFFC000200000;
+
+    /* __STACKSIZE__ = 4096; */
+    __text_start = .;
+    .start :
+    {
+        *(.start);
+    } > SRAM
+
+    . = ALIGN(8);
+
+    .text :
+    {
+        *(.text)                        /* remaining code */
+        *(.text.*)                      /* remaining code */
+        *(.rodata)                      /* read-only data (constants) */
+        *(.rodata*)
+        *(.glue_7)
+        *(.glue_7t)
+        *(.gnu.linkonce.t*)
+
+        /* section information for finsh shell */
+        . = ALIGN(8);
+        __fsymtab_start = .;
+        KEEP(*(FSymTab))
+        __fsymtab_end = .;
+        . = ALIGN(8);
+        __vsymtab_start = .;
+        KEEP(*(VSymTab))
+        __vsymtab_end = .;
+        . = ALIGN(8);
+
+        /* section information for initial. */
+        . = ALIGN(8);
+        __rt_init_start = .;
+        KEEP(*(SORT(.rti_fn*)))
+        __rt_init_end = .;
+        . = ALIGN(8);
+
+        __rt_utest_tc_tab_start = .;
+        KEEP(*(UtestTcTab))
+        __rt_utest_tc_tab_end = .;
+
+        . = ALIGN(8);
+        _etext = .;
+    } > SRAM
+
+    .eh_frame_hdr :
+    {
+         *(.eh_frame_hdr)
+         *(.eh_frame_entry)
+    } > SRAM
+    .eh_frame : ONLY_IF_RO { KEEP (*(.eh_frame)) } > SRAM
+
+    . = ALIGN(8);
+    __text_end = .;
+    __text_size = __text_end - __text_start;
+
+    .data :
+    {
+        *(.data)
+        *(.data.*)
+
+        *(.data1)
+        *(.data1.*)
+
+        . = ALIGN(8);
+        PROVIDE( __global_pointer$ = . + 0x800 );
+
+        *(.sdata)
+        *(.sdata.*)
+    } > SRAM
+
+    . = ALIGN(8);
+    .ctors :
+    {
+        PROVIDE(__ctors_start__ = .);
+        KEEP(*(SORT(.init_array.*)))
+        KEEP(*(.init_array))
+        PROVIDE(__ctors_end__ = .);
+    } > SRAM
+
+    .dtors :
+    {
+        PROVIDE(__dtors_start__ = .);
+        KEEP(*(SORT(.fini_array.*)))
+        KEEP(*(.fini_array))
+        PROVIDE(__dtors_end__ = .);
+    } > SRAM
+
+    /* stack for dual core */
+    .stack :
+    {
+        . = ALIGN(64);
+        __stack_start__ = .;
+        /* Dynamically allocate stack areas according to RT_CPUS_NR */
+        . += (__STACKSIZE__ * RT_CPUS_NR);
+        __stack_end__ = .;
+    } > SRAM
+
+    .sbss :
+    {
+    __bss_start = .;
+        *(.sbss)
+        *(.sbss.*)
+        *(.dynsbss)
+        *(.scommon)
+    } > SRAM
+
+    .percpu (NOLOAD) :
+    {
+        /* 2MB Align for MMU early map */
+        . = ALIGN(0x200000);
+        PROVIDE(__percpu_start = .);
+
+        *(.percpu)
+
+        /* 2MB Align for MMU early map */
+        . = ALIGN(0x200000);
+
+        PROVIDE(__percpu_end = .);
+
+        /* Clone the area */
+        . = __percpu_end + (__percpu_end - __percpu_start) * (RT_CPUS_NR - 1);
+        PROVIDE(__percpu_real_end = .);
+    } > SRAM
+
+    .bss :
+    {
+        *(.bss)
+        *(.bss.*)
+        *(.dynbss)
+        *(COMMON)
+    __bss_end = .;
+    } > SRAM
+
+    _end = .;
+
+    /* Stabs debugging sections.  */
+    .stab          0 : { *(.stab) }
+    .stabstr       0 : { *(.stabstr) }
+    .stab.excl     0 : { *(.stab.excl) }
+    .stab.exclstr  0 : { *(.stab.exclstr) }
+    .stab.index    0 : { *(.stab.index) }
+    .stab.indexstr 0 : { *(.stab.indexstr) }
+    .comment       0 : { *(.comment) }
+    /* DWARF debug sections.
+     * Symbols in the DWARF debugging sections are relative to the beginning
+     * of the section so we begin them at 0.  */
+    /* DWARF 1 */
+    .debug          0 : { *(.debug) }
+    .line           0 : { *(.line) }
+    /* GNU DWARF 1 extensions */
+    .debug_srcinfo  0 : { *(.debug_srcinfo) }
+    .debug_sfnames  0 : { *(.debug_sfnames) }
+    /* DWARF 1.1 and DWARF 2 */
+    .debug_aranges  0 : { *(.debug_aranges) }
+    .debug_pubnames 0 : { *(.debug_pubnames) }
+    /* DWARF 2 */
+    .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+    .debug_abbrev   0 : { *(.debug_abbrev) }
+    .debug_line     0 : { *(.debug_line) }
+    .debug_frame    0 : { *(.debug_frame) }
+    .debug_str      0 : { *(.debug_str) }
+    .debug_loc      0 : { *(.debug_loc) }
+    .debug_macinfo  0 : { *(.debug_macinfo) }
+    /* SGI/MIPS DWARF 2 extensions */
+    .debug_weaknames 0 : { *(.debug_weaknames) }
+    .debug_funcnames 0 : { *(.debug_funcnames) }
+    .debug_typenames 0 : { *(.debug_typenames) }
+    .debug_varnames  0 : { *(.debug_varnames) }
+}
diff --git a/bsp/xiangshan-verilator-xiangshan/link_stacksize.lds b/bsp/xiangshan-verilator-xiangshan/link_stacksize.lds
new file mode 100644
index 00000000000..14c2aad91f8
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/link_stacksize.lds
@@ -0,0 +1 @@
+__STACKSIZE__ = 16384;
diff --git a/bsp/xiangshan-verilator-xiangshan/qemu-dbg.sh b/bsp/xiangshan-verilator-xiangshan/qemu-dbg.sh
new file mode 100755
index 00000000000..69f62e7f6fb
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/qemu-dbg.sh
@@ -0,0 +1,16 @@
+QEMU_CMD="qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin -s -S"
+
+if grep -q "#define RT_USING_SMP" ./rtconfig.h 2>/dev/null; then
+    hart_num=$(grep "RT_CPUS_NR = [0-9]*;" ./link_cpus.lds | awk -F'[=;]' '{gsub(/ /, "", $2); print $2}')
+    if [ -z "$hart_num" ]; then
+        hart_num=1
+    fi
+    QEMU_CMD="$QEMU_CMD -smp $hart_num"
+fi
+
+QEMU_CMD="$QEMU_CMD \
+-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \
+-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 \
+-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0"
+
+eval $QEMU_CMD
\ No newline at end of file
diff --git a/bsp/xiangshan-verilator-xiangshan/qemu-dumpdtb.sh b/bsp/xiangshan-verilator-xiangshan/qemu-dumpdtb.sh
new file mode 100755
index 00000000000..12068b571a9
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/qemu-dumpdtb.sh
@@ -0,0 +1 @@
+qemu-system-riscv64 -nographic -machine virt,dumpdtb=virt.dtb -m 256M -kernel rtthread.bin
diff --git a/bsp/xiangshan-verilator-xiangshan/qemu-nographic.bat b/bsp/xiangshan-verilator-xiangshan/qemu-nographic.bat
new file mode 100644
index 00000000000..df55b35e84f
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/qemu-nographic.bat
@@ -0,0 +1,9 @@
+@echo off
+if exist sd.bin goto run
+qemu-img create -f raw sd.bin 64M
+
+:run
+qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin ^
+-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 ^
+-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 ^
+-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0
diff --git a/bsp/xiangshan-verilator-xiangshan/qemu-rv64ilp32-nographic.sh b/bsp/xiangshan-verilator-xiangshan/qemu-rv64ilp32-nographic.sh
new file mode 100755
index 00000000000..798cb9d3d70
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/qemu-rv64ilp32-nographic.sh
@@ -0,0 +1 @@
+qemu-system-riscv64ilp32  -cpu rv64 -M virt -m 256M -nographic -kernel rtthread.elf
diff --git a/bsp/xiangshan-verilator-xiangshan/qemu-v-dbg.sh b/bsp/xiangshan-verilator-xiangshan/qemu-v-dbg.sh
new file mode 100644
index 00000000000..d22af856cbf
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/qemu-v-dbg.sh
@@ -0,0 +1,4 @@
+qemu-system-riscv64 -nographic -machine virt -cpu rv64,v=true,vlen=128,vext_spec=v1.0 -m 256M -kernel rtthread.bin \
+-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \
+-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 -s -S \
+-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0
diff --git a/bsp/xiangshan-verilator-xiangshan/qemu-v-nographic.sh b/bsp/xiangshan-verilator-xiangshan/qemu-v-nographic.sh
new file mode 100644
index 00000000000..068ff4fa6c3
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/qemu-v-nographic.sh
@@ -0,0 +1,9 @@
+if [ ! -f "sd.bin" ]; then
+dd if=/dev/zero of=sd.bin bs=1024 count=65536
+mkfs.fat sd.bin
+fi
+
+qemu-system-riscv64 -nographic -machine virt -cpu rv64,v=true,vlen=128,vext_spec=v1.0 -m 256M -kernel rtthread.bin \
+-drive if=none,file=sd.bin,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \
+-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 \
+-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0
diff --git a/bsp/xiangshan-verilator-xiangshan/rtconfig.h b/bsp/xiangshan-verilator-xiangshan/rtconfig.h
new file mode 100644
index 00000000000..d63d57ca77b
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/rtconfig.h
@@ -0,0 +1,557 @@
+#ifndef RT_CONFIG_H__
+#define RT_CONFIG_H__
+
+/* RT-Thread Kernel */
+
+/* klibc options */
+
+/* rt_vsnprintf options */
+
+#define RT_KLIBC_USING_VSNPRINTF_LONGLONG
+#define RT_KLIBC_USING_VSNPRINTF_STANDARD
+#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_SPECIFIERS
+#define RT_KLIBC_USING_VSNPRINTF_EXPONENTIAL_SPECIFIERS
+#define RT_KLIBC_USING_VSNPRINTF_WRITEBACK_SPECIFIER
+#define RT_KLIBC_USING_VSNPRINTF_CHECK_NUL_IN_FORMAT_SPECIFIER
+#define RT_KLIBC_USING_VSNPRINTF_INTEGER_BUFFER_SIZE 32
+#define RT_KLIBC_USING_VSNPRINTF_DECIMAL_BUFFER_SIZE 32
+#define RT_KLIBC_USING_VSNPRINTF_FLOAT_PRECISION 6
+#define RT_KLIBC_USING_VSNPRINTF_MAX_INTEGRAL_DIGITS_FOR_DECIMAL 9
+#define RT_KLIBC_USING_VSNPRINTF_LOG10_TAYLOR_TERMS 4
+/* end of rt_vsnprintf options */
+
+/* rt_vsscanf options */
+
+/* end of rt_vsscanf options */
+
+/* rt_memset options */
+
+/* end of rt_memset options */
+
+/* rt_memcpy options */
+
+/* end of rt_memcpy options */
+
+/* rt_memmove options */
+
+/* end of rt_memmove options */
+
+/* rt_memcmp options */
+
+/* end of rt_memcmp options */
+
+/* rt_strstr options */
+
+/* end of rt_strstr options */
+
+/* rt_strcasecmp options */
+
+/* end of rt_strcasecmp options */
+
+/* rt_strncpy options */
+
+/* end of rt_strncpy options */
+
+/* rt_strcpy options */
+
+/* end of rt_strcpy options */
+
+/* rt_strncmp options */
+
+/* end of rt_strncmp options */
+
+/* rt_strcmp options */
+
+/* end of rt_strcmp options */
+
+/* rt_strlen options */
+
+/* end of rt_strlen options */
+
+/* rt_strnlen options */
+
+/* end of rt_strnlen options */
+/* end of klibc options */
+#define RT_NAME_MAX 24
+#define RT_CPUS_NR 1
+#define RT_ALIGN_SIZE 8
+#define RT_THREAD_PRIORITY_32
+#define RT_THREAD_PRIORITY_MAX 32
+#define RT_TICK_PER_SECOND 100
+#define RT_USING_OVERFLOW_CHECK
+#define RT_USING_HOOK
+#define RT_HOOK_USING_FUNC_PTR
+#define RT_USING_IDLE_HOOK
+#define RT_IDLE_HOOK_LIST_SIZE 4
+#define IDLE_THREAD_STACK_SIZE 16384
+#define RT_USING_TIMER_SOFT
+#define RT_TIMER_THREAD_PRIO 4
+#define RT_TIMER_THREAD_STACK_SIZE 16384
+#define RT_USING_CPU_USAGE_TRACER
+
+/* kservice options */
+
+/* end of kservice options */
+#define RT_USING_DEBUG
+#define RT_DEBUGING_ASSERT
+#define RT_DEBUGING_COLOR
+#define RT_DEBUGING_CONTEXT
+
+/* Inter-Thread communication */
+
+#define RT_USING_SEMAPHORE
+#define RT_USING_MUTEX
+#define RT_USING_EVENT
+#define RT_USING_MAILBOX
+#define RT_USING_MESSAGEQUEUE
+#define RT_USING_SIGNALS
+/* end of Inter-Thread communication */
+
+/* Memory Management */
+
+#define RT_USING_MEMPOOL
+#define RT_USING_SLAB
+#define RT_USING_SLAB_AS_HEAP
+#define RT_USING_MEMTRACE
+#define RT_USING_HEAP
+/* end of Memory Management */
+#define RT_USING_DEVICE
+#define RT_USING_DEVICE_OPS
+#define RT_USING_CONSOLE
+#define RT_CONSOLEBUF_SIZE 256
+#define RT_CONSOLE_DEVICE_NAME "uart0"
+#define RT_VER_NUM 0x50300
+#define RT_BACKTRACE_LEVEL_MAX_NR 32
+/* end of RT-Thread Kernel */
+#define ARCH_CPU_64BIT
+#define RT_USING_CACHE
+#define ARCH_MM_MMU
+#define ARCH_RISCV
+#define ARCH_RISCV64
+#define ARCH_USING_NEW_CTX_SWITCH
+#define ARCH_USING_RISCV_COMMON64
+#define ARCH_REMAP_KERNEL
+
+/* RT-Thread Components */
+
+#define RT_USING_COMPONENTS_INIT
+#define RT_USING_USER_MAIN
+#define RT_MAIN_THREAD_STACK_SIZE 8388608
+#define RT_MAIN_THREAD_PRIORITY 10
+#define RT_USING_MSH
+#define RT_USING_FINSH
+#define FINSH_USING_MSH
+#define FINSH_THREAD_NAME "tshell"
+#define FINSH_THREAD_PRIORITY 20
+#define FINSH_THREAD_STACK_SIZE 16384
+#define FINSH_USING_HISTORY
+#define FINSH_HISTORY_LINES 10
+#define FINSH_USING_SYMTAB
+#define FINSH_CMD_SIZE 80
+#define MSH_USING_BUILT_IN_COMMANDS
+#define FINSH_USING_DESCRIPTION
+#define FINSH_ARG_MAX 10
+#define FINSH_USING_OPTION_COMPLETION
+
+/* DFS: device virtual file system */
+
+#define RT_USING_DFS
+#define DFS_USING_POSIX
+#define DFS_USING_WORKDIR
+#define DFS_FD_MAX 32
+#define RT_USING_DFS_V2
+#define RT_USING_DFS_ELMFAT
+
+/* elm-chan's FatFs, Generic FAT Filesystem Module */
+
+#define RT_DFS_ELM_CODE_PAGE 437
+#define RT_DFS_ELM_WORD_ACCESS
+#define RT_DFS_ELM_USE_LFN_3
+#define RT_DFS_ELM_USE_LFN 3
+#define RT_DFS_ELM_LFN_UNICODE_0
+#define RT_DFS_ELM_LFN_UNICODE 0
+#define RT_DFS_ELM_MAX_LFN 255
+#define RT_DFS_ELM_DRIVES 2
+#define RT_DFS_ELM_MAX_SECTOR_SIZE 512
+#define RT_DFS_ELM_REENTRANT
+#define RT_DFS_ELM_MUTEX_TIMEOUT 3000
+/* end of elm-chan's FatFs, Generic FAT Filesystem Module */
+#define RT_USING_DFS_DEVFS
+#define RT_USING_DFS_ROMFS
+/* end of DFS: device virtual file system */
+
+/* Device Drivers */
+
+#define RT_USING_DEVICE_IPC
+#define RT_UNAMED_PIPE_NUMBER 64
+#define RT_USING_SYSTEM_WORKQUEUE
+#define RT_SYSTEM_WORKQUEUE_STACKSIZE 8192
+#define RT_SYSTEM_WORKQUEUE_PRIORITY 23
+#define RT_USING_SERIAL
+#define RT_USING_SERIAL_V1
+#define RT_SERIAL_USING_DMA
+#define RT_SERIAL_RB_BUFSZ 64
+#define RT_USING_CPUTIME
+#define RT_USING_CPUTIME_RISCV
+#define CPUTIME_TIMER_FREQ 10000000
+#define RT_USING_NULL
+#define RT_USING_ZERO
+#define RT_USING_RANDOM
+#define RT_USING_RTC
+#define RT_USING_SOFT_RTC
+#define RT_USING_KTIME
+/* end of Device Drivers */
+
+/* C/C++ and POSIX layer */
+
+/* ISO-ANSI C layer */
+
+/* Timezone and Daylight Saving Time */
+
+#define RT_LIBC_USING_LIGHT_TZ_DST
+#define RT_LIBC_TZ_DEFAULT_HOUR 8
+#define RT_LIBC_TZ_DEFAULT_MIN 0
+#define RT_LIBC_TZ_DEFAULT_SEC 0
+/* end of Timezone and Daylight Saving Time */
+/* end of ISO-ANSI C layer */
+
+/* POSIX (Portable Operating System Interface) layer */
+
+#define RT_USING_POSIX_FS
+#define RT_USING_POSIX_DEVIO
+#define RT_USING_POSIX_STDIO
+#define RT_USING_POSIX_POLL
+#define RT_USING_POSIX_SELECT
+#define RT_USING_POSIX_TERMIOS
+#define RT_USING_POSIX_AIO
+#define RT_USING_POSIX_MMAN
+#define RT_USING_POSIX_DELAY
+#define RT_USING_POSIX_CLOCK
+#define RT_USING_POSIX_TIMER
+#define RT_USING_PTHREADS
+#define PTHREAD_NUM_MAX 8
+
+/* Interprocess Communication (IPC) */
+
+#define RT_USING_POSIX_PIPE
+#define RT_USING_POSIX_PIPE_SIZE 512
+
+/* Socket is in the 'Network' category */
+
+/* end of Interprocess Communication (IPC) */
+/* end of POSIX (Portable Operating System Interface) layer */
+#define RT_USING_CPLUSPLUS
+#define RT_USING_CPLUSPLUS11
+#define RT_USING_CPP_WRAPPER
+/* end of C/C++ and POSIX layer */
+
+/* Network */
+
+#define RT_USING_SAL
+#define SAL_INTERNET_CHECK
+#define SOCKET_TABLE_STEP_LEN 4
+
+/* Docking with protocol stacks */
+
+#define SAL_USING_LWIP
+/* end of Docking with protocol stacks */
+#define SAL_USING_POSIX
+#define RT_USING_NETDEV
+#define NETDEV_USING_IFCONFIG
+#define NETDEV_USING_PING
+#define NETDEV_USING_NETSTAT
+#define NETDEV_USING_AUTO_DEFAULT
+#define NETDEV_IPV4 1
+#define NETDEV_IPV6 0
+#define RT_USING_LWIP
+#define RT_USING_LWIP203
+#define RT_USING_LWIP_VER_NUM 0x20003
+#define RT_LWIP_MEM_ALIGNMENT 4
+#define RT_LWIP_IGMP
+#define RT_LWIP_ICMP
+#define RT_LWIP_DNS
+#define RT_LWIP_DHCP
+#define IP_SOF_BROADCAST 1
+#define IP_SOF_BROADCAST_RECV 1
+
+/* Static IPv4 Address */
+
+#define RT_LWIP_IPADDR "192.168.1.30"
+#define RT_LWIP_GWADDR "192.168.1.1"
+#define RT_LWIP_MSKADDR "255.255.255.0"
+/* end of Static IPv4 Address */
+#define RT_LWIP_UDP
+#define RT_LWIP_TCP
+#define RT_LWIP_RAW
+#define RT_MEMP_NUM_NETCONN 8
+#define RT_LWIP_PBUF_NUM 16
+#define RT_LWIP_RAW_PCB_NUM 4
+#define RT_LWIP_UDP_PCB_NUM 4
+#define RT_LWIP_TCP_PCB_NUM 4
+#define RT_LWIP_TCP_SEG_NUM 40
+#define RT_LWIP_TCP_SND_BUF 8196
+#define RT_LWIP_TCP_WND 8196
+#define RT_LWIP_TCPTHREAD_PRIORITY 10
+#define RT_LWIP_TCPTHREAD_MBOX_SIZE 8
+#define RT_LWIP_TCPTHREAD_STACKSIZE 8192
+#define RT_LWIP_ETHTHREAD_PRIORITY 12
+#define RT_LWIP_ETHTHREAD_STACKSIZE 8192
+#define RT_LWIP_ETHTHREAD_MBOX_SIZE 8
+#define LWIP_NETIF_STATUS_CALLBACK 1
+#define LWIP_NETIF_LINK_CALLBACK 1
+#define RT_LWIP_NETIF_NAMESIZE 6
+#define SO_REUSE 1
+#define LWIP_SO_RCVTIMEO 1
+#define LWIP_SO_SNDTIMEO 1
+#define LWIP_SO_RCVBUF 1
+#define LWIP_SO_LINGER 0
+#define LWIP_NETIF_LOOPBACK 0
+#define RT_LWIP_USING_PING
+/* end of Network */
+
+/* Memory protection */
+
+/* end of Memory protection */
+
+/* Utilities */
+
+#define RT_USING_UTEST
+#define UTEST_THR_STACK_SIZE 4096
+#define UTEST_THR_PRIORITY 20
+#define RT_UTEST_MAX_OPTIONS 64
+#define RT_USING_RESOURCE_ID
+#define RT_USING_ADT
+#define RT_USING_ADT_AVL
+#define RT_USING_ADT_BITMAP
+#define RT_USING_ADT_HASHMAP
+#define RT_USING_ADT_REF
+/* end of Utilities */
+
+/* Memory management */
+
+#define RT_PAGE_AFFINITY_BLOCK_SIZE 0x1000
+#define RT_PAGE_MAX_ORDER 11
+
+/* Debugging */
+
+/* end of Debugging */
+/* end of Memory management */
+
+/* Using USB legacy version */
+
+/* end of Using USB legacy version */
+/* end of RT-Thread Components */
+
+/* RT-Thread Utestcases */
+
+/* end of RT-Thread Utestcases */
+
+/* RT-Thread online packages */
+
+/* IoT - internet of things */
+
+
+/* Wi-Fi */
+
+/* Marvell WiFi */
+
+/* end of Marvell WiFi */
+
+/* Wiced WiFi */
+
+/* end of Wiced WiFi */
+
+/* CYW43012 WiFi */
+
+/* end of CYW43012 WiFi */
+
+/* BL808 WiFi */
+
+/* end of BL808 WiFi */
+
+/* CYW43439 WiFi */
+
+/* end of CYW43439 WiFi */
+/* end of Wi-Fi */
+
+/* IoT Cloud */
+
+/* end of IoT Cloud */
+/* end of IoT - internet of things */
+
+/* security packages */
+
+/* end of security packages */
+
+/* language packages */
+
+/* JSON: JavaScript Object Notation, a lightweight data-interchange format */
+
+/* end of JSON: JavaScript Object Notation, a lightweight data-interchange format */
+
+/* XML: Extensible Markup Language */
+
+/* end of XML: Extensible Markup Language */
+/* end of language packages */
+
+/* multimedia packages */
+
+/* LVGL: powerful and easy-to-use embedded GUI library */
+
+/* end of LVGL: powerful and easy-to-use embedded GUI library */
+
+/* u8g2: a monochrome graphic library */
+
+/* end of u8g2: a monochrome graphic library */
+/* end of multimedia packages */
+
+/* tools packages */
+
+/* end of tools packages */
+
+/* system packages */
+
+/* enhanced kernel services */
+
+/* end of enhanced kernel services */
+
+/* acceleration: Assembly language or algorithmic acceleration packages */
+
+/* end of acceleration: Assembly language or algorithmic acceleration packages */
+
+/* CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */
+
+/* end of CMSIS: ARM Cortex-M Microcontroller Software Interface Standard */
+
+/* Micrium: Micrium software products porting for RT-Thread */
+
+/* end of Micrium: Micrium software products porting for RT-Thread */
+/* end of system packages */
+
+/* peripheral libraries and drivers */
+
+/* HAL & SDK Drivers */
+
+/* STM32 HAL & SDK Drivers */
+
+/* end of STM32 HAL & SDK Drivers */
+
+/* Infineon HAL Packages */
+
+/* end of Infineon HAL Packages */
+
+/* Kendryte SDK */
+
+/* end of Kendryte SDK */
+
+/* WCH HAL & SDK Drivers */
+
+/* end of WCH HAL & SDK Drivers */
+
+/* AT32 HAL & SDK Drivers */
+
+/* end of AT32 HAL & SDK Drivers */
+
+/* HC32 DDL Drivers */
+
+/* end of HC32 DDL Drivers */
+
+/* NXP HAL & SDK Drivers */
+
+/* end of NXP HAL & SDK Drivers */
+
+/* NUVOTON Drivers */
+
+/* end of NUVOTON Drivers */
+
+/* GD32 Drivers */
+
+/* end of GD32 Drivers */
+/* end of HAL & SDK Drivers */
+
+/* sensors drivers */
+
+/* end of sensors drivers */
+
+/* touch drivers */
+
+/* end of touch drivers */
+/* end of peripheral libraries and drivers */
+
+/* AI packages */
+
+/* end of AI packages */
+
+/* Signal Processing and Control Algorithm Packages */
+
+/* end of Signal Processing and Control Algorithm Packages */
+
+/* miscellaneous packages */
+
+/* project laboratory */
+
+/* end of project laboratory */
+
+/* samples: kernel and components samples */
+
+/* end of samples: kernel and components samples */
+
+/* entertainment: terminal games and other interesting software packages */
+
+/* end of entertainment: terminal games and other interesting software packages */
+/* end of miscellaneous packages */
+
+/* Arduino libraries */
+
+
+/* Projects and Demos */
+
+/* end of Projects and Demos */
+
+/* Sensors */
+
+/* end of Sensors */
+
+/* Display */
+
+/* end of Display */
+
+/* Timing */
+
+/* end of Timing */
+
+/* Data Processing */
+
+/* end of Data Processing */
+
+/* Data Storage */
+
+/* Communication */
+
+/* end of Communication */
+
+/* Device Control */
+
+/* end of Device Control */
+
+/* Other */
+
+/* end of Other */
+
+/* Signal IO */
+
+/* end of Signal IO */
+
+/* Uncategorized */
+
+/* end of Arduino libraries */
+/* end of RT-Thread online packages */
+
+/* XiangShan configs */
+
+/* end of XiangShan configs */
+#define BOARD_XIANGSHAN
+#define PLIC_BASE 0x3c000000
+#define __STACKSIZE__ 16384
+
+#endif
diff --git a/bsp/xiangshan-verilator-xiangshan/rtconfig.py b/bsp/xiangshan-verilator-xiangshan/rtconfig.py
new file mode 100644
index 00000000000..4f9e16b5f89
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/rtconfig.py
@@ -0,0 +1,52 @@
+import os
+
+# toolchains options
+ARCH        ='risc-v'
+CPU         ='virt64'
+CROSS_TOOL  ='llvm-riscv'
+
+RTT_ROOT = os.getenv('RTT_ROOT') or os.path.join(os.getcwd(), '..', '..')
+
+if os.getenv('RTT_CC'):
+    CROSS_TOOL = os.getenv('RTT_CC')
+
+if  CROSS_TOOL == 'llvm-riscv':
+    PLATFORM    = 'llvm-riscv'
+    EXEC_PATH   = os.getenv('RTT_EXEC_PATH') or '/usr/bin'
+else:
+    print('Please make sure your toolchains is LLVM RISC-V!')
+    exit(0)
+
+BUILD = 'release'
+
+if PLATFORM == 'llvm-riscv':
+    # toolchains
+    PREFIX  = os.getenv('RTT_CC_PREFIX') or 'riscv64-unknown-elf-'
+    CC      = PREFIX + 'clang'
+    CXX     = PREFIX + 'clang++'
+    AS      = PREFIX + 'clang'
+    AR      = PREFIX + 'llvm-ar'
+    LINK    = PREFIX + 'clang++'
+    TARGET_EXT = 'elf'
+    SIZE    = PREFIX + 'size'
+    OBJDUMP = PREFIX + 'objdump'
+    OBJCPY  = PREFIX + 'objcopy'
+
+    DEVICE  = ' -mcmodel=medany -march=rv64imac -mabi=lp64 '
+    # Enable emulated TLS
+    CFLAGS  = DEVICE + '-fno-omit-frame-pointer -flax-vector-conversions -Wno-cpp -fno-common -ffunction-sections -fdata-sections -fdiagnostics-color=always -Xclang -fexperimental-max-bitint-width=20000 -fbracket-depth=2048 -Wno-parentheses-equality -DGSIM -femulated-tls'
+    AFLAGS  = ' -c' + DEVICE + ' -x assembler-with-cpp -D__ASSEMBLY__ '
+    LFLAGS  = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,_start -T link.lds' + ' -stdlib=libstdc++ -lc -lsupc++ -lgcc -lstdc++ -static'
+    CPATH   = ''
+    LPATH   = ''
+
+    if BUILD == 'debug':
+        CFLAGS += ' -O0 -ggdb '
+        AFLAGS += ' -ggdb'
+    else:
+        CFLAGS += ' -O3'
+
+    CXXFLAGS = CFLAGS
+
+DUMP_ACTION = OBJDUMP + ' -D -S $TARGET > rtthread.asm\n'
+POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread.bin\n' + SIZE + ' $TARGET \n'
diff --git a/bsp/xiangshan-verilator-xiangshan/run.sh b/bsp/xiangshan-verilator-xiangshan/run.sh
new file mode 100755
index 00000000000..c332915098c
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/run.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+usage ()
+{
+	echo "Usage:"
+	echo "./run.sh []"
+	echo "Note: if  is not provided, will create a 'sd.bin'"
+	echo "in the current directory and load it by default."
+}
+
+path_image=${1}
+
+if [ -z $path_image ]; then
+	path_image="./sd.bin"
+	if [ ! -f $path_image ]; then
+		dd if=/dev/zero of=$path_image bs=1024 count=65536
+		mkfs.fat $path_image
+	fi
+fi
+
+if [ ! -f $path_image ]; then
+	echo "ERROR: $path_image does not exist!"
+	usage
+	exit
+fi
+
+QEMU_CMD="qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin"
+
+if grep -q "#define RT_USING_SMP" ./rtconfig.h 2>/dev/null; then
+    hart_num=$(grep "RT_CPUS_NR = [0-9]*;" ./link_cpus.lds 2>/dev/null | awk -F'[=;]' '{gsub(/ /, "", $2); print $2}')
+    if [ -z "$hart_num" ] || [ "$hart_num" -lt 1 ]; then
+        echo "Warning: Invalid or missing RT_CPUS_NR, defaulting to 1"
+        hart_num=1
+    fi
+    QEMU_CMD="$QEMU_CMD -smp $hart_num"
+fi
+
+QEMU_CMD="$QEMU_CMD \
+-drive if=none,file=$path_image,format=raw,id=blk0 -device virtio-blk-device,drive=blk0,bus=virtio-mmio-bus.0 \
+-netdev user,id=tap0 -device virtio-net-device,netdev=tap0,bus=virtio-mmio-bus.1 \
+-device virtio-serial-device -chardev socket,host=127.0.0.1,port=4321,server=on,wait=off,telnet=on,id=console0 -device virtserialport,chardev=console0"
+
+eval $QEMU_CMD
\ No newline at end of file
diff --git a/bsp/xiangshan-verilator-xiangshan/smart-env.bat b/bsp/xiangshan-verilator-xiangshan/smart-env.bat
new file mode 100644
index 00000000000..87c310560fc
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/smart-env.bat
@@ -0,0 +1,30 @@
+
+@set def_arch=arm
+
+@if not "%1"=="" (
+    @set def_arch=%1
+)
+
+@if %def_arch%==arm (
+    @set RTT_CC=gcc
+    @set RTT_EXEC_PATH=%cd%\tools\gnu_gcc\arm-linux-musleabi_for_i686-w64-mingw32\bin
+    @set RTT_CC_PREFIX=arm-linux-musleabi-
+    @copy configs\def_config_arm .config
+) else if %def_arch%==riscv64 (
+    @set RTT_CC=gcc
+    @set RTT_EXEC_PATH=E:\workspace\rt-smart\userapps\tools\gnu_gcc\riscv64-linux-musleabi_for_i686-w64-mingw32\bin
+    @set RTT_CC_PREFIX=riscv64-unknown-linux-musl-
+    @copy configs\def_config_riscv64 .config
+) else (
+    @echo "ERROR:supported_arch=arm riscv64!"
+    @goto EXIT
+)
+
+@set PATH=%RTT_EXEC_PATH%;%PATH%
+
+@echo "Arch      => %def_arch%"
+@echo "CC        => %RTT_CC%"
+@echo "PREFIX    => %RTT_CC_PREFIX%"
+@echo "EXEC_PATH => %RTT_EXEC_PATH%"
+
+:EXIT
\ No newline at end of file
diff --git a/bsp/xiangshan-verilator-xiangshan/verilator-case/Makefile b/bsp/xiangshan-verilator-xiangshan/verilator-case/Makefile
new file mode 100644
index 00000000000..3a481ccc60d
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/verilator-case/Makefile
@@ -0,0 +1,24 @@
+VERILATOR ?= verilator
+TOP       := dut_150
+TB        := tb_150.cpp
+SRC       := $(TOP).v
+APP_DIR   := ../applications
+OUT_DIR   := $(APP_DIR)/verilated_$(TOP)
+
+# Keep the generated model strictly single-threaded and avoid features
+# that pull in timing/coroutine support which we trimmed from the runtime.
+VERILATOR_FLAGS := --no-timing --no-trace --threads 1
+# Propagate our RT-Thread specific switches into generated code.
+VERILATOR_CFLAGS := -DVL_RT_THREAD -DVL_MT_DISABLED
+
+.PHONY: all clean
+
+all: $(OUT_DIR)/V$(TOP).cpp
+
+$(OUT_DIR)/V$(TOP).cpp: $(SRC) $(TB) Makefile
+	@mkdir -p $(OUT_DIR)
+	$(VERILATOR) -Wall --cc $(SRC) --exe $(TB) -Mdir $(OUT_DIR) \
+		$(VERILATOR_FLAGS) --CFLAGS "$(VERILATOR_CFLAGS)"
+
+clean:
+	@rm -rf $(OUT_DIR)
diff --git a/bsp/xiangshan-verilator-xiangshan/verilator-case/dut_150.v b/bsp/xiangshan-verilator-xiangshan/verilator-case/dut_150.v
new file mode 100644
index 00000000000..6030c2b9d5e
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/verilator-case/dut_150.v
@@ -0,0 +1,46 @@
+module dut_150 (
+    input clk,
+    input resetn,    // active-low synchronous reset
+    input [3:1] r,   // request
+    output [3:1] g   // grant
+  ); 
+
+    parameter a=2'd0, b=2'd1, c=2'd2, d=2'd3;
+    reg [1:0] state, next_state;
+    
+    always@(*) begin
+        case(state)
+            a: begin
+                if(r[1])    next_state = b;
+                else if(~r[1] & r[2])   next_state = c;
+                else if(~r[1] & ~r[2] & r[3])   next_state = d;
+                else    next_state = a;
+            end
+            b: begin
+                if(r[1])    next_state = b;
+                else        next_state = a;
+            end
+            c: begin
+                if(r[2])    next_state = c;
+                else        next_state = a;
+            end
+            d: begin
+                if(r[3])    next_state = d;
+                else        next_state = a;
+            end
+        endcase
+    end
+
+    always@(posedge clk) begin
+        if(~resetn)
+            state <= a;
+        else
+            state <= next_state;
+    end
+    
+    assign g[1] = (state == b);
+    assign g[2] = (state == c);
+    assign g[3] = (state == d);
+    
+    
+endmodule
diff --git a/bsp/xiangshan-verilator-xiangshan/verilator-case/tb_150.cpp b/bsp/xiangshan-verilator-xiangshan/verilator-case/tb_150.cpp
new file mode 100644
index 00000000000..637881bd91b
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/verilator-case/tb_150.cpp
@@ -0,0 +1,133 @@
+#include 
+#include 
+#include 
+#include 
+
+#include "verilated.h"
+#include "verilated_cov.h"
+#include "Vdut_150.h"
+
+static inline void tick(Vdut_150 *dut, VerilatedContext *ctx) {
+    dut->clk = 0;
+    dut->eval();
+    ctx->timeInc(1);
+    dut->clk = 1;
+    dut->eval();
+    ctx->timeInc(1);
+}
+
+// g[3:1] packed into low 3 bits of dut->g (bit0=g[1], bit1=g[2], bit2=g[3])
+static inline uint8_t g_bus(Vdut_150 *dut) {
+    return static_cast(dut->g & 0x07u);
+}
+
+int main(int argc, char **argv) {
+    Verilated::commandArgs(argc, argv);
+    auto ctx = std::make_unique();
+    ctx->traceEverOn(false);
+
+    auto dut = std::make_unique(ctx.get());
+
+    enum { A = 0, B = 1, C = 2, D = 3 };
+    uint8_t state = A;
+
+    auto apply_reset = [&]() {
+        dut->resetn = 0;
+        dut->r = 0;
+        tick(dut.get(), ctx.get());
+        dut->resetn = 1;
+        state = A;
+    };
+
+    auto step = [&](uint8_t r_val, const char *ctx_str) {
+        uint8_t r = r_val & 0x07u;  // r[3:1] in bits [2:0]
+        dut->r = r;
+
+        // Model next_state with r[1:3] mapped to bits 0..2.
+        bool r1 = (r & 0x1u) != 0;
+        bool r2 = (r & 0x2u) != 0;
+        bool r3 = (r & 0x4u) != 0;
+
+        uint8_t next = state;
+        switch (state) {
+            case A:
+                if (r1)                        next = B;
+                else if (!r1 && r2)            next = C;
+                else if (!r1 && !r2 && r3)     next = D;
+                else                           next = A;
+                break;
+            case B:
+                next = r1 ? B : A;
+                break;
+            case C:
+                next = r2 ? C : A;
+                break;
+            case D:
+                next = r3 ? D : A;
+                break;
+        }
+
+        tick(dut.get(), ctx.get());
+        state = next;
+
+        // Expected g bus: bit0 for B, bit1 for C, bit2 for D.
+        uint8_t g_exp = 0;
+        if (state == B) g_exp = 0x1u;
+        else if (state == C) g_exp = 0x2u;
+        else if (state == D) g_exp = 0x4u;
+
+        uint8_t g_act = g_bus(dut.get());
+        if (g_act != g_exp) {
+            std::cerr << "[TB] dut_150 failed (" << ctx_str << "): "
+                      << "state=" << int(state)
+                      << " r=0x" << std::hex << int(r)
+                      << " expected g=0x" << int(g_exp)
+                      << " got g=0x" << int(g_act) << std::dec << std::endl;
+            std::exit(EXIT_FAILURE);
+        }
+    };
+
+    // Scenario 1: exercise all branches in state A (r1, r2, r3, none).
+    apply_reset();                  // A
+    step(0x00u, "A->A_none");       // else
+    step(0x01u, "A->B_r1");         // if(r1)
+    // Now in B; release back to A
+    step(0x00u, "B->A_release_r1");
+
+    apply_reset();                  // A
+    step(0x02u, "A->C_r2");         // else if(~r1 & r2)
+    step(0x00u, "C->A_release_r2");
+
+    apply_reset();                  // A
+    step(0x04u, "A->D_r3");         // else if(~r1 & ~r2 & r3)
+    step(0x00u, "D->A_release_r3");
+
+    // Scenario 2: exercise B state's if/else.
+    apply_reset();                  // A
+    step(0x01u, "A->B_for_B");      // into B
+    step(0x01u, "B->B_hold_r1");    // if(r1) branch
+    step(0x00u, "B->A_else");       // else branch
+
+    // Scenario 3: C state's branches.
+    apply_reset();                  // A
+    step(0x02u, "A->C_for_C");
+    step(0x02u, "C->C_hold_r2");    // if(r2)
+    step(0x00u, "C->A_else_C");     // else
+
+    // Scenario 4: D state's branches.
+    apply_reset();                  // A
+    step(0x04u, "A->D_for_D");
+    step(0x04u, "D->D_hold_r3");    // if(r3)
+    step(0x00u, "D->A_else_D");     // else
+
+    std::cout << "[TB] dut_150 passed: fixed-priority arbiter full coverage" << std::endl;
+
+#if VM_COVERAGE
+    const char *covPath = std::getenv("VERILATOR_COV_FILE");
+    if (covPath == nullptr || covPath[0] == '\0') {
+        covPath = "coverage.dat";
+    }
+    VerilatedCov::write(covPath);
+#endif
+    return EXIT_SUCCESS;
+}

From 2952895e103057eb428412b7a6cd999d307d158c Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Tue, 7 Apr 2026 20:32:31 +0800
Subject: [PATCH 17/18] fix(exit): check if pthread data is really created

---
 components/libc/compilers/common/cstdlib.c |  9 +++++++--
 components/libc/posix/pthreads/pthread.c   | 20 +++++++++++++++++++-
 2 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/components/libc/compilers/common/cstdlib.c b/components/libc/compilers/common/cstdlib.c
index 523b84faf3d..ae3455e7d4c 100644
--- a/components/libc/compilers/common/cstdlib.c
+++ b/components/libc/compilers/common/cstdlib.c
@@ -30,10 +30,15 @@ void __rt_libc_exit(int status)
 #ifdef RT_USING_PTHREADS
         if (self->pthread_data != RT_NULL)
         {
+            extern rt_bool_t _pthread_data_is_created(rt_thread_t tid);
             extern void pthread_exit(void *value);
-            pthread_exit((void *)(intptr_t)status);
+
+            if (_pthread_data_is_created(self))
+            {
+                pthread_exit((void *)(intptr_t)status);
+                return;
+            }
         }
-        else
 #endif
         {
             rt_thread_control(self, RT_THREAD_CTRL_CLOSE, RT_NULL);
diff --git a/components/libc/posix/pthreads/pthread.c b/components/libc/posix/pthreads/pthread.c
index 05818fb6a52..38040297b0f 100644
--- a/components/libc/posix/pthreads/pthread.c
+++ b/components/libc/posix/pthreads/pthread.c
@@ -304,6 +304,24 @@ _pthread_data_t *_pthread_get_self_data(rt_bool_t create)
     return ptd;
 }
 
+rt_bool_t _pthread_data_is_created(rt_thread_t tid)
+{
+    _pthread_data_t *ptd;
+
+    if (tid == RT_NULL)
+    {
+        return RT_FALSE;
+    }
+
+    ptd = (_pthread_data_t *)tid->pthread_data;
+    if (ptd == RT_NULL)
+    {
+        return RT_FALSE;
+    }
+
+    return ptd->thread_entry != RT_NULL ? RT_TRUE : RT_FALSE;
+}
+
 /**
  * @brief Perform final cleanup of thread resources during thread termination
  *
@@ -451,7 +469,7 @@ int pthread_create(pthread_t            *pid,
     static rt_uint16_t pthread_number = 0;
 
     pthread_t pth_id;
-    _pthread_data_t *ptd;
+    _pthread_data_t *ptd = RT_NULL;
 
     /* pid shall be provided */
     RT_ASSERT(pid != RT_NULL);

From bce7c0b8e0e5e613d6e8eb7ab72568c911b04073 Mon Sep 17 00:00:00 2001
From: Haojin Tang 
Date: Tue, 7 Apr 2026 20:33:48 +0800
Subject: [PATCH 18/18] feat(xiangshan-verilator-xiangshan): support simulating
 XS with verilator

---
 .../applications/README.md                    |  49 +-
 .../applications/SConscript                   | 239 +++++++--
 .../applications/main.cpp                     | 138 -----
 .../applications/verilated_dut_150/.gitignore |   2 -
 .../applications/xiangshan_emu_rtthread.cpp   | 484 ++++++++++++++++++
 .../applications/xiangshan_emu_rtthread.h     |  97 ++++
 .../applications/xiangshan_hello_bin.S        |  29 ++
 .../applications/xiangshan_jtag_stub.cpp      |  23 +
 .../applications/xiangshan_rtthread_main.cpp  |  77 +++
 .../driver/board.h                            |   3 +-
 bsp/xiangshan-verilator-xiangshan/link.lds    |   4 +-
 bsp/xiangshan-verilator-xiangshan/rtconfig.py |   2 +-
 .../verilator-case/Makefile                   |  24 -
 .../verilator-case/dut_150.v                  |  46 --
 .../verilator-case/tb_150.cpp                 | 133 -----
 15 files changed, 921 insertions(+), 429 deletions(-)
 delete mode 100644 bsp/xiangshan-verilator-xiangshan/applications/main.cpp
 delete mode 100644 bsp/xiangshan-verilator-xiangshan/applications/verilated_dut_150/.gitignore
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/xiangshan_emu_rtthread.cpp
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/xiangshan_emu_rtthread.h
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/xiangshan_hello_bin.S
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/xiangshan_jtag_stub.cpp
 create mode 100644 bsp/xiangshan-verilator-xiangshan/applications/xiangshan_rtthread_main.cpp
 delete mode 100644 bsp/xiangshan-verilator-xiangshan/verilator-case/Makefile
 delete mode 100644 bsp/xiangshan-verilator-xiangshan/verilator-case/dut_150.v
 delete mode 100644 bsp/xiangshan-verilator-xiangshan/verilator-case/tb_150.cpp

diff --git a/bsp/xiangshan-verilator-xiangshan/applications/README.md b/bsp/xiangshan-verilator-xiangshan/applications/README.md
index afa1627391a..49ccbc18eaf 100644
--- a/bsp/xiangshan-verilator-xiangshan/applications/README.md
+++ b/bsp/xiangshan-verilator-xiangshan/applications/README.md
@@ -1,46 +1,31 @@
-# Verilated DUT on RT-Thread (qemu-virt64-riscv)
+# XiangShan Verilator Simulation on RT-Thread
 
 ## 目标
 
-在 RT-Thread qemu-virt64-riscv BSP 中运行 Verilator 生成的 `dut_150` 仿真程序,使用 C++ 测试逻辑(`applications/main.cpp`)。
+在 RT-Thread qemu-virt64-riscv BSP 中运行 XiangShan 的 Verilator 仿真,guest 负载为 `XiangShan/ready-to-run/hello.bin`。
 
-## 关键裁剪点
+## 构建前提
 
-- **单线程运行**:若运行环境缺少 pthread,需强制 `threads(1)`;本目录已将线程池改为 RT-Thread API,可按需开启多线程。
-- **禁用计时/波形**:去除 coroutine/timing/trace 相关源码与生成选项:`--no-timing --no-trace`。
-- **RT-Thread 适配宏**:使用 `-DVL_RT_THREAD -DVL_MT_DISABLED` 避免调用缺失的 libc/线程特性,使用自带的字符串/数学函数。
-- **运行库精简**:通过 `verilator --getenv VERILATOR_ROOT` 获取 Verilator 安装目录,只从其 `include/` 中编译 `verilated.cpp`、`verilated_random.cpp`、`verilated_threads.cpp` 这 3 个运行时源文件。
-- **运行库来源**:BSP 不再依赖 `applications/verilator_runtime` 这份本地拷贝参与构建,而是直接使用当前系统 Verilator 安装中的 runtime。
+- XiangShan 产物已通过 `make emu NO_DIFF=1 ...` 生成(`build/verilator-compile/`、`build/generated-src/` 等)。
+- `NOOP_HOME` 环境变量指向 XiangShan 根目录(或设置 `RTT_XIANGSHAN_ROOT`)。
 
-## 生成模型
+## 构建命令
 
-路径:`bsp/xiangshan-verilator/verilator-case/Makefile`
+```sh
+export NOOP_HOME=~/rt-thread-for-corvus/XiangShan
+scons --exec-path=$HOME/riscv-baremetal/bin --cc-prefix=riscv64-unknown-elf- -j64
+```
 
-- 默认调用:`verilator -Wall --cc dut_150.v --exe tb_150.cpp -Mdir ../applications/verilated_dut_150 --no-timing --no-trace --threads 1 --CFLAGS "-DVL_RT_THREAD -DVL_MT_DISABLED"`
-- 输出:`applications/verilated_dut_150/` 下的 `Vdut_150.*`。
+## 运行命令
 
-## 应用构建集成
+```sh
+timeout 720 ~/qemu/build/qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin
+```
 
-文件:`applications/SConscript`
+## 验收标准
 
-- 编译源:`*.c`/`*.cpp` + `verilated_dut_150/*.cpp` + 运行库最小集。
-- `CPPPATH`:应用目录、`verilated_dut_150`、`verilator --getenv VERILATOR_ROOT` 返回目录下的 `include/`。
-- `CPPDEFINES`:包含 `VL_RT_THREAD`。
+QEMU 日志中出现 guest UART 输出 `Hello, world!`,宿主不崩溃。
 
 ## 运行入口
 
-文件:`applications/main.cpp`
-
-- 创建上下文后可按需设置线程数,默认跟随 Verilator 生成的 `threads()`。
-- 重置与驱动 DUT 的测试序列,带有 `rt_kprintf`/`std::cout` 日志方便调试。
-- 为避免退出流程触发异常,结尾保持循环休眠(`rt_thread_mdelay`)。
-
-## 构建与运行
-
-- 构建:`scons --exec-path=$HOME/riscv-baremetal/bin --cc-prefix=riscv64-unknown-elf-`(在 `bsp/xiangshan-verilator`)。
-- 运行:`timeout 1 ~/qemu/build/qemu-system-riscv64 -nographic -machine virt -m 256M -kernel rtthread.bin`。
-
-## 已知行为
-
-- 文件系统未挂载会提示 `DFS.fs mount / failed ...`,但不影响仿真测试逻辑。
-- 运行日志会打印 `[TB] ...`,确认 DUT 已构造并执行测试用例。
+`applications/xiangshan_rtthread_main.cpp`:RT-Thread 专用 XiangShan 仿真入口,使用固定 `CommonArgs` 配置,不依赖命令行参数。
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/SConscript b/bsp/xiangshan-verilator-xiangshan/applications/SConscript
index 268a40666c9..ecf816e92ad 100644
--- a/bsp/xiangshan-verilator-xiangshan/applications/SConscript
+++ b/bsp/xiangshan-verilator-xiangshan/applications/SConscript
@@ -1,78 +1,217 @@
+import glob
 import os
 import subprocess
 
 from building import *
 
+# ---------------------------------------------------------------------------
+# Helpers
+# ---------------------------------------------------------------------------
 
-def _get_verilator_include_dir():
+def _unique(seq):
+    """Deduplicate a list while preserving order."""
+    seen = set()
+    return [x for x in seq if not (x in seen or seen.add(x))]
+
+
+def _workspace_root():
+    """Three levels up from applications/: bsp//applications -> repo root."""
+    return os.path.dirname(os.path.dirname(os.path.dirname(GetCurrentDir())))
+
+
+def _get_verilator_dirs():
+    """Return (include_dir, vltstd_dir) from the installed Verilator."""
     try:
-        verilator_root = subprocess.check_output(
+        root = subprocess.check_output(
             ['verilator', '--getenv', 'VERILATOR_ROOT'], universal_newlines=True
         ).strip()
     except (OSError, subprocess.CalledProcessError) as exc:
         raise RuntimeError(
             'Failed to query VERILATOR_ROOT via "verilator --getenv VERILATOR_ROOT"'
         ) from exc
+    if not root:
+        raise RuntimeError('verilator --getenv VERILATOR_ROOT returned an empty path')
+
+    inc = os.path.join(root, 'include')
+    vltstd = os.path.join(inc, 'vltstd')
+    for d in (inc, vltstd):
+        if not os.path.isdir(d):
+            raise RuntimeError('Verilator directory not found: %s' % d)
+    return inc, vltstd
+
+
+def _get_xiangshan_root():
+    """Locate XiangShan root via env vars or workspace default."""
+    candidates = []
+    for name in ('RTT_XIANGSHAN_ROOT', 'NOOP_HOME'):
+        val = os.getenv(name)
+        if val:
+            candidates.append(os.path.abspath(os.path.expanduser(val)))
+    candidates.append(os.path.join(_workspace_root(), 'XiangShan'))
+
+    for c in _unique(candidates):
+        if os.path.isdir(c):
+            return c
+    raise RuntimeError(
+        'XiangShan root not found. '
+        'Export RTT_XIANGSHAN_ROOT or NOOP_HOME to the XiangShan absolute path.'
+    )
+
+
+def _collect_xiangshan_inputs(xs_root):
+    """Return (sources, include_paths, cppdefines) for the XiangShan host-side build."""
+    build       = os.path.join(xs_root, 'build')
+    gen_dir     = os.path.join(build, 'generated-src')
+    vcomp_dir   = os.path.join(build, 'verilator-compile')
+    difftest    = os.path.join(xs_root, 'difftest')
+    csrc        = os.path.join(difftest, 'src', 'test', 'csrc')
+    common_dir  = os.path.join(csrc, 'common')
+    config_dir  = os.path.join(difftest, 'config')
+    emu_dir     = os.path.join(csrc, 'emu')
+    verilator_d = os.path.join(csrc, 'verilator')
+    plugin_inc  = os.path.join(csrc, 'plugin', 'include')
+    spikedasm_d = os.path.join(csrc, 'plugin', 'spikedasm')
+    workload_d  = os.path.join(xs_root, 'ready-to-run')
+    hello_bin   = os.path.join(workload_d, 'hello.bin')
+
+    # Minimal host-side source set (task 2 conclusion)
+    common_names = [
+        'elfloader.cpp', 'uart.cpp', 'compress.cpp', 'common.cpp',
+        'golden.cpp', 'flash.cpp', 'coverage.cpp', 'device.cpp',
+        'stopwatch.cpp', 'dut.cpp', 'ram.cpp', 'sdcard.cpp',
+    ]
+    sources = [os.path.join(common_dir, n) for n in common_names]
+    sources += [
+        os.path.join(config_dir, 'config.cpp'),
+        os.path.join(gen_dir, 'difftest-dpic.cpp'),
+        os.path.join(verilator_d, 'verilator.cpp'),
+    ]
 
-    if not verilator_root:
-        raise RuntimeError('`verilator --getenv VERILATOR_ROOT` returned an empty path')
+    # VSimTop generated sources
+    vsimtop = sorted(glob.glob(os.path.join(vcomp_dir, 'VSimTop*.cpp')))
+    if not vsimtop:
+        raise RuntimeError(
+            'No VSimTop generated sources under %s. '
+            'Run `export NOOP_HOME=%s && make emu ...` in XiangShan first.'
+            % (vcomp_dir, xs_root)
+        )
+    sources += vsimtop
+
+    # Validate all required paths exist
+    required = [
+        gen_dir, vcomp_dir, workload_d, hello_bin,
+        common_dir, config_dir, emu_dir, verilator_d, plugin_inc, spikedasm_d,
+    ] + sources
+    missing = [p for p in required if not os.path.exists(p)]
+    if missing:
+        raise RuntimeError(
+            'XiangShan generated inputs incomplete. Missing:\n  '
+            + '\n  '.join(missing)
+        )
 
-    include_dir = os.path.join(verilator_root, 'include')
-    if not os.path.isdir(include_dir):
-        raise RuntimeError('Verilator include directory not found: %s' % include_dir)
+    include_paths = [
+        common_dir, config_dir, gen_dir, plugin_inc,
+        spikedasm_d, emu_dir, verilator_d, vcomp_dir,
+    ]
 
-    return include_dir
+    cppdefines = [
+        'CONFIG_NO_DIFFTEST', 'VERILATOR', 'VERILATOR_4_210',
+        ('NUM_CORES', 1),
+        ('NOOP_HOME', '\\"%s\\"' % xs_root),
+        ('NOOP_HOME_PATH', xs_root),
+        'NO_GZ_COMPRESSION', 'NO_ZSTD_COMPRESSION',
+    ]
 
-cwd = GetCurrentDir()
+    return sources, include_paths, cppdefines
 
-# Application sources
-src = Glob('*.c') + Glob('*.cpp')
-
-# Verilated DUT and runtime support
-verilated_dut_dir = os.path.join(cwd, 'verilated_dut_150')
-verilator_rt_dir = _get_verilator_include_dir()
-verilator_runtime_build_dir = os.path.join(
-    os.path.dirname(cwd), 'build', 'applications', 'verilator_runtime'
-)
-
-# Only pull in the bits of the Verilated runtime we need for a single-threaded,
-# non-tracing simulation on RT-Thread.
-verilator_runtime_src = [
-    os.path.join(verilator_rt_dir, name)
-    for name in [
-        'verilated.cpp',
-        'verilated_random.cpp',
-        'verilated_threads.cpp',
-    ]
-]
 
-verilator_runtime_objs = [
-    Env.Object(
-        target=os.path.join(
-            verilator_runtime_build_dir,
-            os.path.splitext(os.path.basename(source))[0] + Env['OBJSUFFIX'],
-        ),
-        source=source,
-    )[0]
-    for source in verilator_runtime_src
-]
+def _build_objects(env_proto, sources, build_dir, cpppath, cppdefines, cxxflags):
+    """Compile a list of source files into a private build directory."""
+    ws = _workspace_root()
+    e = env_proto.Clone()
+    e.AppendUnique(CPPPATH=cpppath, CPPDEFINES=cppdefines)
+    if cxxflags:
+        e['CXXFLAGS'] = e.get('CXXFLAGS', '') + ' ' + ' '.join(cxxflags)
 
-src += Glob(os.path.join('verilated_dut_150', '*.cpp'))
+    objs = []
+    for src in sources:
+        src_abs = os.path.abspath(str(src))
+        if src_abs.startswith(ws + os.sep):
+            rel = os.path.relpath(src_abs, ws)
+        else:
+            rel = os.path.basename(src_abs)
+        target = os.path.join(build_dir, os.path.splitext(rel)[0] + e['OBJSUFFIX'])
+        tdir = os.path.dirname(target)
+        if not os.path.isdir(tdir):
+            os.makedirs(tdir)
+        objs.append(e.Object(target=target, source=src)[0])
+    return objs
 
-CPPPATH = [cwd, verilated_dut_dir, verilator_rt_dir]
-CPPDEFINES = ['VL_RT_THREAD']
 
-group = DefineGroup(
-    'Applications', src, depend=[''], CPPPATH=CPPPATH, CPPDEFINES=CPPDEFINES
-)
+# ---------------------------------------------------------------------------
+# Main build logic
+# ---------------------------------------------------------------------------
 
+cwd = GetCurrentDir()
+bsp_dir = os.path.dirname(cwd)
+ws_root = _workspace_root()
+
+verilator_inc, verilator_vltstd = _get_verilator_dirs()
+xs_root = _get_xiangshan_root()
+xs_src, xs_inc, xs_defines = _collect_xiangshan_inputs(xs_root)
+
+# Include paths (deduplicated, order-preserving)
+local_cpppath = _unique([cwd, verilator_inc, verilator_vltstd] + xs_inc)
+local_cppdefines = ['VL_RT_THREAD'] + xs_defines
+local_cxxflags = ['-std=gnu++17']
+
+# Application-local sources (*.c + *.cpp + *.S minus main.cpp)
+app_c   = Glob('*.c')
+app_cpp = [n for n in Glob('*.cpp') if os.path.basename(str(n)) != 'main.cpp']
+app_asm = Glob('*.S')
+
+# External sources: XiangShan host-side (hello.bin blob is now in app_asm)
+external_src = xs_src
+
+# Verilator runtime sources
+verilator_rt_names = ['verilated.cpp', 'verilated_random.cpp',
+                      'verilated_threads.cpp', 'verilated_dpi.cpp']
+verilator_rt_src = [os.path.join(verilator_inc, n) for n in verilator_rt_names]
+
+# Register the application group with RT-Thread build system
+group_kwargs = {
+    'LOCAL_CPPPATH': local_cpppath,
+    'LOCAL_CPPDEFINES': local_cppdefines,
+    'LOCAL_CXXFLAGS': ' ' + ' '.join(local_cxxflags),
+}
+group = DefineGroup('Applications', app_c + app_cpp + app_asm, depend=[''], **group_kwargs)
 objs = [group]
-objs += verilator_runtime_objs
-
-list = os.listdir(cwd)
 
-for item in list:
+# Recurse into subdirectories
+for item in os.listdir(cwd):
     if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
-        objs = objs + SConscript(os.path.join(item, 'SConscript'))
+        objs += SConscript(os.path.join(item, 'SConscript'))
+
+# Deferred compilation of external and Verilator runtime objects
+external_objs = []
+runtime_objs  = []
+
+def _register_deferred_objects():
+    if not external_objs and external_src:
+        external_objs.extend(_build_objects(
+            Env, external_src,
+            os.path.join(bsp_dir, 'build', 'applications', 'xiangshan_objects_xiangshan'),
+            local_cpppath, local_cppdefines, local_cxxflags,
+        ))
+    if not runtime_objs:
+        runtime_objs.extend(_build_objects(
+            Env, verilator_rt_src,
+            os.path.join(bsp_dir, 'build', 'applications', 'verilator_runtime_xiangshan'),
+            local_cpppath, local_cppdefines, local_cxxflags,
+        ))
+
+RegisterPreBuildingAction(_register_deferred_objects)
+objs.append(external_objs)
+objs.append(runtime_objs)
 
 Return('objs')
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/main.cpp b/bsp/xiangshan-verilator-xiangshan/applications/main.cpp
deleted file mode 100644
index 042146bdc79..00000000000
--- a/bsp/xiangshan-verilator-xiangshan/applications/main.cpp
+++ /dev/null
@@ -1,138 +0,0 @@
-#include 
-#include 
-#include 
-#include 
-
-#include 
-
-#include "Vdut_150.h"
-
-static inline void tick(Vdut_150 *dut, VerilatedContext *ctx) {
-    dut->clk = 0;
-    dut->eval();
-    ctx->timeInc(1);
-    dut->clk = 1;
-    dut->eval();
-    ctx->timeInc(1);
-}
-
-// g[3:1] packed into low 3 bits of dut->g (bit0=g[1], bit1=g[2], bit2=g[3])
-static inline uint8_t g_bus(Vdut_150 *dut) {
-    return static_cast(dut->g & 0x07u);
-}
-
-int main(int argc, char **argv) {
-    Verilated::commandArgs(argc, argv);
-    auto ctx = std::make_unique();
-    ctx->traceEverOn(false);
-    ctx->threads(1);
-    auto dut = std::make_unique(ctx.get());
-
-    enum { A = 0, B = 1, C = 2, D = 3 };
-    uint8_t state = A;
-
-    auto apply_reset = [&]() {
-        dut->resetn = 0;
-        dut->r = 0;
-        tick(dut.get(), ctx.get());
-        dut->resetn = 1;
-        state = A;
-    };
-
-    auto step = [&](uint8_t r_val, const char *ctx_str) {
-        uint8_t r = r_val & 0x07u;  // r[3:1] in bits [2:0]
-        dut->r = r;
-
-        // Model next_state with r[1:3] mapped to bits 0..2.
-        bool r1 = (r & 0x1u) != 0;
-        bool r2 = (r & 0x2u) != 0;
-        bool r3 = (r & 0x4u) != 0;
-
-        uint8_t next = state;
-        switch (state) {
-            case A:
-                if (r1)                        next = B;
-                else if (!r1 && r2)            next = C;
-                else if (!r1 && !r2 && r3)     next = D;
-                else                           next = A;
-                break;
-            case B:
-                next = r1 ? B : A;
-                break;
-            case C:
-                next = r2 ? C : A;
-                break;
-            case D:
-                next = r3 ? D : A;
-                break;
-        }
-
-        rt_kprintf("[TB] cycle time=%llu state=%u r=%u next=%u g_bus=%u (%s)\n",
-                   static_cast(ctx->time()), state, r, next,
-                   static_cast(g_bus(dut.get())),
-                   ctx_str);
-
-        tick(dut.get(), ctx.get());
-        state = next;
-
-        // Expected g bus: bit0 for B, bit1 for C, bit2 for D.
-        uint8_t g_exp = 0;
-        if (state == B) g_exp = 0x1u;
-        else if (state == C) g_exp = 0x2u;
-        else if (state == D) g_exp = 0x4u;
-
-        uint8_t g_act = g_bus(dut.get());
-        if (g_act != g_exp) {
-            std::cerr << "[TB] dut_150 failed (" << ctx_str << "): "
-                      << "state=" << int(state)
-                      << " r=0x" << std::hex << int(r)
-                      << " expected g=0x" << int(g_exp)
-                      << " got g=0x" << int(g_act) << std::dec << std::endl;
-            std::exit(EXIT_FAILURE);
-        }
-    };
-
-    // Scenario 1: exercise all branches in state A (r1, r2, r3, none).
-    apply_reset();                  // A
-    step(0x00u, "A->A_none");       // else
-    step(0x01u, "A->B_r1");         // if(r1)
-    // Now in B; release back to A
-    step(0x00u, "B->A_release_r1");
-
-    apply_reset();                  // A
-    step(0x02u, "A->C_r2");         // else if(~r1 & r2)
-    step(0x00u, "C->A_release_r2");
-
-    apply_reset();                  // A
-    step(0x04u, "A->D_r3");         // else if(~r1 & ~r2 & r3)
-    step(0x00u, "D->A_release_r3");
-
-    // Scenario 2: exercise B state's if/else.
-    apply_reset();                  // A
-    step(0x01u, "A->B_for_B");      // into B
-    step(0x01u, "B->B_hold_r1");    // if(r1) branch
-    step(0x00u, "B->A_else");       // else branch
-
-    // Scenario 3: C state's branches.
-    apply_reset();                  // A
-    step(0x02u, "A->C_for_C");
-    step(0x02u, "C->C_hold_r2");    // if(r2)
-    step(0x00u, "C->A_else_C");     // else
-
-    // Scenario 4: D state's branches.
-    apply_reset();                  // A
-    step(0x04u, "A->D_for_D");
-    step(0x04u, "D->D_hold_r3");    // if(r3)
-    step(0x00u, "D->A_else_D");     // else
-
-    std::cout << "[TB] dut_150 passed: fixed-priority arbiter full coverage" << std::endl;
-
-#if VM_COVERAGE
-    const char *covPath = std::getenv("VERILATOR_COV_FILE");
-    if (covPath == nullptr || covPath[0] == '\0') {
-        covPath = "coverage.dat";
-    }
-    VerilatedCov::write(covPath);
-#endif
-    return EXIT_SUCCESS;
-}
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/verilated_dut_150/.gitignore b/bsp/xiangshan-verilator-xiangshan/applications/verilated_dut_150/.gitignore
deleted file mode 100644
index d6b7ef32c84..00000000000
--- a/bsp/xiangshan-verilator-xiangshan/applications/verilated_dut_150/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*
-!.gitignore
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_emu_rtthread.cpp b/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_emu_rtthread.cpp
new file mode 100644
index 00000000000..eebedc0e002
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_emu_rtthread.cpp
@@ -0,0 +1,484 @@
+/*
+ * RT-Thread-specific Emulator implementation.
+ *
+ * This is a stripped-down version of XiangShan's emu.cpp that removes
+ * lightsss, getopt, sys/resource, fork, and snapshot dependencies.
+ * It lives in the BSP so that the XiangShan source tree stays unmodified.
+ *
+ * Based on: XiangShan/difftest/src/test/csrc/emu/emu.cpp
+ */
+
+#include "xiangshan_emu_rtthread.h"
+#include "compress.h"
+#include "device.h"
+#include "flash.h"
+#include "ram.h"
+#include "remote_bitbang.h"
+#include "sdcard.h"
+#include 
+#ifndef CONFIG_NO_DIFFTEST
+#include "difftest.h"
+#include "goldenmem.h"
+#include "refproxy.h"
+#endif // CONFIG_NO_DIFFTEST
+#ifdef ENABLE_CHISEL_DB
+#include "chisel_db.h"
+#endif
+
+extern remote_bitbang_t *jtag;
+
+namespace {
+
+const char *default_emu_argv[] = {"rtthread-xiangshan"};
+
+inline void normalize_emu_argv(int &argc, const char **&argv) {
+  if (argc <= 0 || argv == nullptr) {
+    argc = 1;
+    argv = default_emu_argv;
+  }
+}
+
+} // namespace
+
+Emulator::Emulator(const CommonArgs &fixed_args, int argc, const char *argv[])
+    : dut_ptr(new SIMULATOR), cycles(0), trapCode(STATE_RUNNING), elapsed_time(uptime()) {
+
+  normalize_emu_argv(argc, argv);
+  initialize(fixed_args, argc, argv);
+}
+
+void Emulator::initialize(const CommonArgs &initial_args, int argc, const char *argv[]) {
+
+  normalize_emu_argv(argc, argv);
+
+  args = initial_args;
+#ifdef VERILATOR
+  Verilated::commandArgs(argc, argv); // Prepare extra args for TLMonitor
+#endif
+
+#ifdef VERILATOR
+  // srand
+  srand(args.seed);
+  srand48(args.seed);
+  Verilated::randSeed(args.seed);
+  Verilated::randReset(2);
+#endif // VERILATOR
+
+  // init remote-bitbang
+  if (enable_simjtag) {
+    jtag_init();
+  }
+  // init flash
+  init_flash(args.flash_bin);
+
+  if (args.enable_waveform) {
+    uint64_t waveform_clock = args.enable_waveform_full ? 2 * args.log_begin : args.log_begin;
+    if (args.wave_path != NULL) {
+      dut_ptr->waveform_init(waveform_clock, args.wave_path);
+    } else {
+      dut_ptr->waveform_init(waveform_clock);
+    }
+  }
+
+  // init core
+  reset_ncycles(args.reset_cycles);
+
+  // init ram
+  uint64_t ram_size = DEFAULT_EMU_RAM_SIZE;
+  if (args.ram_size) {
+    ram_size = parse_ramsize(args.ram_size);
+  }
+  // footprints
+  if (args.image_as_footprints) {
+    if (args.linearized_name) {
+      simMemory = new LinearizedFootprintsMemory(args.image, ram_size, args.linearized_name);
+    } else {
+      simMemory = new FootprintsMemory(args.image, ram_size);
+    }
+  }
+  // normal linear memory
+  else {
+    if (args.footprints_name) {
+      simMemory = new MmapMemoryWithFootprints(args.image, ram_size, args.footprints_name);
+    } else {
+      init_ram(args.image, ram_size);
+    }
+  }
+
+  if (args.gcpt_restore) {
+    if (args.overwrite_nbytes_autoset) {
+      FILE *fp = fopen(args.gcpt_restore, "rb");
+      fseek(fp, 4, SEEK_SET);
+      fread(&args.overwrite_nbytes, sizeof(uint32_t), 1, fp);
+      fclose(fp);
+    }
+    overwrite_ram(args.gcpt_restore, args.overwrite_nbytes);
+  }
+
+  if (args.copy_ram_offset) {
+    copy_ram(args.copy_ram_offset);
+  }
+
+#ifdef ENABLE_CHISEL_DB
+  init_db(args.dump_db, (args.select_db != NULL), args.select_db);
+#endif
+
+  // set log time range and log level
+  dut_ptr->set_log_begin(args.log_begin);
+  dut_ptr->set_log_end(args.log_end);
+
+#ifndef CONFIG_NO_DIFFTEST
+  // init difftest
+  auto ref_ramsize = args.ram_size ? simMemory->get_size() : 0;
+  difftest_init(args.enable_diff, ref_ramsize);
+
+  // init difftest traces
+  if (args.trace_name) {
+    for (int i = 0; i < NUM_CORES; i++) {
+      difftest[i]->set_trace(args.trace_name, args.trace_is_read);
+    }
+  }
+#endif // CONFIG_NO_DIFFTEST
+
+  init_device();
+
+#ifndef CONFIG_NO_DIFFTEST
+#ifdef DEBUG_REFILL
+  for (int i = 0; i < NUM_CORES; i++) {
+    difftest[i]->set_track_instr(args.track_instr);
+  }
+#endif
+#endif // CONFIG_NO_DIFFTEST
+
+  for (int i = 0; i < NUM_CORES; i++) {
+    core_max_instr[i] = args.max_instr;
+  }
+
+#if VM_COVERAGE == 1
+  if (args.dump_coverage) {
+    coverage = Verilated::threadContextp()->coveragep();
+  }
+#endif
+}
+
+Emulator::~Emulator() {
+  // Simulation ends here, do clean up & display jobs
+
+#if VM_COVERAGE == 1
+  if (args.dump_coverage) {
+    save_coverage();
+  }
+#endif
+
+  // warning: this function may still simulate the circuit
+  // simulator resources must be released after this function
+  display_stats();
+
+#ifndef CONFIG_NO_DIFFTEST
+  stats.update(difftest[0]->dut);
+#endif // CONFIG_NO_DIFFTEST
+
+  simMemory->display_stats();
+  delete simMemory;
+  simMemory = nullptr;
+
+#ifndef CONFIG_NO_DIFFTEST
+  if (args.enable_diff) {
+    goldenmem_finish();
+  }
+#endif // CONFIG_NO_DIFFTEST
+  flash_finish();
+#ifndef CONFIG_NO_DIFFTEST
+  difftest_finish();
+#endif // CONFIG_NO_DIFFTEST
+
+#ifdef ENABLE_CHISEL_DB
+  if (args.dump_db) {
+    save_db(logdb_filename());
+  }
+#endif
+
+  elapsed_time = uptime() - elapsed_time;
+
+  Info(ANSI_COLOR_BLUE "Seed=%d Guest cycle spent: %'" PRIu64
+                       " (this will be different from cycleCnt if emu loads a snapshot)\n" ANSI_COLOR_RESET,
+       args.seed, cycles);
+  Info(ANSI_COLOR_BLUE "Host time spent: %'dms\n" ANSI_COLOR_RESET, elapsed_time);
+
+  if (enable_simjtag) {
+    delete jtag;
+  }
+
+  delete dut_ptr;
+}
+
+inline void Emulator::reset_ncycles(size_t cycles) {
+  if (args.trace_name && args.trace_is_read) {
+    return;
+  }
+  for (int i = 0; i < cycles; i++) {
+    dut_ptr->set_reset(1);
+
+#ifdef VERILATOR
+    dut_ptr->set_clock(1);
+    dut_ptr->step();
+#endif // VERILATOR
+
+    if (args.enable_waveform && args.enable_waveform_full && args.log_begin == 0) {
+      dut_ptr->waveform_tick();
+    }
+
+#ifdef VERILATOR
+    dut_ptr->set_clock(0);
+    dut_ptr->step();
+#endif // VERILATOR
+
+    if (args.enable_waveform && args.enable_waveform_full && args.log_begin == 0) {
+      dut_ptr->waveform_tick();
+    }
+
+    dut_ptr->set_reset(0);
+  }
+}
+
+inline void Emulator::single_cycle() {
+  if (args.trace_name && args.trace_is_read) {
+    goto end_single_cycle;
+  }
+
+#ifdef VERILATOR
+  dut_ptr->set_clock(1);
+  dut_ptr->step();
+#endif // VERILATOR
+
+  if (args.enable_waveform) {
+#if !defined(CONFIG_NO_DIFFTEST) && !defined(CONFIG_DIFFTEST_SQUASH)
+    uint64_t cycle = difftest[0]->get_trap_event()->cycleCnt;
+#else
+    static uint64_t cycle = -1UL;
+    cycle++;
+#endif
+    bool in_range = (args.log_begin <= cycle) && (cycle <= args.log_end);
+    if (in_range || force_dump_wave) {
+      dut_ptr->waveform_tick();
+    }
+  }
+
+  dut_ptr->step_uart();
+
+#ifdef VERILATOR
+  dut_ptr->set_clock(0);
+  dut_ptr->step();
+#endif // VERILATOR
+
+  if (args.enable_waveform && args.enable_waveform_full) {
+#if !defined(CONFIG_NO_DIFFTEST) && !defined(CONFIG_DIFFTEST_MERGE)
+    uint64_t cycle = difftest[0]->get_trap_event()->cycleCnt;
+#else
+    static uint64_t cycle = -1UL;
+    cycle++;
+#endif
+    bool in_range = (args.log_begin <= cycle) && (cycle <= args.log_end);
+    if (in_range || force_dump_wave) {
+      dut_ptr->waveform_tick();
+    }
+  }
+
+end_single_cycle:
+  cycles++;
+}
+
+int Emulator::tick() {
+
+  // cycle limitation
+  bool exceed_cycle_limit = false;
+#ifdef CONFIG_NO_DIFFTEST
+  exceed_cycle_limit = !args.max_cycles;
+#else
+  for (int i = 0; i < NUM_CORES; i++) {
+    auto trap = difftest[i]->get_trap_event();
+    if (trap->cycleCnt >= args.max_cycles) {
+      exceed_cycle_limit = true;
+    }
+  }
+#endif // CONFIG_NO_DIFFTEST
+
+  if (exceed_cycle_limit) {
+    trapCode = STATE_LIMIT_EXCEEDED;
+    return trapCode;
+  }
+
+  // instruction limitation
+#ifndef CONFIG_NO_DIFFTEST
+  for (int i = 0; i < NUM_CORES; i++) {
+    auto trap = difftest[i]->get_trap_event();
+    if (trap->instrCnt >= core_max_instr[i]) {
+      trapCode = STATE_LIMIT_EXCEEDED;
+      return trapCode;
+    }
+  }
+#endif // CONFIG_NO_DIFFTEST
+  // assertions
+  if (assert_count > 0) {
+    Info("The simulation stopped. There might be some assertion failed.\n");
+    trapCode = STATE_ABORT;
+    return trapCode;
+  }
+  // signals
+  if (signal_num != 0) {
+    trapCode = STATE_SIG;
+  }
+
+  // exit signal: non-zero exit exits the simulation. exit all 1's indicates good.
+  uint64_t difftest_exit = dut_ptr->get_difftest_exit();
+  if (difftest_exit) {
+    if (difftest_exit == -1UL) {
+      trapCode = STATE_SIM_EXIT;
+    } else {
+      Info("The simulation aborted via the top-level exit of 0x%lx.\n", difftest_exit);
+      trapCode = STATE_ABORT;
+    }
+  }
+
+  if (trapCode != STATE_RUNNING) {
+    return trapCode;
+  }
+#ifndef CONFIG_NO_DIFFTEST
+  for (int i = 0; i < NUM_CORES; i++) {
+    auto trap = difftest[i]->get_trap_event();
+    if (trap->instrCnt >= args.warmup_instr) {
+      Info("Warmup finished. The performance counters will be dumped and then reset.\n");
+      dut_ptr->set_perf_clean(1);
+      dut_ptr->set_perf_dump(1);
+      args.warmup_instr = -1;
+    }
+    if (trap->cycleCnt % args.stat_cycles == args.stat_cycles - 1) {
+      dut_ptr->set_perf_clean(1);
+      dut_ptr->set_perf_dump(1);
+    }
+    if (args.enable_ref_trace) {
+      bool is_debug = difftest[i]->proxy->get_debug();
+      if (trap->cycleCnt >= args.log_begin && !is_debug) {
+        difftest[i]->proxy->set_debug(true);
+      }
+      if (trap->cycleCnt >= args.log_end && is_debug) {
+        difftest[i]->proxy->set_debug(false);
+      }
+    }
+    if (args.enable_commit_trace) {
+      bool is_commit_trace = difftest[i]->get_commit_trace();
+      if (trap->cycleCnt >= args.log_begin && !is_commit_trace) {
+        difftest[i]->set_commit_trace(true);
+      }
+      if (trap->cycleCnt >= args.log_end && is_commit_trace) {
+        difftest[i]->set_commit_trace(false);
+      }
+    }
+  }
+#endif // CONFIG_NO_DIFFTEST
+
+  single_cycle();
+#ifdef CONFIG_NO_DIFFTEST
+  args.max_cycles--;
+#endif // CONFIG_NO_DIFFTEST
+
+  dut_ptr->set_perf_clean(0);
+  dut_ptr->set_perf_dump(0);
+
+#ifndef CONFIG_NO_DIFFTEST
+  int step = 0;
+  if (args.trace_name && args.trace_is_read) {
+    step = 1;
+    difftest_trace_read();
+  } else {
+    step = dut_ptr->get_difftest_step();
+  }
+
+  static uint64_t stuck_timer = 0;
+  if (step) {
+    stuck_timer = 0;
+  } else {
+    stuck_timer++;
+    if (stuck_timer >= Difftest::stuck_limit) {
+      Info("No difftest check for more than %lu cycles, maybe get stuck.", Difftest::stuck_limit);
+      return STATE_ABORT;
+    }
+  }
+
+  if (args.trace_name && !args.trace_is_read) {
+    difftest_trace_write(step);
+  }
+
+  trapCode = difftest_nstep(step, args.enable_diff);
+
+  if (trapCode != STATE_RUNNING) {
+    return trapCode;
+  }
+#endif // CONFIG_NO_DIFFTEST
+
+  return 0;
+}
+
+int Emulator::is_finished() {
+  return
+#ifdef VERILATOR
+      Verilated::gotFinish() ||
+#endif // VERILATOR
+      trapCode != STATE_RUNNING;
+}
+
+int Emulator::is_good() {
+  return is_good_trap();
+}
+
+#if VM_COVERAGE == 1
+void Emulator::save_coverage() {
+  const char *p = create_noop_filename(".coverage.dat");
+  Info("dump coverage data to %s...\n", p);
+  coverage->write(p);
+}
+#endif
+
+void Emulator::trigger_stat_dump() {
+  dut_ptr->set_perf_dump(1);
+  if (get_args().force_dump_result) {
+    dut_ptr->set_log_end(-1);
+  }
+  single_cycle();
+}
+
+void Emulator::display_stats() {
+#ifndef CONFIG_NO_DIFFTEST
+  for (int i = 0; i < NUM_CORES; i++) {
+    printf("Core %d: ", i);
+    uint64_t pc = difftest[i]->get_trap_event()->pc;
+    switch (trapCode) {
+      case STATE_GOODTRAP:
+        eprintf(ANSI_COLOR_GREEN "HIT GOOD TRAP at pc = 0x%" PRIx64 "\n" ANSI_COLOR_RESET, pc);
+        break;
+      case STATE_BADTRAP: eprintf(ANSI_COLOR_RED "HIT BAD TRAP at pc = 0x%" PRIx64 "\n" ANSI_COLOR_RESET, pc); break;
+      case STATE_ABORT: eprintf(ANSI_COLOR_RED "ABORT at pc = 0x%" PRIx64 "\n" ANSI_COLOR_RESET, pc); break;
+      case STATE_LIMIT_EXCEEDED:
+        eprintf(ANSI_COLOR_YELLOW "EXCEEDING CYCLE/INSTR LIMIT at pc = 0x%" PRIx64 "\n" ANSI_COLOR_RESET, pc);
+        break;
+      case STATE_SIG:
+        eprintf(ANSI_COLOR_YELLOW "SOME SIGNAL STOPS THE PROGRAM at pc = 0x%" PRIx64 "\n" ANSI_COLOR_RESET, pc);
+        break;
+      case STATE_SIM_EXIT: eprintf(ANSI_COLOR_YELLOW "EXIT at pc = 0x%" PRIx64 "\n" ANSI_COLOR_RESET, pc); break;
+      default: eprintf(ANSI_COLOR_RED "Unknown trap code: %d\n", trapCode);
+    }
+
+    difftest[i]->display_stats();
+  }
+#endif // CONFIG_NO_DIFFTEST
+
+  if (trapCode != STATE_ABORT) {
+    trigger_stat_dump();
+  }
+}
+
+void Emulator::snapshot_save() {}
+
+void Emulator::snapshot_load(const char *filename) {
+  (void)filename;
+}
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_emu_rtthread.h b/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_emu_rtthread.h
new file mode 100644
index 00000000000..690b01a8226
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_emu_rtthread.h
@@ -0,0 +1,97 @@
+/*
+ * RT-Thread-specific Emulator wrapper.
+ *
+ * This is a stripped-down version of XiangShan's emu.h that removes
+ * lightsss (fork-based snapshot), getopt, and sys/resource dependencies.
+ * It lives in the BSP so that the XiangShan source tree stays unmodified.
+ */
+
+#ifndef __XIANGSHAN_EMU_RTTHREAD_H
+#define __XIANGSHAN_EMU_RTTHREAD_H
+
+#include "args.h"
+#include "common.h"
+#include "dut.h"
+#include "simulator.h"
+#include 
+
+class LightSSS;
+
+class Emulator final : public DUT {
+private:
+  Simulator *dut_ptr;
+
+  bool force_dump_wave = false;
+  CommonArgs args;
+  LightSSS *lightsss = NULL;
+#if VM_COVERAGE == 1
+  VerilatedCovContext *coverage = NULL;
+#endif // VM_COVERAGE
+
+  // emu control variable
+  uint64_t cycles;
+  int trapCode;
+  uint32_t lasttime_snapshot = 0;
+  uint64_t core_max_instr[NUM_CORES];
+  uint32_t lasttime_poll = 0;
+  uint32_t elapsed_time;
+
+  void initialize(const CommonArgs &initial_args, int argc, const char *argv[]);
+
+  inline void reset_ncycles(size_t cycles);
+  inline void single_cycle();
+  void trigger_stat_dump();
+  void display_stats();
+
+  inline const char *logdb_filename() {
+    return create_noop_filename(".db");
+  }
+
+  void snapshot_save();
+  void snapshot_load(const char *filename);
+
+  inline const char *waveform_filename() {
+#ifdef ENABLE_FST
+    const char *filename = create_noop_filename(".fst");
+#else
+    const char *filename = create_noop_filename(".vcd");
+#endif
+    Info("dump wave to %s...\n", filename);
+    return filename;
+  }
+
+  const char *cycle_wavefile(uint64_t cycles);
+
+#if VM_COVERAGE == 1
+  void save_coverage();
+#endif
+
+  void fork_child_init() {}
+  inline bool is_fork_child() { return false; }
+
+public:
+  explicit Emulator(const CommonArgs &fixed_args, int argc = 0, const char *argv[] = nullptr);
+  ~Emulator();
+  uint64_t execute(uint64_t max_cycle, uint64_t max_instr);
+  uint64_t get_cycles() const {
+    return cycles;
+  }
+  CommonArgs get_args() const {
+    return args;
+  }
+  bool is_good_trap() {
+#ifdef FUZZING
+    return !(trapCode == STATE_ABORT);
+#else
+    return trapCode == STATE_GOODTRAP || trapCode == STATE_LIMIT_EXCEEDED || trapCode == STATE_SIM_EXIT;
+#endif
+  };
+  int get_trapcode() {
+    return trapCode;
+  }
+  int tick();
+  int is_finished();
+  int is_good();
+};
+
+#endif
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_hello_bin.S b/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_hello_bin.S
new file mode 100644
index 00000000000..163dd8c6e56
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_hello_bin.S
@@ -0,0 +1,29 @@
+/*
+ * Embed XiangShan guest workload (hello.bin) into the RT-Thread image.
+ *
+ * This file relies on the NOOP_HOME_PATH macro, which the build system
+ * defines as a bare (unquoted) absolute path to the XiangShan root,
+ * e.g.  -DNOOP_HOME_PATH=/path/to/XiangShan
+ *
+ * The preprocessor stringify operator turns the macro-expanded token
+ * sequence into a single string literal suitable for .incbin.
+ *
+ * The two symbols exported here are consumed by
+ * xiangshan_rtthread_main.cpp to construct the "wim@addr+size"
+ * in-memory image descriptor passed to XiangShan's RAM initialisation.
+ */
+
+#ifndef NOOP_HOME_PATH
+#error "NOOP_HOME_PATH must be defined to the XiangShan root (bare path, no quotes)"
+#endif
+
+#define _STRINGIFY(x) #x
+#define STRINGIFY(x)  _STRINGIFY(x)
+
+    .section .rodata
+    .balign 8
+    .global xiangshan_hello_bin_start
+xiangshan_hello_bin_start:
+    .incbin STRINGIFY(NOOP_HOME_PATH/ready-to-run/hello.bin)
+    .global xiangshan_hello_bin_end
+xiangshan_hello_bin_end:
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_jtag_stub.cpp b/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_jtag_stub.cpp
new file mode 100644
index 00000000000..a758f6ccd00
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_jtag_stub.cpp
@@ -0,0 +1,23 @@
+#include "remote_bitbang.h"
+
+remote_bitbang_t *jtag = nullptr;
+bool enable_simjtag = false;
+uint16_t remote_jtag_port = 23334;
+
+extern "C" void jtag_init()
+{
+}
+
+extern "C" int jtag_tick(unsigned char *jtag_TCK,
+                          unsigned char *jtag_TMS,
+                          unsigned char *jtag_TDI,
+                          unsigned char *jtag_TRSTn,
+                          unsigned char jtag_TDO)
+{
+    (void)jtag_TCK;
+    (void)jtag_TMS;
+    (void)jtag_TDI;
+    (void)jtag_TRSTn;
+    (void)jtag_TDO;
+    return 0;
+}
\ No newline at end of file
diff --git a/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_rtthread_main.cpp b/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_rtthread_main.cpp
new file mode 100644
index 00000000000..3c7a92b4d63
--- /dev/null
+++ b/bsp/xiangshan-verilator-xiangshan/applications/xiangshan_rtthread_main.cpp
@@ -0,0 +1,77 @@
+#include 
+#include 
+
+#include "args.h"
+#include "common.h"
+#include "dut.h"
+#include "xiangshan_emu_rtthread.h"
+
+namespace {
+
+const char *kRtThreadXiangShanProgramName = "rtthread-xiangshan";
+
+extern "C" {
+extern const unsigned char xiangshan_hello_bin_start[];
+extern const unsigned char xiangshan_hello_bin_end[];
+}
+
+const char *embedded_hello_image() {
+    static char image_spec[64];
+    static bool initialized = false;
+
+    if (!initialized) {
+        uintptr_t start = reinterpret_cast(xiangshan_hello_bin_start);
+        uintptr_t end = reinterpret_cast(xiangshan_hello_bin_end);
+        unsigned long image_addr = static_cast(start);
+        unsigned long image_size = static_cast(end - start);
+
+        std::snprintf(image_spec, sizeof(image_spec), "wim@%lx+%lx", image_addr, image_size);
+        initialized = true;
+    }
+
+    return image_spec;
+}
+
+CommonArgs build_rtthread_args() {
+    CommonArgs args;
+    args.enable_diff = false;
+    args.enable_waveform = false;
+    args.enable_waveform_full = false;
+    args.enable_snapshot = false;
+    args.enable_fork = false;
+    args.force_dump_result = false;
+    args.image = embedded_hello_image();
+    args.ram_size = "16MB";
+    args.flash_bin = nullptr;
+    return args;
+}
+
+int run_xiangshan_emulator() {
+    common_init_without_assertion(kRtThreadXiangShanProgramName);
+
+    Verilated::threadContextp()->threads(1);
+    auto emu = new Emulator(build_rtthread_args());
+
+    common_enable_assert();
+
+    while (!emu->is_finished()) {
+        emu->tick();
+    }
+
+    bool is_good = emu->is_good();
+
+    delete emu;
+
+    stats.display();
+    common_finish();
+    return !is_good;
+}
+
+} // namespace
+
+int main(int argc, char **argv) {
+    (void)argc;
+    (void)argv;
+
+    return run_xiangshan_emulator();
+}
\ No newline at end of file
diff --git a/bsp/xiangshan-verilator-xiangshan/driver/board.h b/bsp/xiangshan-verilator-xiangshan/driver/board.h
index 9c74ae6b267..27a8cac8535 100644
--- a/bsp/xiangshan-verilator-xiangshan/driver/board.h
+++ b/bsp/xiangshan-verilator-xiangshan/driver/board.h
@@ -20,12 +20,13 @@ extern unsigned int __bss_end;
 #define KERNEL_VADDR_START 0x0
 #endif
 
+#define VIRT64_QEMU_MEMSZ (0x10000000)
 #define VIRT64_SBI_MEMSZ (0x200000)
 
 #define RT_HW_HEAP_BEGIN ((void *)&__bss_end)
 #define RT_HW_HEAP_END   ((void *)(RT_HW_HEAP_BEGIN + 64 * 1024 * 1024))
 #define RT_HW_PAGE_START RT_HW_HEAP_END
-#define RT_HW_PAGE_END   ((void *)(KERNEL_VADDR_START + (128 * 1024 * 1024 - VIRT64_SBI_MEMSZ)))
+#define RT_HW_PAGE_END   ((void *)(KERNEL_VADDR_START + (VIRT64_QEMU_MEMSZ - VIRT64_SBI_MEMSZ)))
 
 void rt_hw_board_init(void);
 void rt_init_user_mem(struct rt_thread *thread, const char *name,
diff --git a/bsp/xiangshan-verilator-xiangshan/link.lds b/bsp/xiangshan-verilator-xiangshan/link.lds
index da750aca9bb..0d73605b32c 100644
--- a/bsp/xiangshan-verilator-xiangshan/link.lds
+++ b/bsp/xiangshan-verilator-xiangshan/link.lds
@@ -16,12 +16,12 @@ OUTPUT_ARCH( "riscv" )
 /*
  * Memory layout:
  * 0x80000000 - 0x80200000: SBI
- * 0x80200000 - 0x81200000: Kernel
+ * 0x80200000 - 0x90000000: Kernel and runtime memory under QEMU -m 256M
  */
 
 MEMORY
 {
-   SRAM : ORIGIN = 0x80200000, LENGTH = 0x1000000
+    SRAM : ORIGIN = 0x80200000, LENGTH = 0x0fe00000
 }
 
 ENTRY(_start)
diff --git a/bsp/xiangshan-verilator-xiangshan/rtconfig.py b/bsp/xiangshan-verilator-xiangshan/rtconfig.py
index 4f9e16b5f89..a605bc00785 100644
--- a/bsp/xiangshan-verilator-xiangshan/rtconfig.py
+++ b/bsp/xiangshan-verilator-xiangshan/rtconfig.py
@@ -34,7 +34,7 @@
 
     DEVICE  = ' -mcmodel=medany -march=rv64imac -mabi=lp64 '
     # Enable emulated TLS
-    CFLAGS  = DEVICE + '-fno-omit-frame-pointer -flax-vector-conversions -Wno-cpp -fno-common -ffunction-sections -fdata-sections -fdiagnostics-color=always -Xclang -fexperimental-max-bitint-width=20000 -fbracket-depth=2048 -Wno-parentheses-equality -DGSIM -femulated-tls'
+    CFLAGS  = DEVICE + '-fno-omit-frame-pointer -flax-vector-conversions -Wno-cpp -fno-common -ffunction-sections -fdata-sections -fdiagnostics-color=always -Xclang -fexperimental-max-bitint-width=20000 -fbracket-depth=2048 -Wno-parentheses-equality -femulated-tls'
     AFLAGS  = ' -c' + DEVICE + ' -x assembler-with-cpp -D__ASSEMBLY__ '
     LFLAGS  = DEVICE + ' -nostartfiles -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,_start -T link.lds' + ' -stdlib=libstdc++ -lc -lsupc++ -lgcc -lstdc++ -static'
     CPATH   = ''
diff --git a/bsp/xiangshan-verilator-xiangshan/verilator-case/Makefile b/bsp/xiangshan-verilator-xiangshan/verilator-case/Makefile
deleted file mode 100644
index 3a481ccc60d..00000000000
--- a/bsp/xiangshan-verilator-xiangshan/verilator-case/Makefile
+++ /dev/null
@@ -1,24 +0,0 @@
-VERILATOR ?= verilator
-TOP       := dut_150
-TB        := tb_150.cpp
-SRC       := $(TOP).v
-APP_DIR   := ../applications
-OUT_DIR   := $(APP_DIR)/verilated_$(TOP)
-
-# Keep the generated model strictly single-threaded and avoid features
-# that pull in timing/coroutine support which we trimmed from the runtime.
-VERILATOR_FLAGS := --no-timing --no-trace --threads 1
-# Propagate our RT-Thread specific switches into generated code.
-VERILATOR_CFLAGS := -DVL_RT_THREAD -DVL_MT_DISABLED
-
-.PHONY: all clean
-
-all: $(OUT_DIR)/V$(TOP).cpp
-
-$(OUT_DIR)/V$(TOP).cpp: $(SRC) $(TB) Makefile
-	@mkdir -p $(OUT_DIR)
-	$(VERILATOR) -Wall --cc $(SRC) --exe $(TB) -Mdir $(OUT_DIR) \
-		$(VERILATOR_FLAGS) --CFLAGS "$(VERILATOR_CFLAGS)"
-
-clean:
-	@rm -rf $(OUT_DIR)
diff --git a/bsp/xiangshan-verilator-xiangshan/verilator-case/dut_150.v b/bsp/xiangshan-verilator-xiangshan/verilator-case/dut_150.v
deleted file mode 100644
index 6030c2b9d5e..00000000000
--- a/bsp/xiangshan-verilator-xiangshan/verilator-case/dut_150.v
+++ /dev/null
@@ -1,46 +0,0 @@
-module dut_150 (
-    input clk,
-    input resetn,    // active-low synchronous reset
-    input [3:1] r,   // request
-    output [3:1] g   // grant
-  ); 
-
-    parameter a=2'd0, b=2'd1, c=2'd2, d=2'd3;
-    reg [1:0] state, next_state;
-    
-    always@(*) begin
-        case(state)
-            a: begin
-                if(r[1])    next_state = b;
-                else if(~r[1] & r[2])   next_state = c;
-                else if(~r[1] & ~r[2] & r[3])   next_state = d;
-                else    next_state = a;
-            end
-            b: begin
-                if(r[1])    next_state = b;
-                else        next_state = a;
-            end
-            c: begin
-                if(r[2])    next_state = c;
-                else        next_state = a;
-            end
-            d: begin
-                if(r[3])    next_state = d;
-                else        next_state = a;
-            end
-        endcase
-    end
-
-    always@(posedge clk) begin
-        if(~resetn)
-            state <= a;
-        else
-            state <= next_state;
-    end
-    
-    assign g[1] = (state == b);
-    assign g[2] = (state == c);
-    assign g[3] = (state == d);
-    
-    
-endmodule
diff --git a/bsp/xiangshan-verilator-xiangshan/verilator-case/tb_150.cpp b/bsp/xiangshan-verilator-xiangshan/verilator-case/tb_150.cpp
deleted file mode 100644
index 637881bd91b..00000000000
--- a/bsp/xiangshan-verilator-xiangshan/verilator-case/tb_150.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-#include 
-#include 
-#include 
-#include 
-
-#include "verilated.h"
-#include "verilated_cov.h"
-#include "Vdut_150.h"
-
-static inline void tick(Vdut_150 *dut, VerilatedContext *ctx) {
-    dut->clk = 0;
-    dut->eval();
-    ctx->timeInc(1);
-    dut->clk = 1;
-    dut->eval();
-    ctx->timeInc(1);
-}
-
-// g[3:1] packed into low 3 bits of dut->g (bit0=g[1], bit1=g[2], bit2=g[3])
-static inline uint8_t g_bus(Vdut_150 *dut) {
-    return static_cast(dut->g & 0x07u);
-}
-
-int main(int argc, char **argv) {
-    Verilated::commandArgs(argc, argv);
-    auto ctx = std::make_unique();
-    ctx->traceEverOn(false);
-
-    auto dut = std::make_unique(ctx.get());
-
-    enum { A = 0, B = 1, C = 2, D = 3 };
-    uint8_t state = A;
-
-    auto apply_reset = [&]() {
-        dut->resetn = 0;
-        dut->r = 0;
-        tick(dut.get(), ctx.get());
-        dut->resetn = 1;
-        state = A;
-    };
-
-    auto step = [&](uint8_t r_val, const char *ctx_str) {
-        uint8_t r = r_val & 0x07u;  // r[3:1] in bits [2:0]
-        dut->r = r;
-
-        // Model next_state with r[1:3] mapped to bits 0..2.
-        bool r1 = (r & 0x1u) != 0;
-        bool r2 = (r & 0x2u) != 0;
-        bool r3 = (r & 0x4u) != 0;
-
-        uint8_t next = state;
-        switch (state) {
-            case A:
-                if (r1)                        next = B;
-                else if (!r1 && r2)            next = C;
-                else if (!r1 && !r2 && r3)     next = D;
-                else                           next = A;
-                break;
-            case B:
-                next = r1 ? B : A;
-                break;
-            case C:
-                next = r2 ? C : A;
-                break;
-            case D:
-                next = r3 ? D : A;
-                break;
-        }
-
-        tick(dut.get(), ctx.get());
-        state = next;
-
-        // Expected g bus: bit0 for B, bit1 for C, bit2 for D.
-        uint8_t g_exp = 0;
-        if (state == B) g_exp = 0x1u;
-        else if (state == C) g_exp = 0x2u;
-        else if (state == D) g_exp = 0x4u;
-
-        uint8_t g_act = g_bus(dut.get());
-        if (g_act != g_exp) {
-            std::cerr << "[TB] dut_150 failed (" << ctx_str << "): "
-                      << "state=" << int(state)
-                      << " r=0x" << std::hex << int(r)
-                      << " expected g=0x" << int(g_exp)
-                      << " got g=0x" << int(g_act) << std::dec << std::endl;
-            std::exit(EXIT_FAILURE);
-        }
-    };
-
-    // Scenario 1: exercise all branches in state A (r1, r2, r3, none).
-    apply_reset();                  // A
-    step(0x00u, "A->A_none");       // else
-    step(0x01u, "A->B_r1");         // if(r1)
-    // Now in B; release back to A
-    step(0x00u, "B->A_release_r1");
-
-    apply_reset();                  // A
-    step(0x02u, "A->C_r2");         // else if(~r1 & r2)
-    step(0x00u, "C->A_release_r2");
-
-    apply_reset();                  // A
-    step(0x04u, "A->D_r3");         // else if(~r1 & ~r2 & r3)
-    step(0x00u, "D->A_release_r3");
-
-    // Scenario 2: exercise B state's if/else.
-    apply_reset();                  // A
-    step(0x01u, "A->B_for_B");      // into B
-    step(0x01u, "B->B_hold_r1");    // if(r1) branch
-    step(0x00u, "B->A_else");       // else branch
-
-    // Scenario 3: C state's branches.
-    apply_reset();                  // A
-    step(0x02u, "A->C_for_C");
-    step(0x02u, "C->C_hold_r2");    // if(r2)
-    step(0x00u, "C->A_else_C");     // else
-
-    // Scenario 4: D state's branches.
-    apply_reset();                  // A
-    step(0x04u, "A->D_for_D");
-    step(0x04u, "D->D_hold_r3");    // if(r3)
-    step(0x00u, "D->A_else_D");     // else
-
-    std::cout << "[TB] dut_150 passed: fixed-priority arbiter full coverage" << std::endl;
-
-#if VM_COVERAGE
-    const char *covPath = std::getenv("VERILATOR_COV_FILE");
-    if (covPath == nullptr || covPath[0] == '\0') {
-        covPath = "coverage.dat";
-    }
-    VerilatedCov::write(covPath);
-#endif
-    return EXIT_SUCCESS;
-}