mirror of
https://github.com/retroSoC/retroSoC.git
synced 2026-11-04 22:32:36 +00:00
feat(piolite): implement P2 core and registers
Implement the standalone two-SM PIO-lite core, shared 32x16 IMEM, per-SM 8x32 FIFOs, APB1.0/ISA1.0 register mirror, GPIO guard and local stream lifecycle adapters. Keep Tiny integration, central DMA, HAL loader, profiles, dependencies and warning baselines unchanged. Add exhaustive 65536-word differential testing, directed/random PL-V coverage, exact SVH/C parity, formal proofs and named witnesses, plus fail-closed dual-simulator, synthesis, netlist and observational STA flows. Three independent review rounds closed all findings, including lifecycle coverage and evidence-integrity gaps. Final IHP130 and ICS55 stage reports pass; phase_accepted remains false.
This commit is contained in:
37 files changed
+5732
-1
No files matched your search
@@ -558,6 +558,11 @@ include physical/librelane/mini/Makefile
|
||||
endif
|
||||
include physical/ecc/Makefile
|
||||
|
||||
.PHONY: piolite-p2-lint piolite-p2-rtl piolite-p2-formal piolite-p2-synth piolite-p2-netsim piolite-p2-sta piolite-p2-report
|
||||
piolite-p2-lint piolite-p2-rtl piolite-p2-formal piolite-p2-synth piolite-p2-netsim piolite-p2-sta piolite-p2-report: manifest
|
||||
$(PYTHON) $(ROOT_PATH)/scripts/piolite_p2.py $(patsubst piolite-p2-%,%,$@) \
|
||||
--variant-root $(VARIANT_ROOT) --pdk $(PDK) --simulator $(SIMU) --jobs $(JOBS)
|
||||
|
||||
.PHONY: spi-p1-lint spi-p1-rtl spi-p1-formal spi-p1-synth spi-p1-netsim spi-p1-sta spi-p1-report
|
||||
spi-p1-lint spi-p1-rtl spi-p1-formal spi-p1-synth spi-p1-netsim spi-p1-sta spi-p1-report: manifest
|
||||
$(PYTHON) $(ROOT_PATH)/scripts/spi_p1.py $(patsubst spi-p1-%,%,$@) \
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
#ifndef RETROSOC_HAL_PIOLITE_REGS_H
|
||||
#define RETROSOC_HAL_PIOLITE_REGS_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/* PIO-lite APB/ISA 1.0. Constants do not imply a mapped product instance. */
|
||||
#define RS_PIOLITE_ABI_APB4_IP_ID UINT32_C(0x000)
|
||||
#define RS_PIOLITE_ABI_APB4_IP_VERSION UINT32_C(0x004)
|
||||
#define RS_PIOLITE_ABI_APB4_ISA_VERSION UINT32_C(0x008)
|
||||
#define RS_PIOLITE_ABI_APB4_CAPABILITY UINT32_C(0x00c)
|
||||
#define RS_PIOLITE_ABI_APB4_ROUTABLE_MASK UINT32_C(0x010)
|
||||
#define RS_PIOLITE_ABI_APB4_CLOCK_HZ UINT32_C(0x014)
|
||||
#define RS_PIOLITE_ABI_APB4_FLAGS UINT32_C(0x018)
|
||||
#define RS_PIOLITE_ABI_APB4_FLAG_SET UINT32_C(0x01c)
|
||||
#define RS_PIOLITE_ABI_APB4_FLAG_CLEAR UINT32_C(0x020)
|
||||
#define RS_PIOLITE_ABI_APB4_IRQ_STATUS UINT32_C(0x024)
|
||||
#define RS_PIOLITE_ABI_APB4_IRQ_ENABLE UINT32_C(0x028)
|
||||
#define RS_PIOLITE_ABI_APB4_TX_BIND UINT32_C(0x02c)
|
||||
#define RS_PIOLITE_ABI_APB4_RX_BIND UINT32_C(0x030)
|
||||
#define RS_PIOLITE_ABI_APB4_TRANSPORT_STATUS UINT32_C(0x034)
|
||||
#define RS_PIOLITE_ABI_APB4_IMEM_VALID UINT32_C(0x038)
|
||||
#define RS_PIOLITE_ABI_APB4_OWNED_MASK UINT32_C(0x03c)
|
||||
#define RS_PIOLITE_ABI_APB4_START UINT32_C(0x040)
|
||||
#define RS_PIOLITE_ABI_APB4_PAUSE UINT32_C(0x044)
|
||||
#define RS_PIOLITE_ABI_APB4_RESUME UINT32_C(0x048)
|
||||
#define RS_PIOLITE_ABI_APB4_DISABLE UINT32_C(0x04c)
|
||||
#define RS_PIOLITE_ABI_APB4_ABORT UINT32_C(0x050)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_RESET UINT32_C(0x054)
|
||||
#define RS_PIOLITE_ABI_APB4_BLOCK_RESET UINT32_C(0x058)
|
||||
#define RS_PIOLITE_ABI_APB4_IMEM_BASE UINT32_C(0x100)
|
||||
#define RS_PIOLITE_ABI_APB4_IMEM_END UINT32_C(0x17c)
|
||||
#define RS_PIOLITE_ABI_APB4_SM0_BASE UINT32_C(0x200)
|
||||
#define RS_PIOLITE_ABI_APB4_SM1_BASE UINT32_C(0x280)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_STRIDE UINT32_C(0x080)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_STATUS UINT32_C(0x000)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_CONFIG UINT32_C(0x004)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_DIV UINT32_C(0x008)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_PROGRAM UINT32_C(0x00c)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_WRAP UINT32_C(0x010)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_IN_PINS UINT32_C(0x014)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_OUT_PINS UINT32_C(0x018)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_SET_PINS UINT32_C(0x01c)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_CLAIM_MASK UINT32_C(0x020)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_OUTPUT_MASK UINT32_C(0x024)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_INITIAL_VALUE UINT32_C(0x028)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_INITIAL_OE UINT32_C(0x02c)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_INITIAL_XY UINT32_C(0x030)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_SHIFT_LIMITS UINT32_C(0x034)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_TEST_SELECT UINT32_C(0x038)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_WATERMARKS UINT32_C(0x03c)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_TXDATA UINT32_C(0x040)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_RXDATA UINT32_C(0x044)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_FIFO_LEVELS UINT32_C(0x048)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_EVENTS UINT32_C(0x04c)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_EVENT_ENABLE UINT32_C(0x050)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_FAULT UINT32_C(0x054)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_PC UINT32_C(0x058)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_ISR UINT32_C(0x05c)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_OSR UINT32_C(0x060)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_XY UINT32_C(0x064)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_SHIFT_COUNTS UINT32_C(0x068)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_TICK_COUNT UINT32_C(0x06c)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_TX_STALL_COUNT UINT32_C(0x070)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_RX_STALL_COUNT UINT32_C(0x074)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_RETIRED_COUNT UINT32_C(0x078)
|
||||
#define RS_PIOLITE_ABI_APB4_SM_EXEC_PHASE UINT32_C(0x07c)
|
||||
#define RS_PIOLITE_ABI_APB4_ID_VALUE UINT32_C(0x50494f4c)
|
||||
#define RS_PIOLITE_ABI_APB4_VERSION_VALUE UINT32_C(0x00010000)
|
||||
#define RS_PIOLITE_ABI_APB4_ISA_VALUE UINT32_C(0x00010000)
|
||||
#define RS_PIOLITE_ABI_APB4_CAPABILITY_VALUE UINT32_C(0x08082002)
|
||||
#define RS_PIOLITE_ABI_APB4_ROUTES_VALUE UINT32_C(0xffffffff)
|
||||
#define RS_PIOLITE_ABI_APB4_DIV_RESET UINT32_C(0x00000001)
|
||||
#define RS_PIOLITE_ABI_APB4_SHIFT_LIMITS_RESET UINT32_C(0x00002020)
|
||||
#define RS_PIOLITE_ABI_APB4_WATERMARKS_RESET UINT32_C(0x00000100)
|
||||
#define RS_PIOLITE_ABI_APB4_IMEM_WORDS 32U
|
||||
#define RS_PIOLITE_ABI_APB4_IMEM_WORD_STRIDE 4U
|
||||
#define RS_PIOLITE_ABI_APB4_SM_COUNT 2U
|
||||
#define RS_PIOLITE_ABI_APB4_FIFO_DEPTH 8U
|
||||
#define RS_PIOLITE_ABI_APB4_IRQ_MASK UINT32_C(0x03030f03)
|
||||
#define RS_PIOLITE_ABI_STATUS_STATE 0U
|
||||
#define RS_PIOLITE_ABI_STATUS_STALL 4U
|
||||
#define RS_PIOLITE_ABI_STATUS_LAST_FIFO_FAILED 8U
|
||||
#define RS_PIOLITE_ABI_CONFIG_AUTOPULL 0U
|
||||
#define RS_PIOLITE_ABI_CONFIG_AUTOPUSH 1U
|
||||
#define RS_PIOLITE_ABI_CONFIG_TX_RIGHT 2U
|
||||
#define RS_PIOLITE_ABI_CONFIG_RX_RIGHT 3U
|
||||
#define RS_PIOLITE_ABI_CONFIG_TEST_PIN_ENABLE 4U
|
||||
#define RS_PIOLITE_ABI_DIV_VALUE 0U
|
||||
#define RS_PIOLITE_ABI_PROGRAM_BASE 0U
|
||||
#define RS_PIOLITE_ABI_PROGRAM_LEN 8U
|
||||
#define RS_PIOLITE_ABI_PROGRAM_ENTRY 16U
|
||||
#define RS_PIOLITE_ABI_WRAP_BOTTOM 0U
|
||||
#define RS_PIOLITE_ABI_WRAP_TOP 8U
|
||||
#define RS_PIOLITE_ABI_IN_PINS_BASE 0U
|
||||
#define RS_PIOLITE_ABI_IN_PINS_WIDTH 8U
|
||||
#define RS_PIOLITE_ABI_OUT_PINS_BASE 0U
|
||||
#define RS_PIOLITE_ABI_OUT_PINS_WIDTH 8U
|
||||
#define RS_PIOLITE_ABI_SET_PINS_BASE 0U
|
||||
#define RS_PIOLITE_ABI_SET_PINS_WIDTH 8U
|
||||
#define RS_PIOLITE_ABI_CLAIM_MASK_PINS 0U
|
||||
#define RS_PIOLITE_ABI_OUTPUT_MASK_PINS 0U
|
||||
#define RS_PIOLITE_ABI_INITIAL_VALUE_PINS 0U
|
||||
#define RS_PIOLITE_ABI_INITIAL_OE_PINS 0U
|
||||
#define RS_PIOLITE_ABI_INITIAL_XY_X 0U
|
||||
#define RS_PIOLITE_ABI_INITIAL_XY_Y 16U
|
||||
#define RS_PIOLITE_ABI_SHIFT_LIMITS_TX_BITS 0U
|
||||
#define RS_PIOLITE_ABI_SHIFT_LIMITS_RX_BITS 8U
|
||||
#define RS_PIOLITE_ABI_TEST_SELECT_GPIO 0U
|
||||
#define RS_PIOLITE_ABI_TEST_SELECT_FLAG 8U
|
||||
#define RS_PIOLITE_ABI_WATERMARKS_TX_LOW 0U
|
||||
#define RS_PIOLITE_ABI_WATERMARKS_RX_HIGH 8U
|
||||
#define RS_PIOLITE_ABI_FIFO_LEVELS_TX 0U
|
||||
#define RS_PIOLITE_ABI_FIFO_LEVELS_RX 8U
|
||||
#define RS_PIOLITE_ABI_EVENTS_HALT 0U
|
||||
#define RS_PIOLITE_ABI_EVENTS_ABORT 1U
|
||||
#define RS_PIOLITE_ABI_EVENTS_FAULT 2U
|
||||
#define RS_PIOLITE_ABI_EVENTS_TX_STALL 3U
|
||||
#define RS_PIOLITE_ABI_EVENTS_RX_STALL 4U
|
||||
#define RS_PIOLITE_ABI_EVENTS_FIFO_FAIL 5U
|
||||
#define RS_PIOLITE_ABI_EVENT_ENABLE_HALT 0U
|
||||
#define RS_PIOLITE_ABI_EVENT_ENABLE_ABORT 1U
|
||||
#define RS_PIOLITE_ABI_EVENT_ENABLE_FAULT 2U
|
||||
#define RS_PIOLITE_ABI_EVENT_ENABLE_TX_STALL 3U
|
||||
#define RS_PIOLITE_ABI_EVENT_ENABLE_RX_STALL 4U
|
||||
#define RS_PIOLITE_ABI_EVENT_ENABLE_FIFO_FAIL 5U
|
||||
#define RS_PIOLITE_ABI_FAULT_CODE 0U
|
||||
#define RS_PIOLITE_ABI_FAULT_PC 8U
|
||||
#define RS_PIOLITE_ABI_SHIFT_COUNTS_ISR 0U
|
||||
#define RS_PIOLITE_ABI_SHIFT_COUNTS_OSR 8U
|
||||
#define RS_PIOLITE_ABI_EXEC_PHASE_DIV 0U
|
||||
#define RS_PIOLITE_ABI_EXEC_PHASE_DELAY 16U
|
||||
#define RS_PIOLITE_ABI_TX_BIND_SM 0U
|
||||
#define RS_PIOLITE_ABI_TX_BIND_CHANNEL 8U
|
||||
#define RS_PIOLITE_ABI_TX_BIND_ENABLE 31U
|
||||
#define RS_PIOLITE_ABI_RX_BIND_SM 0U
|
||||
#define RS_PIOLITE_ABI_RX_BIND_CHANNEL 8U
|
||||
#define RS_PIOLITE_ABI_RX_BIND_ENABLE 31U
|
||||
#define RS_PIOLITE_ABI_TRANSPORT_STATUS_TX_JOB 0U
|
||||
#define RS_PIOLITE_ABI_TRANSPORT_STATUS_RX_JOB 1U
|
||||
#define RS_PIOLITE_ABI_TRANSPORT_STATUS_TX_HELD 2U
|
||||
#define RS_PIOLITE_ABI_TRANSPORT_STATUS_RX_HELD 3U
|
||||
#define RS_PIOLITE_ABI_TRANSPORT_STATUS_TX_CLOSING 4U
|
||||
#define RS_PIOLITE_ABI_TRANSPORT_STATUS_RX_CLOSING 5U
|
||||
#define RS_PIOLITE_ABI_IRQ_STATUS_SM0 0U
|
||||
#define RS_PIOLITE_ABI_IRQ_STATUS_SM1 1U
|
||||
#define RS_PIOLITE_ABI_IRQ_STATUS_FLAGS 8U
|
||||
#define RS_PIOLITE_ABI_IRQ_STATUS_TX_LEVEL 16U
|
||||
#define RS_PIOLITE_ABI_IRQ_STATUS_RX_LEVEL 24U
|
||||
#define RS_PIOLITE_ABI_STATE_DISABLED 0U
|
||||
#define RS_PIOLITE_ABI_STATE_RUNNING 1U
|
||||
#define RS_PIOLITE_ABI_STATE_PAUSED 2U
|
||||
#define RS_PIOLITE_ABI_STATE_HALTED 3U
|
||||
#define RS_PIOLITE_ABI_STATE_CLOSING 4U
|
||||
#define RS_PIOLITE_ABI_STALL_NONE 0U
|
||||
#define RS_PIOLITE_ABI_STALL_PIN 1U
|
||||
#define RS_PIOLITE_ABI_STALL_FLAG 2U
|
||||
#define RS_PIOLITE_ABI_STALL_TX 3U
|
||||
#define RS_PIOLITE_ABI_STALL_RX 4U
|
||||
#define RS_PIOLITE_ABI_FAULT_NONE 0U
|
||||
#define RS_PIOLITE_ABI_FAULT_ILLEGAL_INSTRUCTION 1U
|
||||
#define RS_PIOLITE_ABI_FAULT_INVALID_FETCH 2U
|
||||
#define RS_PIOLITE_ABI_FAULT_PROGRAM_BOUNDS 3U
|
||||
#define RS_PIOLITE_ABI_FAULT_SHIFT_BOUNDS 4U
|
||||
#define RS_PIOLITE_ABI_FAULT_PIN_AUTHORIZATION 5U
|
||||
#define RS_PIOLITE_ABI_FAULT_OWNERSHIP_FILTER 6U
|
||||
#define RS_PIOLITE_ABI_FAULT_GPIO_READY 7U
|
||||
#define RS_PIOLITE_ABI_FAULT_STREAM_BEAT 8U
|
||||
#define RS_PIOLITE_ABI_OPCODE_CTRL 0U
|
||||
#define RS_PIOLITE_ABI_OPCODE_SET_PIN 1U
|
||||
#define RS_PIOLITE_ABI_OPCODE_SET_DIR 2U
|
||||
#define RS_PIOLITE_ABI_OPCODE_SET_COUNT 3U
|
||||
#define RS_PIOLITE_ABI_OPCODE_IN 4U
|
||||
#define RS_PIOLITE_ABI_OPCODE_OUT 5U
|
||||
#define RS_PIOLITE_ABI_OPCODE_MOVE 6U
|
||||
#define RS_PIOLITE_ABI_OPCODE_FIFO 7U
|
||||
#define RS_PIOLITE_ABI_OPCODE_WAIT 8U
|
||||
#define RS_PIOLITE_ABI_OPCODE_BRANCH 9U
|
||||
#define RS_PIOLITE_ABI_OPCODE_FLAG 10U
|
||||
#define RS_PIOLITE_ABI_CTRL_NOP 0U
|
||||
#define RS_PIOLITE_ABI_CTRL_HALT 1U
|
||||
#define RS_PIOLITE_ABI_CTRL_CLEAR_ISR 2U
|
||||
#define RS_PIOLITE_ABI_CTRL_CLEAR_OSR 3U
|
||||
#define RS_PIOLITE_ABI_IN_PINS 0U
|
||||
#define RS_PIOLITE_ABI_IN_X 1U
|
||||
#define RS_PIOLITE_ABI_IN_Y 2U
|
||||
#define RS_PIOLITE_ABI_IN_ZERO 3U
|
||||
#define RS_PIOLITE_ABI_OUT_PINS 0U
|
||||
#define RS_PIOLITE_ABI_OUT_DIRECTIONS 1U
|
||||
#define RS_PIOLITE_ABI_OUT_X 2U
|
||||
#define RS_PIOLITE_ABI_OUT_Y 3U
|
||||
#define RS_PIOLITE_ABI_OUT_DISCARD 4U
|
||||
#define RS_PIOLITE_ABI_MOVE_ISR 0U
|
||||
#define RS_PIOLITE_ABI_MOVE_OSR 1U
|
||||
#define RS_PIOLITE_ABI_MOVE_X 2U
|
||||
#define RS_PIOLITE_ABI_MOVE_Y 3U
|
||||
#define RS_PIOLITE_ABI_MOVE_ZERO 4U
|
||||
#define RS_PIOLITE_ABI_MOVE_ONES 5U
|
||||
#define RS_PIOLITE_ABI_FIFO_PULL 0U
|
||||
#define RS_PIOLITE_ABI_FIFO_PUSH 1U
|
||||
#define RS_PIOLITE_ABI_FIFO_NONBLOCKING 0U
|
||||
#define RS_PIOLITE_ABI_FIFO_BLOCKING 1U
|
||||
#define RS_PIOLITE_ABI_WAIT_PIN 0U
|
||||
#define RS_PIOLITE_ABI_WAIT_FLAG 1U
|
||||
#define RS_PIOLITE_ABI_BRANCH_ALWAYS 0U
|
||||
#define RS_PIOLITE_ABI_BRANCH_X_ZERO 1U
|
||||
#define RS_PIOLITE_ABI_BRANCH_Y_ZERO 2U
|
||||
#define RS_PIOLITE_ABI_BRANCH_DEC_X_NONZERO 3U
|
||||
#define RS_PIOLITE_ABI_BRANCH_DEC_Y_NONZERO 4U
|
||||
#define RS_PIOLITE_ABI_BRANCH_TEST_PIN_HIGH 5U
|
||||
#define RS_PIOLITE_ABI_BRANCH_FIFO_FAILED 6U
|
||||
#define RS_PIOLITE_ABI_BRANCH_TEST_FLAG_SET 7U
|
||||
#define RS_PIOLITE_ABI_FLAG_CLEAR 0U
|
||||
#define RS_PIOLITE_ABI_FLAG_SET 1U
|
||||
|
||||
typedef enum {
|
||||
RS_PIOLITE_OPCODE_CTRL = 0,
|
||||
RS_PIOLITE_OPCODE_SET_PIN = 1,
|
||||
RS_PIOLITE_OPCODE_SET_DIR = 2,
|
||||
RS_PIOLITE_OPCODE_SET_COUNT = 3,
|
||||
RS_PIOLITE_OPCODE_IN = 4,
|
||||
RS_PIOLITE_OPCODE_OUT = 5,
|
||||
RS_PIOLITE_OPCODE_MOVE = 6,
|
||||
RS_PIOLITE_OPCODE_FIFO = 7,
|
||||
RS_PIOLITE_OPCODE_WAIT = 8,
|
||||
RS_PIOLITE_OPCODE_BRANCH = 9,
|
||||
RS_PIOLITE_OPCODE_FLAG = 10
|
||||
} rs_piolite_opcode_e;
|
||||
|
||||
typedef enum {
|
||||
RS_PIOLITE_BRANCH_ALWAYS = 0,
|
||||
RS_PIOLITE_BRANCH_X_ZERO = 1,
|
||||
RS_PIOLITE_BRANCH_Y_ZERO = 2,
|
||||
RS_PIOLITE_BRANCH_DEC_X_NONZERO = 3,
|
||||
RS_PIOLITE_BRANCH_DEC_Y_NONZERO = 4,
|
||||
RS_PIOLITE_BRANCH_TEST_PIN_HIGH = 5,
|
||||
RS_PIOLITE_BRANCH_FIFO_FAILED = 6,
|
||||
RS_PIOLITE_BRANCH_TEST_FLAG_SET = 7
|
||||
} rs_piolite_branch_condition_e;
|
||||
|
||||
typedef enum {
|
||||
RS_PIOLITE_CTRL_NOP = 0,
|
||||
RS_PIOLITE_CTRL_HALT = 1,
|
||||
RS_PIOLITE_CTRL_CLEAR_ISR = 2,
|
||||
RS_PIOLITE_CTRL_CLEAR_OSR = 3
|
||||
} rs_piolite_ctrl_command_e;
|
||||
|
||||
typedef enum {
|
||||
RS_PIOLITE_IN_PINS = 0,
|
||||
RS_PIOLITE_IN_X = 1,
|
||||
RS_PIOLITE_IN_Y = 2,
|
||||
RS_PIOLITE_IN_ZERO = 3
|
||||
} rs_piolite_in_source_e;
|
||||
|
||||
typedef enum {
|
||||
RS_PIOLITE_OUT_PINS = 0,
|
||||
RS_PIOLITE_OUT_DIRECTIONS = 1,
|
||||
RS_PIOLITE_OUT_X = 2,
|
||||
RS_PIOLITE_OUT_Y = 3,
|
||||
RS_PIOLITE_OUT_DISCARD = 4
|
||||
} rs_piolite_out_destination_e;
|
||||
|
||||
typedef enum {
|
||||
RS_PIOLITE_MOVE_ISR = 0,
|
||||
RS_PIOLITE_MOVE_OSR = 1,
|
||||
RS_PIOLITE_MOVE_X = 2,
|
||||
RS_PIOLITE_MOVE_Y = 3,
|
||||
RS_PIOLITE_MOVE_ZERO = 4,
|
||||
RS_PIOLITE_MOVE_ONES = 5
|
||||
} rs_piolite_move_source_e;
|
||||
|
||||
typedef enum { RS_PIOLITE_FIFO_PULL = 0, RS_PIOLITE_FIFO_PUSH = 1 } rs_piolite_fifo_operation_e;
|
||||
|
||||
typedef enum {
|
||||
RS_PIOLITE_FIFO_NONBLOCKING = 0,
|
||||
RS_PIOLITE_FIFO_BLOCKING = 1
|
||||
} rs_piolite_fifo_mode_e;
|
||||
|
||||
typedef enum { RS_PIOLITE_WAIT_PIN = 0, RS_PIOLITE_WAIT_FLAG = 1 } rs_piolite_wait_source_e;
|
||||
|
||||
typedef enum { RS_PIOLITE_FLAG_CLEAR = 0, RS_PIOLITE_FLAG_SET = 1 } rs_piolite_flag_action_e;
|
||||
|
||||
#endif
|
||||
@@ -380,13 +380,90 @@ slots 0..22 in their respective independent allocations. No IMEM enlargement,
|
||||
padding demonstration, protocol claim, or simultaneous all-application claim
|
||||
is made. P2-P5 requirements remain open as listed below.
|
||||
|
||||
## PIOLITE-P2 Standalone Implementation Checkpoint (2026-10-30)
|
||||
|
||||
The P2 implementation provides the two-SM RTL core, complete APB1.0 CSR map,
|
||||
handwritten C mirror/parity, pre-edge Common FIFO wrappers, local GPIO/stream
|
||||
lifecycle adapters, independent BFMs/assertions, and a fail-closed standalone
|
||||
driver. It has `phase_accepted=false`; Tiny integration, central DMA, profiles,
|
||||
dependencies, baselines, and the HAL loader remain unchanged.
|
||||
|
||||
Final source-bound roots are
|
||||
`build/ihp130-tiny-2026-10-30-05-15-b09f17c85292/meta/piolite-p2/` and
|
||||
`build/ics55-tiny-2026-10-30-05-15-c524d3273238/meta/piolite-p2/`, both at
|
||||
24 MHz. The following commands passed on each PDK (substitute the shown config
|
||||
and matching root):
|
||||
|
||||
```sh
|
||||
make CONFIG=configs/ci/ihp130-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 SIMU=VERILATOR piolite-p2-lint piolite-p2-rtl
|
||||
make CONFIG=configs/ci/ihp130-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 SIMU=IVERILOG piolite-p2-rtl
|
||||
make CONFIG=configs/ci/ihp130-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 piolite-p2-formal
|
||||
make CONFIG=configs/ci/ihp130-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 SYNTH=YOSYS piolite-p2-synth
|
||||
make CONFIG=configs/ci/ihp130-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 SIMU=IVERILOG piolite-p2-netsim
|
||||
make CONFIG=configs/ci/ihp130-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 STA=OPENSTA piolite-p2-sta
|
||||
make CONFIG=configs/ci/ihp130-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 piolite-p2-report
|
||||
make CONFIG=configs/ci/ics55-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 SIMU=VERILATOR piolite-p2-lint piolite-p2-rtl
|
||||
make CONFIG=configs/ci/ics55-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 SIMU=IVERILOG piolite-p2-rtl
|
||||
make CONFIG=configs/ci/ics55-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 piolite-p2-formal
|
||||
make CONFIG=configs/ci/ics55-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 SYNTH=YOSYS piolite-p2-synth
|
||||
make CONFIG=configs/ci/ics55-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 SIMU=IVERILOG piolite-p2-netsim
|
||||
make CONFIG=configs/ci/ics55-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 STA=OPENSTA piolite-p2-sta
|
||||
make CONFIG=configs/ci/ics55-tiny.mk BUILD_TIMESTAMP=2026-10-30-05-15 piolite-p2-report
|
||||
```
|
||||
|
||||
`piolite_exhaustive_tb.sv` executes every one of the 65536 instruction words in
|
||||
`piolite_sm` on both simulators. It records exactly 22257 legal and 43279
|
||||
rejected encodings and compares complete one-tick runtime state, flag and FIFO
|
||||
side effects against P1-generated records. Twenty-nine longer differential
|
||||
programs cover divider/delay, packing, branch/wrap, and fault boundaries. The
|
||||
independent PL-V checklist closes PL-V01/03/04/05/06/07/08 at P2 scope: all
|
||||
119 CSR offsets and access classes, reserved/unaligned/partial-strobe and
|
||||
unimplemented-SM errors, X/Y 0/1/65535, exact shift thresholds, pre-edge and
|
||||
512-operation randomized FIFO activity, command/commit and first-fault races,
|
||||
saturating counters, and paused execution while DMA progresses. Each required
|
||||
`PIOLITE_P2_COVER` marker occurs exactly once per simulator. A two-word
|
||||
region gives every non-HALT candidate a PC1 successor, and a delay-1 SET_COUNT
|
||||
makes EXEC_PHASE non-vacuous. PAUSE is accepted on the first execution tick for
|
||||
IN/autopush, OUT/autopull/pins, SET_COUNT, FLAG, and HALT candidates; every
|
||||
pre-commit PC, shift/count, X/Y, pin, FIFO/flag/event, divider, and retirement
|
||||
value is retained. Illegal
|
||||
configuration and IMEM writes return PSLVERR atomically without changing
|
||||
requested state in RUNNING, CLOSING, and DISABLED-with-active-transport cases.
|
||||
|
||||
Formal evidence retains FIFO basecase and temporal induction at depth 16 (17
|
||||
assertions), adds full-top BMC at depth 12 (28 assertions), and completes a
|
||||
24-step cover campaign with all six required Yosys witnesses: APB PREADY, SM
|
||||
RUNNING, SM commit, CLOSING, nonzero fault, and nonzero OE. Exact assertion-name
|
||||
sets and each indexed cover-site witness are mandatory; the driver rejects a
|
||||
deleted assertion and a deleted named witness in generated negative self-checks.
|
||||
Bounds and assumptions are recorded in `formal-bounds.json`: synchronous reset
|
||||
release, aligned/full-word APB accesses, stable GPIO readiness/filtering with
|
||||
arbitrary ownership loss, inactive DMA job-owned flags, and symbolic stream
|
||||
sidebands. VCD is disabled only for cover; YW witnesses are mandatory. Bounded
|
||||
proofs do not establish external liveness, analog CDC, or product timing.
|
||||
`setup_helpers.py` is included in the refreshed producer identity.
|
||||
|
||||
Both block flows verify two SMs, one 512-bit IMEM, four 256-bit FIFO payload
|
||||
arrays, no latches, foreign cells, or four-SM geometry. IHP130 reports 34070
|
||||
cells/area 449058.897; ICS55 reports 31133 cells/area 67359.88. Mapped-netlist
|
||||
simulation and aggregate reports pass on both. OpenSTA reports setup/hold
|
||||
WNS/TNS 0.00 with the retained unconstrained-endpoint and missing-I/O-delay
|
||||
warnings; these are observational results, not timing closure or a rate claim.
|
||||
|
||||
Focused host validation passed 199 tests plus Ruff, RTL format/style/readiness,
|
||||
C format/policy/host, and whitespace checks. The normative `docs/ip/piolite.md`
|
||||
hash remains a producer input; this verification ledger is post-run reporting
|
||||
output and no longer self-invalidates evidence. P3+ integration, central-DMA
|
||||
V2.1/GPIO/RCU drain and handoff, firmware/application qualification, final
|
||||
PVT/physical/pad/silicon evidence, and independent phase review remain open.
|
||||
|
||||
## Freeze Record and Remaining Gaps
|
||||
|
||||
| Phase | Status |
|
||||
| --- | --- |
|
||||
| PIOLITE-P0 | Documentation freeze complete; local links/anchor, command dry-runs, unchanged legacy/R2 IDs and QFN64/ALT tables, documentation-only scope and whitespace checks passed on 2026-10-04 |
|
||||
| PIOLITE-P1 | Host model, assembler, focused tests and actual 32-word footprints added on 2026-10-28; `phase_accepted: false`; Tiny integration and protocol qualification remain absent |
|
||||
| PIOLITE-P2 | Not started: core, CSR/ISA parity and standalone RTL/formal/synthesis evidence absent |
|
||||
| PIOLITE-P2 | Standalone core/CSR/parity, exhaustive and directed dual-simulator, PL-V checklist, full-top formal, IHP130/ICS55 synthesis/netlist and observational STA evidence added on 2026-10-30; `phase_accepted=false`; independent review and P3+ product acceptance remain open |
|
||||
| PIOLITE-P3 | Not started: R2-dependent Tiny wiring, DMA V2.1 and GPIO/RCU/HAL lifecycle absent |
|
||||
| PIOLITE-P4 | Not started: complete SDK/application and dual-simulator/contention qualification absent |
|
||||
| PIOLITE-P5 | Not started: PIO-inclusive IHP130 timing, physical, Pad/package and silicon evidence absent |
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# PIOLITE-P2 internal 24 MHz PCLK observation; product timing is not qualified.
|
||||
create_clock -name pclk -period 41.666666667 [get_ports clk_i]
|
||||
# No blanket false path on reset or GPIO/stream inputs. Report uncharacterized boundaries.
|
||||
@@ -0,0 +1,13 @@
|
||||
foreach name {PIOLITE_NETLIST PIOLITE_LIBERTY PIOLITE_REPORT_DIR} {
|
||||
if {![info exists ::env($name)]} { error "missing $name" }
|
||||
}
|
||||
read_liberty $::env(PIOLITE_LIBERTY)
|
||||
read_verilog $::env(PIOLITE_NETLIST)
|
||||
link_design apb4_piolite_block_top
|
||||
read_sdc [file join [file dirname [info script]] piolite_block.sdc]
|
||||
check_setup -verbose > $::env(PIOLITE_REPORT_DIR)/constraints.rpt
|
||||
report_checks -path_delay min_max -group_path_count 20 > $::env(PIOLITE_REPORT_DIR)/paths.rpt
|
||||
report_wns -max > $::env(PIOLITE_REPORT_DIR)/setup_wns.rpt
|
||||
report_tns -max > $::env(PIOLITE_REPORT_DIR)/setup_tns.rpt
|
||||
report_wns -min > $::env(PIOLITE_REPORT_DIR)/hold_wns.rpt
|
||||
report_tns -min > $::env(PIOLITE_REPORT_DIR)/hold_tns.rpt
|
||||
@@ -0,0 +1,103 @@
|
||||
module apb4_piolite_block_top (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic [31:0] clock_hz_i,
|
||||
input logic [31:0] user_select_i,
|
||||
input logic [31:0] user_status_i,
|
||||
input logic [31:0] filter_enable_i,
|
||||
input logic gpio_ready_i,
|
||||
input logic [31:0] gpio_input_i,
|
||||
output logic [31:0] gpio_value_o,
|
||||
output logic [31:0] gpio_oe_o,
|
||||
input logic dma_tx_job_i,
|
||||
input logic dma_rx_job_i,
|
||||
input logic dma_tx_valid_i,
|
||||
output logic dma_tx_ready_o,
|
||||
input logic [31:0] dma_tx_data_i,
|
||||
input logic [ 3:0] dma_tx_keep_i,
|
||||
input logic [ 3:0] dma_tx_strb_i,
|
||||
input logic dma_tx_id_i,
|
||||
input logic dma_tx_dest_i,
|
||||
input logic dma_tx_user_i,
|
||||
input logic dma_tx_last_i,
|
||||
output logic dma_rx_valid_o,
|
||||
input logic dma_rx_ready_i,
|
||||
output logic [31:0] dma_rx_data_o,
|
||||
output logic [ 3:0] dma_rx_keep_o,
|
||||
output logic [ 3:0] dma_rx_strb_o,
|
||||
output logic dma_rx_last_o,
|
||||
output logic dma_rx_id_o,
|
||||
output logic dma_rx_dest_o,
|
||||
output logic dma_rx_user_o,
|
||||
output logic dma_tx_admit_o,
|
||||
output logic dma_rx_admit_o,
|
||||
output logic irq_o,
|
||||
input logic [31:0] paddr_i,
|
||||
input logic [ 2:0] pprot_i,
|
||||
input logic psel_i,
|
||||
input logic penable_i,
|
||||
input logic pwrite_i,
|
||||
input logic [31:0] pwdata_i,
|
||||
input logic [ 3:0] pstrb_i,
|
||||
output logic pready_o,
|
||||
output logic [31:0] prdata_o,
|
||||
`ifdef PIOLITE_FORMAL_TOP
|
||||
output logic [43:0] formal_sm0_o,
|
||||
`endif
|
||||
output logic pslverr_o
|
||||
);
|
||||
apb4_if u_apb (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
assign u_apb.paddr = paddr_i;
|
||||
assign u_apb.pprot = pprot_i;
|
||||
assign u_apb.psel = psel_i;
|
||||
assign u_apb.penable = penable_i;
|
||||
assign u_apb.pwrite = pwrite_i;
|
||||
assign u_apb.pwdata = pwdata_i;
|
||||
assign u_apb.pstrb = pstrb_i;
|
||||
assign pready_o = u_apb.pready;
|
||||
assign prdata_o = u_apb.prdata;
|
||||
assign pslverr_o = u_apb.pslverr;
|
||||
|
||||
apb4_piolite u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clock_hz_i (clock_hz_i),
|
||||
.user_select_i (user_select_i),
|
||||
.user_status_i (user_status_i),
|
||||
.filter_enable_i(filter_enable_i),
|
||||
.gpio_ready_i (gpio_ready_i),
|
||||
.gpio_input_i (gpio_input_i),
|
||||
.gpio_value_o (gpio_value_o),
|
||||
.gpio_oe_o (gpio_oe_o),
|
||||
.dma_tx_job_i (dma_tx_job_i),
|
||||
.dma_rx_job_i (dma_rx_job_i),
|
||||
.dma_tx_valid_i (dma_tx_valid_i),
|
||||
.dma_tx_ready_o (dma_tx_ready_o),
|
||||
.dma_tx_data_i (dma_tx_data_i),
|
||||
.dma_tx_keep_i (dma_tx_keep_i),
|
||||
.dma_tx_strb_i (dma_tx_strb_i),
|
||||
.dma_tx_id_i (dma_tx_id_i),
|
||||
.dma_tx_dest_i (dma_tx_dest_i),
|
||||
.dma_tx_user_i (dma_tx_user_i),
|
||||
.dma_tx_last_i (dma_tx_last_i),
|
||||
.dma_rx_valid_o (dma_rx_valid_o),
|
||||
.dma_rx_ready_i (dma_rx_ready_i),
|
||||
.dma_rx_data_o (dma_rx_data_o),
|
||||
.dma_rx_keep_o (dma_rx_keep_o),
|
||||
.dma_rx_strb_o (dma_rx_strb_o),
|
||||
.dma_rx_last_o (dma_rx_last_o),
|
||||
.dma_rx_id_o (dma_rx_id_o),
|
||||
.dma_rx_dest_o (dma_rx_dest_o),
|
||||
.dma_rx_user_o (dma_rx_user_o),
|
||||
.dma_tx_admit_o (dma_tx_admit_o),
|
||||
.dma_rx_admit_o (dma_rx_admit_o),
|
||||
.irq_o (irq_o),
|
||||
`ifdef PIOLITE_FORMAL_TOP
|
||||
.formal_sm0_o (formal_sm0_o),
|
||||
`endif
|
||||
.apb4 (u_apb)
|
||||
);
|
||||
endmodule
|
||||
@@ -0,0 +1,184 @@
|
||||
module apb4_piolite (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic [31:0] clock_hz_i,
|
||||
input logic [31:0] user_select_i,
|
||||
input logic [31:0] user_status_i,
|
||||
input logic [31:0] filter_enable_i,
|
||||
input logic gpio_ready_i,
|
||||
input logic [31:0] gpio_input_i,
|
||||
output logic [31:0] gpio_value_o,
|
||||
output logic [31:0] gpio_oe_o,
|
||||
input logic dma_tx_job_i,
|
||||
input logic dma_rx_job_i,
|
||||
input logic dma_tx_valid_i,
|
||||
output logic dma_tx_ready_o,
|
||||
input logic [31:0] dma_tx_data_i,
|
||||
input logic [ 3:0] dma_tx_keep_i,
|
||||
input logic [ 3:0] dma_tx_strb_i,
|
||||
input logic dma_tx_id_i,
|
||||
input logic dma_tx_dest_i,
|
||||
input logic dma_tx_user_i,
|
||||
input logic dma_tx_last_i,
|
||||
output logic dma_rx_valid_o,
|
||||
input logic dma_rx_ready_i,
|
||||
output logic [31:0] dma_rx_data_o,
|
||||
output logic [ 3:0] dma_rx_keep_o,
|
||||
output logic [ 3:0] dma_rx_strb_o,
|
||||
output logic dma_rx_last_o,
|
||||
output logic dma_rx_id_o,
|
||||
output logic dma_rx_dest_o,
|
||||
output logic dma_rx_user_o,
|
||||
output logic dma_tx_admit_o,
|
||||
output logic dma_rx_admit_o,
|
||||
output logic irq_o,
|
||||
`ifdef PIOLITE_FORMAL_TOP
|
||||
output logic [43:0] formal_sm0_o,
|
||||
`endif
|
||||
apb4_if.slave apb4
|
||||
);
|
||||
piolite_if u_ctrl ();
|
||||
logic [ 1:0] s_guard_fault;
|
||||
logic [ 7:0] s_guard_fault_code [0:1];
|
||||
logic [ 1:0] s_session_live;
|
||||
logic s_stream_fault;
|
||||
logic s_stream_fault_sm;
|
||||
logic s_dma_tx_push;
|
||||
logic [31:0] s_dma_tx_fifo_data;
|
||||
logic s_dma_tx_sm;
|
||||
logic s_dma_tx_accept;
|
||||
logic s_dma_rx_pop;
|
||||
logic s_dma_rx_sm;
|
||||
logic [31:0] s_dma_rx_fifo_data;
|
||||
logic s_dma_rx_accept;
|
||||
logic s_tx_held;
|
||||
logic s_rx_held;
|
||||
logic s_tx_closing;
|
||||
logic s_rx_closing;
|
||||
|
||||
always_comb begin
|
||||
for (integer index = 0; index < 2; index++) begin
|
||||
s_session_live[index] = (u_ctrl.runtime[index].state != piolite_pkg::Disabled) ||
|
||||
(u_ctrl.tx_level[index] != 4'd0) || (u_ctrl.rx_level[index] != 4'd0) ||
|
||||
(u_ctrl.tx_bind[31] && (u_ctrl.tx_bind[0] == index[0])) ||
|
||||
(u_ctrl.rx_bind[31] && (u_ctrl.rx_bind[0] == index[0])) ||
|
||||
(u_ctrl.tx_bind[31] && (u_ctrl.tx_bind[0] == index[0]) &&
|
||||
(dma_tx_job_i || s_tx_held)) ||
|
||||
(u_ctrl.rx_bind[31] && (u_ctrl.rx_bind[0] == index[0]) &&
|
||||
(dma_rx_job_i || s_rx_held));
|
||||
end
|
||||
dma_tx_admit_o = u_ctrl.tx_bind[31] && !s_tx_closing && !s_guard_fault[u_ctrl.tx_bind[0]];
|
||||
dma_rx_admit_o = u_ctrl.rx_bind[31] && !s_rx_closing && !s_guard_fault[u_ctrl.rx_bind[0]];
|
||||
end
|
||||
|
||||
piolite_gpio_guard u_gpio_guard (
|
||||
.cfg_i (u_ctrl.cfg),
|
||||
.runtime_i (u_ctrl.runtime),
|
||||
.session_live_i (s_session_live),
|
||||
.user_select_i (user_select_i),
|
||||
.user_status_i (user_status_i),
|
||||
.filter_enable_i (filter_enable_i),
|
||||
.gpio_ready_i (gpio_ready_i),
|
||||
.start_guard_ok_o(u_ctrl.start_guard_ok),
|
||||
.cpu_guard_ok_o (u_ctrl.cpu_guard_ok),
|
||||
.fault_valid_o (s_guard_fault),
|
||||
.fault_code_o (s_guard_fault_code),
|
||||
.gpio_value_o (gpio_value_o),
|
||||
.gpio_oe_o (gpio_oe_o)
|
||||
);
|
||||
|
||||
piolite_dma_adapter u_dma_adapter (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.block_reset_i (u_ctrl.cmd_block_reset),
|
||||
.sm_reset_i (u_ctrl.cmd_sm_reset),
|
||||
.runtime_i (u_ctrl.runtime),
|
||||
.tx_bind_i (u_ctrl.tx_bind),
|
||||
.rx_bind_i (u_ctrl.rx_bind),
|
||||
.guard_fault_i (s_guard_fault),
|
||||
.rx_level_i (u_ctrl.rx_level),
|
||||
.tx_valid_i (dma_tx_valid_i),
|
||||
.tx_ready_o (dma_tx_ready_o),
|
||||
.tx_data_i (dma_tx_data_i),
|
||||
.tx_keep_i (dma_tx_keep_i),
|
||||
.tx_strb_i (dma_tx_strb_i),
|
||||
.tx_id_i (dma_tx_id_i),
|
||||
.tx_dest_i (dma_tx_dest_i),
|
||||
.tx_user_i (dma_tx_user_i),
|
||||
.tx_last_i (dma_tx_last_i),
|
||||
.tx_fifo_push_o (s_dma_tx_push),
|
||||
.tx_fifo_data_o (s_dma_tx_fifo_data),
|
||||
.tx_fifo_sm_o (s_dma_tx_sm),
|
||||
.tx_fifo_accept_i (s_dma_tx_accept),
|
||||
.rx_fifo_pop_o (s_dma_rx_pop),
|
||||
.rx_fifo_sm_o (s_dma_rx_sm),
|
||||
.rx_fifo_data_i (s_dma_rx_fifo_data),
|
||||
.rx_fifo_accept_i (s_dma_rx_accept),
|
||||
.rx_valid_o (dma_rx_valid_o),
|
||||
.rx_ready_i (dma_rx_ready_i),
|
||||
.rx_data_o (dma_rx_data_o),
|
||||
.rx_keep_o (dma_rx_keep_o),
|
||||
.rx_strb_o (dma_rx_strb_o),
|
||||
.rx_last_o (dma_rx_last_o),
|
||||
.rx_id_o (dma_rx_id_o),
|
||||
.rx_dest_o (dma_rx_dest_o),
|
||||
.rx_user_o (dma_rx_user_o),
|
||||
.tx_held_o (s_tx_held),
|
||||
.rx_held_o (s_rx_held),
|
||||
.tx_closing_o (s_tx_closing),
|
||||
.rx_closing_o (s_rx_closing),
|
||||
.stream_fault_valid_o(s_stream_fault),
|
||||
.stream_fault_sm_o (s_stream_fault_sm)
|
||||
);
|
||||
|
||||
piolite_core u_core (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.pin_inputs_i (gpio_input_i),
|
||||
.guard_fault_i (s_guard_fault),
|
||||
.guard_fault_code_i(s_guard_fault_code),
|
||||
.stream_fault_i (s_stream_fault),
|
||||
.stream_fault_sm_i (s_stream_fault_sm),
|
||||
.dma_tx_push_i (s_dma_tx_push),
|
||||
.dma_tx_data_i (s_dma_tx_fifo_data),
|
||||
.dma_tx_sm_i (s_dma_tx_sm),
|
||||
.dma_tx_accept_o (s_dma_tx_accept),
|
||||
.dma_rx_pop_i (s_dma_rx_pop),
|
||||
.dma_rx_sm_i (s_dma_rx_sm),
|
||||
.dma_rx_data_o (s_dma_rx_fifo_data),
|
||||
.dma_rx_accept_o (s_dma_rx_accept),
|
||||
`ifdef PIOLITE_FORMAL_TOP
|
||||
.formal_sm0_o (formal_sm0_o),
|
||||
`endif
|
||||
.ctrl (u_ctrl)
|
||||
);
|
||||
|
||||
piolite_reg u_reg (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clock_hz_i (clock_hz_i),
|
||||
.user_select_i(user_select_i),
|
||||
.tx_job_i (dma_tx_job_i),
|
||||
.rx_job_i (dma_rx_job_i),
|
||||
.tx_held_i (s_tx_held),
|
||||
.rx_held_i (s_rx_held),
|
||||
.tx_closing_i (s_tx_closing),
|
||||
.rx_closing_i (s_rx_closing),
|
||||
.irq_o (irq_o),
|
||||
.apb4 (apb4),
|
||||
.ctrl (u_ctrl)
|
||||
);
|
||||
`ifdef FORMAL
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (rst_n_i) begin
|
||||
assert ((u_ctrl.runtime[0].output_oe & u_ctrl.runtime[1].output_oe) == 32'd0);
|
||||
for (integer index = 0; index < 2; index++) begin
|
||||
if (u_ctrl.runtime[index].state == piolite_pkg::Closing) begin
|
||||
assert (u_ctrl.runtime[index].output_oe == 32'd0);
|
||||
assert (!u_ctrl.cpu_tx_push[index] && !u_ctrl.cpu_rx_pop[index]);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
@@ -0,0 +1,172 @@
|
||||
module piolite_core (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic [31:0] pin_inputs_i,
|
||||
input logic [ 1:0] guard_fault_i,
|
||||
input logic [ 7:0] guard_fault_code_i[0:1],
|
||||
input logic stream_fault_i,
|
||||
input logic stream_fault_sm_i,
|
||||
input logic dma_tx_push_i,
|
||||
input logic [31:0] dma_tx_data_i,
|
||||
input logic dma_tx_sm_i,
|
||||
output logic dma_tx_accept_o,
|
||||
input logic dma_rx_pop_i,
|
||||
input logic dma_rx_sm_i,
|
||||
output logic [31:0] dma_rx_data_o,
|
||||
output logic dma_rx_accept_o,
|
||||
`ifdef PIOLITE_FORMAL_TOP
|
||||
output logic [43:0] formal_sm0_o,
|
||||
`endif
|
||||
piolite_if.core ctrl
|
||||
);
|
||||
logic [ 4:0] s_instruction_addr [0:1];
|
||||
logic [15:0] s_instruction_data [0:1];
|
||||
logic s_instruction_valid[0:1];
|
||||
logic [ 3:0] s_sm_flag_set [0:1];
|
||||
logic [ 3:0] s_sm_flag_clear [0:1];
|
||||
logic s_sm_tx_pop [0:1];
|
||||
logic s_sm_rx_push [0:1];
|
||||
logic [31:0] s_sm_rx_data [0:1];
|
||||
logic [31:0] s_tx_pop_data [0:1];
|
||||
logic [31:0] s_rx_pop_data [0:1];
|
||||
logic s_tx_push_accept [0:1];
|
||||
logic s_tx_pop_accept [0:1];
|
||||
logic s_rx_push_accept [0:1];
|
||||
logic s_rx_pop_accept [0:1];
|
||||
logic s_tx_full [0:1];
|
||||
logic s_tx_empty [0:1];
|
||||
logic s_rx_full [0:1];
|
||||
logic s_rx_empty [0:1];
|
||||
logic [31:0] s_flags_d;
|
||||
logic [31:0] s_flags_q;
|
||||
logic [ 3:0] s_flag_set;
|
||||
logic [ 3:0] s_flag_clear;
|
||||
logic s_unused;
|
||||
`ifdef PIOLITE_FORMAL_TOP
|
||||
logic s_sm_formal_commit[0:1];
|
||||
|
||||
assign formal_sm0_o = {
|
||||
ctrl.runtime[0].state,
|
||||
s_sm_formal_commit[0],
|
||||
ctrl.runtime[0].fault_code,
|
||||
ctrl.runtime[0].output_oe
|
||||
};
|
||||
`endif
|
||||
assign s_unused = ^{s_tx_pop_accept[0], s_tx_pop_accept[1], s_rx_push_accept[0],
|
||||
s_rx_push_accept[1], s_tx_full[0], s_tx_full[1], s_rx_empty[0], s_rx_empty[1]};
|
||||
assign ctrl.flags = s_flags_q[3:0];
|
||||
assign s_flag_set = ctrl.flag_set | s_sm_flag_set[0] | s_sm_flag_set[1];
|
||||
assign s_flag_clear = ctrl.flag_clear | s_sm_flag_clear[0] | s_sm_flag_clear[1];
|
||||
always_comb begin
|
||||
s_flags_d = ctrl.cmd_block_reset ? 32'd0 :
|
||||
((s_flags_q & ~{28'd0, s_flag_clear}) | {28'd0, s_flag_set});
|
||||
dma_tx_accept_o = dma_tx_push_i && s_tx_push_accept[dma_tx_sm_i];
|
||||
dma_rx_accept_o = dma_rx_pop_i && s_rx_pop_accept[dma_rx_sm_i];
|
||||
dma_rx_data_o = s_rx_pop_data[dma_rx_sm_i];
|
||||
end
|
||||
|
||||
dffr #(
|
||||
.DATA_WIDTH(32)
|
||||
) u_flags (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_flags_d),
|
||||
.dat_o (s_flags_q)
|
||||
);
|
||||
|
||||
piolite_imem u_imem (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clear_valid_i(ctrl.cmd_block_reset),
|
||||
.write_i (ctrl.imem_write),
|
||||
.write_addr_i (ctrl.imem_write_addr),
|
||||
.write_data_i (ctrl.imem_write_data),
|
||||
.sm0_addr_i (s_instruction_addr[0]),
|
||||
.sm0_data_o (s_instruction_data[0]),
|
||||
.sm0_valid_o (s_instruction_valid[0]),
|
||||
.sm1_addr_i (s_instruction_addr[1]),
|
||||
.sm1_data_o (s_instruction_data[1]),
|
||||
.sm1_valid_o (s_instruction_valid[1]),
|
||||
.apb_addr_i (ctrl.imem_read_addr),
|
||||
.apb_data_o (ctrl.imem_read_data),
|
||||
.valid_o (ctrl.imem_valid)
|
||||
);
|
||||
|
||||
for (genvar index = 0; index < 2; index++) begin : gen_sm
|
||||
logic s_external_tx_push;
|
||||
logic s_external_rx_pop;
|
||||
logic [31:0] s_external_tx_data;
|
||||
|
||||
assign s_external_tx_push = ctrl.cpu_tx_push[index] ||
|
||||
(dma_tx_push_i && (dma_tx_sm_i == index[0]));
|
||||
assign s_external_tx_data = (dma_tx_push_i && (dma_tx_sm_i == index[0])) ?
|
||||
dma_tx_data_i : ctrl.cpu_tx_data;
|
||||
assign s_external_rx_pop = ctrl.cpu_rx_pop[index] ||
|
||||
(dma_rx_pop_i && (dma_rx_sm_i == index[0]));
|
||||
assign ctrl.cpu_rx_data[index] = s_rx_pop_data[index];
|
||||
|
||||
piolite_sm u_sm (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.cfg_i (ctrl.cfg[index]),
|
||||
.start_i (ctrl.cmd_start[index]),
|
||||
.pause_i (ctrl.cmd_pause[index]),
|
||||
.resume_i (ctrl.cmd_resume[index]),
|
||||
.disable_i (ctrl.cmd_disable[index]),
|
||||
.abort_i (ctrl.cmd_abort[index]),
|
||||
.sm_reset_i (ctrl.cmd_sm_reset[index]),
|
||||
.block_reset_i (ctrl.cmd_block_reset),
|
||||
.event_clear_i (ctrl.event_clear[index]),
|
||||
.guard_fault_i (guard_fault_i[index]),
|
||||
.guard_fault_code_i (guard_fault_code_i[index]),
|
||||
.stream_fault_i (stream_fault_i && (stream_fault_sm_i == index[0])),
|
||||
.instruction_i (s_instruction_data[index]),
|
||||
.instruction_valid_i(s_instruction_valid[index]),
|
||||
.instruction_addr_o (s_instruction_addr[index]),
|
||||
.flags_i (s_flags_q),
|
||||
.pin_inputs_i (pin_inputs_i),
|
||||
.flag_set_o (s_sm_flag_set[index]),
|
||||
.flag_clear_o (s_sm_flag_clear[index]),
|
||||
.tx_empty_i (s_tx_empty[index]),
|
||||
.tx_data_i (s_tx_pop_data[index]),
|
||||
.tx_pop_o (s_sm_tx_pop[index]),
|
||||
.rx_full_i (s_rx_full[index]),
|
||||
.rx_push_o (s_sm_rx_push[index]),
|
||||
.rx_data_o (s_sm_rx_data[index]),
|
||||
`ifdef PIOLITE_FORMAL_TOP
|
||||
.formal_commit_o (s_sm_formal_commit[index]),
|
||||
`endif
|
||||
.runtime_o (ctrl.runtime[index])
|
||||
);
|
||||
|
||||
piolite_fifo u_tx_fifo (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.flush_i (ctrl.cmd_sm_reset[index] || ctrl.cmd_block_reset),
|
||||
.push_i (s_external_tx_push),
|
||||
.push_data_i (s_external_tx_data),
|
||||
.pop_i (s_sm_tx_pop[index]),
|
||||
.pop_data_o (s_tx_pop_data[index]),
|
||||
.push_accept_o(s_tx_push_accept[index]),
|
||||
.pop_accept_o (s_tx_pop_accept[index]),
|
||||
.full_o (s_tx_full[index]),
|
||||
.empty_o (s_tx_empty[index]),
|
||||
.count_o (ctrl.tx_level[index])
|
||||
);
|
||||
|
||||
piolite_fifo u_rx_fifo (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.flush_i (ctrl.cmd_sm_reset[index] || ctrl.cmd_block_reset),
|
||||
.push_i (s_sm_rx_push[index]),
|
||||
.push_data_i (s_sm_rx_data[index]),
|
||||
.pop_i (s_external_rx_pop),
|
||||
.pop_data_o (s_rx_pop_data[index]),
|
||||
.push_accept_o(s_rx_push_accept[index]),
|
||||
.pop_accept_o (s_rx_pop_accept[index]),
|
||||
.full_o (s_rx_full[index]),
|
||||
.empty_o (s_rx_empty[index]),
|
||||
.count_o (ctrl.rx_level[index])
|
||||
);
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,210 @@
|
||||
`ifndef RETROSOC_PIOLITE_DEFINE_SVH
|
||||
`define RETROSOC_PIOLITE_DEFINE_SVH
|
||||
`define APB4_PIOLITE__IP_ID 12'h000
|
||||
`define APB4_PIOLITE__IP_VERSION 12'h004
|
||||
`define APB4_PIOLITE__ISA_VERSION 12'h008
|
||||
`define APB4_PIOLITE__CAPABILITY 12'h00c
|
||||
`define APB4_PIOLITE__ROUTABLE_MASK 12'h010
|
||||
`define APB4_PIOLITE__CLOCK_HZ 12'h014
|
||||
`define APB4_PIOLITE__FLAGS 12'h018
|
||||
`define APB4_PIOLITE__FLAG_SET 12'h01c
|
||||
`define APB4_PIOLITE__FLAG_CLEAR 12'h020
|
||||
`define APB4_PIOLITE__IRQ_STATUS 12'h024
|
||||
`define APB4_PIOLITE__IRQ_ENABLE 12'h028
|
||||
`define APB4_PIOLITE__TX_BIND 12'h02c
|
||||
`define APB4_PIOLITE__RX_BIND 12'h030
|
||||
`define APB4_PIOLITE__TRANSPORT_STATUS 12'h034
|
||||
`define APB4_PIOLITE__IMEM_VALID 12'h038
|
||||
`define APB4_PIOLITE__OWNED_MASK 12'h03c
|
||||
`define APB4_PIOLITE__START 12'h040
|
||||
`define APB4_PIOLITE__PAUSE 12'h044
|
||||
`define APB4_PIOLITE__RESUME 12'h048
|
||||
`define APB4_PIOLITE__DISABLE 12'h04c
|
||||
`define APB4_PIOLITE__ABORT 12'h050
|
||||
`define APB4_PIOLITE__SM_RESET 12'h054
|
||||
`define APB4_PIOLITE__BLOCK_RESET 12'h058
|
||||
`define APB4_PIOLITE__IMEM_BASE 12'h100
|
||||
`define APB4_PIOLITE__IMEM_END 12'h17c
|
||||
`define APB4_PIOLITE__SM0_BASE 12'h200
|
||||
`define APB4_PIOLITE__SM1_BASE 12'h280
|
||||
`define APB4_PIOLITE__SM_STRIDE 12'h080
|
||||
`define APB4_PIOLITE__SM_STATUS 12'h000
|
||||
`define APB4_PIOLITE__SM_CONFIG 12'h004
|
||||
`define APB4_PIOLITE__SM_DIV 12'h008
|
||||
`define APB4_PIOLITE__SM_PROGRAM 12'h00c
|
||||
`define APB4_PIOLITE__SM_WRAP 12'h010
|
||||
`define APB4_PIOLITE__SM_IN_PINS 12'h014
|
||||
`define APB4_PIOLITE__SM_OUT_PINS 12'h018
|
||||
`define APB4_PIOLITE__SM_SET_PINS 12'h01c
|
||||
`define APB4_PIOLITE__SM_CLAIM_MASK 12'h020
|
||||
`define APB4_PIOLITE__SM_OUTPUT_MASK 12'h024
|
||||
`define APB4_PIOLITE__SM_INITIAL_VALUE 12'h028
|
||||
`define APB4_PIOLITE__SM_INITIAL_OE 12'h02c
|
||||
`define APB4_PIOLITE__SM_INITIAL_XY 12'h030
|
||||
`define APB4_PIOLITE__SM_SHIFT_LIMITS 12'h034
|
||||
`define APB4_PIOLITE__SM_TEST_SELECT 12'h038
|
||||
`define APB4_PIOLITE__SM_WATERMARKS 12'h03c
|
||||
`define APB4_PIOLITE__SM_TXDATA 12'h040
|
||||
`define APB4_PIOLITE__SM_RXDATA 12'h044
|
||||
`define APB4_PIOLITE__SM_FIFO_LEVELS 12'h048
|
||||
`define APB4_PIOLITE__SM_EVENTS 12'h04c
|
||||
`define APB4_PIOLITE__SM_EVENT_ENABLE 12'h050
|
||||
`define APB4_PIOLITE__SM_FAULT 12'h054
|
||||
`define APB4_PIOLITE__SM_PC 12'h058
|
||||
`define APB4_PIOLITE__SM_ISR 12'h05c
|
||||
`define APB4_PIOLITE__SM_OSR 12'h060
|
||||
`define APB4_PIOLITE__SM_XY 12'h064
|
||||
`define APB4_PIOLITE__SM_SHIFT_COUNTS 12'h068
|
||||
`define APB4_PIOLITE__SM_TICK_COUNT 12'h06c
|
||||
`define APB4_PIOLITE__SM_TX_STALL_COUNT 12'h070
|
||||
`define APB4_PIOLITE__SM_RX_STALL_COUNT 12'h074
|
||||
`define APB4_PIOLITE__SM_RETIRED_COUNT 12'h078
|
||||
`define APB4_PIOLITE__SM_EXEC_PHASE 12'h07c
|
||||
`define APB4_PIOLITE__ID_VALUE 32'h50494f4c
|
||||
`define APB4_PIOLITE__VERSION_VALUE 32'h00010000
|
||||
`define APB4_PIOLITE__ISA_VALUE 32'h00010000
|
||||
`define APB4_PIOLITE__CAPABILITY_VALUE 32'h08082002
|
||||
`define APB4_PIOLITE__ROUTES_VALUE 32'hffffffff
|
||||
`define APB4_PIOLITE__DIV_RESET 32'h00000001
|
||||
`define APB4_PIOLITE__SHIFT_LIMITS_RESET 32'h00002020
|
||||
`define APB4_PIOLITE__WATERMARKS_RESET 32'h00000100
|
||||
`define APB4_PIOLITE__IMEM_WORDS 32
|
||||
`define APB4_PIOLITE__IMEM_WORD_STRIDE 4
|
||||
`define APB4_PIOLITE__SM_COUNT 2
|
||||
`define APB4_PIOLITE__FIFO_DEPTH 8
|
||||
`define APB4_PIOLITE__IRQ_MASK 32'h03030f03
|
||||
`define PIOLITE_STATUS_STATE 0
|
||||
`define PIOLITE_STATUS_STALL 4
|
||||
`define PIOLITE_STATUS_LAST_FIFO_FAILED 8
|
||||
`define PIOLITE_CONFIG_AUTOPULL 0
|
||||
`define PIOLITE_CONFIG_AUTOPUSH 1
|
||||
`define PIOLITE_CONFIG_TX_RIGHT 2
|
||||
`define PIOLITE_CONFIG_RX_RIGHT 3
|
||||
`define PIOLITE_CONFIG_TEST_PIN_ENABLE 4
|
||||
`define PIOLITE_DIV_VALUE 0
|
||||
`define PIOLITE_PROGRAM_BASE 0
|
||||
`define PIOLITE_PROGRAM_LEN 8
|
||||
`define PIOLITE_PROGRAM_ENTRY 16
|
||||
`define PIOLITE_WRAP_BOTTOM 0
|
||||
`define PIOLITE_WRAP_TOP 8
|
||||
`define PIOLITE_IN_PINS_BASE 0
|
||||
`define PIOLITE_IN_PINS_WIDTH 8
|
||||
`define PIOLITE_OUT_PINS_BASE 0
|
||||
`define PIOLITE_OUT_PINS_WIDTH 8
|
||||
`define PIOLITE_SET_PINS_BASE 0
|
||||
`define PIOLITE_SET_PINS_WIDTH 8
|
||||
`define PIOLITE_CLAIM_MASK_PINS 0
|
||||
`define PIOLITE_OUTPUT_MASK_PINS 0
|
||||
`define PIOLITE_INITIAL_VALUE_PINS 0
|
||||
`define PIOLITE_INITIAL_OE_PINS 0
|
||||
`define PIOLITE_INITIAL_XY_X 0
|
||||
`define PIOLITE_INITIAL_XY_Y 16
|
||||
`define PIOLITE_SHIFT_LIMITS_TX_BITS 0
|
||||
`define PIOLITE_SHIFT_LIMITS_RX_BITS 8
|
||||
`define PIOLITE_TEST_SELECT_GPIO 0
|
||||
`define PIOLITE_TEST_SELECT_FLAG 8
|
||||
`define PIOLITE_WATERMARKS_TX_LOW 0
|
||||
`define PIOLITE_WATERMARKS_RX_HIGH 8
|
||||
`define PIOLITE_FIFO_LEVELS_TX 0
|
||||
`define PIOLITE_FIFO_LEVELS_RX 8
|
||||
`define PIOLITE_EVENTS_HALT 0
|
||||
`define PIOLITE_EVENTS_ABORT 1
|
||||
`define PIOLITE_EVENTS_FAULT 2
|
||||
`define PIOLITE_EVENTS_TX_STALL 3
|
||||
`define PIOLITE_EVENTS_RX_STALL 4
|
||||
`define PIOLITE_EVENTS_FIFO_FAIL 5
|
||||
`define PIOLITE_EVENT_ENABLE_HALT 0
|
||||
`define PIOLITE_EVENT_ENABLE_ABORT 1
|
||||
`define PIOLITE_EVENT_ENABLE_FAULT 2
|
||||
`define PIOLITE_EVENT_ENABLE_TX_STALL 3
|
||||
`define PIOLITE_EVENT_ENABLE_RX_STALL 4
|
||||
`define PIOLITE_EVENT_ENABLE_FIFO_FAIL 5
|
||||
`define PIOLITE_FAULT_CODE 0
|
||||
`define PIOLITE_FAULT_PC 8
|
||||
`define PIOLITE_SHIFT_COUNTS_ISR 0
|
||||
`define PIOLITE_SHIFT_COUNTS_OSR 8
|
||||
`define PIOLITE_EXEC_PHASE_DIV 0
|
||||
`define PIOLITE_EXEC_PHASE_DELAY 16
|
||||
`define PIOLITE_TX_BIND_SM 0
|
||||
`define PIOLITE_TX_BIND_CHANNEL 8
|
||||
`define PIOLITE_TX_BIND_ENABLE 31
|
||||
`define PIOLITE_RX_BIND_SM 0
|
||||
`define PIOLITE_RX_BIND_CHANNEL 8
|
||||
`define PIOLITE_RX_BIND_ENABLE 31
|
||||
`define PIOLITE_TRANSPORT_STATUS_TX_JOB 0
|
||||
`define PIOLITE_TRANSPORT_STATUS_RX_JOB 1
|
||||
`define PIOLITE_TRANSPORT_STATUS_TX_HELD 2
|
||||
`define PIOLITE_TRANSPORT_STATUS_RX_HELD 3
|
||||
`define PIOLITE_TRANSPORT_STATUS_TX_CLOSING 4
|
||||
`define PIOLITE_TRANSPORT_STATUS_RX_CLOSING 5
|
||||
`define PIOLITE_IRQ_STATUS_SM0 0
|
||||
`define PIOLITE_IRQ_STATUS_SM1 1
|
||||
`define PIOLITE_IRQ_STATUS_FLAGS 8
|
||||
`define PIOLITE_IRQ_STATUS_TX_LEVEL 16
|
||||
`define PIOLITE_IRQ_STATUS_RX_LEVEL 24
|
||||
`define PIOLITE_STATE_DISABLED 0
|
||||
`define PIOLITE_STATE_RUNNING 1
|
||||
`define PIOLITE_STATE_PAUSED 2
|
||||
`define PIOLITE_STATE_HALTED 3
|
||||
`define PIOLITE_STATE_CLOSING 4
|
||||
`define PIOLITE_STALL_NONE 0
|
||||
`define PIOLITE_STALL_PIN 1
|
||||
`define PIOLITE_STALL_FLAG 2
|
||||
`define PIOLITE_STALL_TX 3
|
||||
`define PIOLITE_STALL_RX 4
|
||||
`define PIOLITE_FAULT_NONE 0
|
||||
`define PIOLITE_FAULT_ILLEGAL_INSTRUCTION 1
|
||||
`define PIOLITE_FAULT_INVALID_FETCH 2
|
||||
`define PIOLITE_FAULT_PROGRAM_BOUNDS 3
|
||||
`define PIOLITE_FAULT_SHIFT_BOUNDS 4
|
||||
`define PIOLITE_FAULT_PIN_AUTHORIZATION 5
|
||||
`define PIOLITE_FAULT_OWNERSHIP_FILTER 6
|
||||
`define PIOLITE_FAULT_GPIO_READY 7
|
||||
`define PIOLITE_FAULT_STREAM_BEAT 8
|
||||
`define PIOLITE_OPCODE_CTRL 0
|
||||
`define PIOLITE_OPCODE_SET_PIN 1
|
||||
`define PIOLITE_OPCODE_SET_DIR 2
|
||||
`define PIOLITE_OPCODE_SET_COUNT 3
|
||||
`define PIOLITE_OPCODE_IN 4
|
||||
`define PIOLITE_OPCODE_OUT 5
|
||||
`define PIOLITE_OPCODE_MOVE 6
|
||||
`define PIOLITE_OPCODE_FIFO 7
|
||||
`define PIOLITE_OPCODE_WAIT 8
|
||||
`define PIOLITE_OPCODE_BRANCH 9
|
||||
`define PIOLITE_OPCODE_FLAG 10
|
||||
`define PIOLITE_CTRL_NOP 0
|
||||
`define PIOLITE_CTRL_HALT 1
|
||||
`define PIOLITE_CTRL_CLEAR_ISR 2
|
||||
`define PIOLITE_CTRL_CLEAR_OSR 3
|
||||
`define PIOLITE_IN_PINS 0
|
||||
`define PIOLITE_IN_X 1
|
||||
`define PIOLITE_IN_Y 2
|
||||
`define PIOLITE_IN_ZERO 3
|
||||
`define PIOLITE_OUT_PINS 0
|
||||
`define PIOLITE_OUT_DIRECTIONS 1
|
||||
`define PIOLITE_OUT_X 2
|
||||
`define PIOLITE_OUT_Y 3
|
||||
`define PIOLITE_OUT_DISCARD 4
|
||||
`define PIOLITE_MOVE_ISR 0
|
||||
`define PIOLITE_MOVE_OSR 1
|
||||
`define PIOLITE_MOVE_X 2
|
||||
`define PIOLITE_MOVE_Y 3
|
||||
`define PIOLITE_MOVE_ZERO 4
|
||||
`define PIOLITE_MOVE_ONES 5
|
||||
`define PIOLITE_FIFO_PULL 0
|
||||
`define PIOLITE_FIFO_PUSH 1
|
||||
`define PIOLITE_FIFO_NONBLOCKING 0
|
||||
`define PIOLITE_FIFO_BLOCKING 1
|
||||
`define PIOLITE_WAIT_PIN 0
|
||||
`define PIOLITE_WAIT_FLAG 1
|
||||
`define PIOLITE_BRANCH_ALWAYS 0
|
||||
`define PIOLITE_BRANCH_X_ZERO 1
|
||||
`define PIOLITE_BRANCH_Y_ZERO 2
|
||||
`define PIOLITE_BRANCH_DEC_X_NONZERO 3
|
||||
`define PIOLITE_BRANCH_DEC_Y_NONZERO 4
|
||||
`define PIOLITE_BRANCH_TEST_PIN_HIGH 5
|
||||
`define PIOLITE_BRANCH_FIFO_FAILED 6
|
||||
`define PIOLITE_BRANCH_TEST_FLAG_SET 7
|
||||
`define PIOLITE_FLAG_CLEAR 0
|
||||
`define PIOLITE_FLAG_SET 1
|
||||
`endif
|
||||
@@ -0,0 +1,171 @@
|
||||
module piolite_dma_adapter (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic block_reset_i,
|
||||
input logic [ 1:0] sm_reset_i,
|
||||
input piolite_pkg::sm_runtime_t runtime_i [0:1],
|
||||
input logic [31:0] tx_bind_i,
|
||||
input logic [31:0] rx_bind_i,
|
||||
input logic [ 1:0] guard_fault_i,
|
||||
input logic [ 3:0] rx_level_i [0:1],
|
||||
input logic tx_valid_i,
|
||||
output logic tx_ready_o,
|
||||
input logic [31:0] tx_data_i,
|
||||
input logic [ 3:0] tx_keep_i,
|
||||
input logic [ 3:0] tx_strb_i,
|
||||
input logic tx_id_i,
|
||||
input logic tx_dest_i,
|
||||
input logic tx_user_i,
|
||||
input logic tx_last_i,
|
||||
output logic tx_fifo_push_o,
|
||||
output logic [31:0] tx_fifo_data_o,
|
||||
output logic tx_fifo_sm_o,
|
||||
input logic tx_fifo_accept_i,
|
||||
output logic rx_fifo_pop_o,
|
||||
output logic rx_fifo_sm_o,
|
||||
input logic [31:0] rx_fifo_data_i,
|
||||
input logic rx_fifo_accept_i,
|
||||
output logic rx_valid_o,
|
||||
input logic rx_ready_i,
|
||||
output logic [31:0] rx_data_o,
|
||||
output logic [ 3:0] rx_keep_o,
|
||||
output logic [ 3:0] rx_strb_o,
|
||||
output logic rx_last_o,
|
||||
output logic rx_id_o,
|
||||
output logic rx_dest_o,
|
||||
output logic rx_user_o,
|
||||
output logic tx_held_o,
|
||||
output logic rx_held_o,
|
||||
output logic tx_closing_o,
|
||||
output logic rx_closing_o,
|
||||
output logic stream_fault_valid_o,
|
||||
output logic stream_fault_sm_o
|
||||
);
|
||||
logic s_tx_held_d;
|
||||
logic s_tx_held_q;
|
||||
logic s_tx_sm_d;
|
||||
logic s_tx_sm_q;
|
||||
logic [31:0] s_tx_data_d;
|
||||
logic [31:0] s_tx_data_q;
|
||||
logic s_rx_held_d;
|
||||
logic s_rx_held_q;
|
||||
logic s_rx_sm_d;
|
||||
logic s_rx_sm_q;
|
||||
logic [31:0] s_rx_data_d;
|
||||
logic [31:0] s_rx_data_q;
|
||||
logic s_tx_closing;
|
||||
logic s_rx_closing;
|
||||
logic s_tx_accept;
|
||||
logic s_rx_accept;
|
||||
logic s_tx_beat_valid;
|
||||
logic s_unused;
|
||||
|
||||
assign s_tx_closing = s_tx_held_q ? (runtime_i[s_tx_sm_q].state == piolite_pkg::Closing) :
|
||||
(tx_bind_i[31] && (runtime_i[tx_bind_i[0]].state == piolite_pkg::Closing));
|
||||
assign s_rx_closing = s_rx_held_q ? (runtime_i[s_rx_sm_q].state == piolite_pkg::Closing) :
|
||||
(rx_bind_i[31] && (runtime_i[rx_bind_i[0]].state == piolite_pkg::Closing));
|
||||
assign tx_ready_o = tx_bind_i[31] && (s_tx_closing ||
|
||||
(!s_tx_held_q && !guard_fault_i[tx_bind_i[0]]));
|
||||
assign s_tx_accept = tx_valid_i && tx_ready_o;
|
||||
assign s_tx_beat_valid = (tx_keep_i == 4'hf) && (tx_strb_i == 4'hf) &&
|
||||
!tx_id_i && !tx_dest_i && !tx_user_i;
|
||||
assign s_rx_accept = rx_valid_o && rx_ready_i;
|
||||
assign tx_fifo_push_o = s_tx_held_q && !s_tx_closing;
|
||||
assign tx_fifo_data_o = s_tx_data_q;
|
||||
assign tx_fifo_sm_o = s_tx_sm_q;
|
||||
assign rx_fifo_pop_o = !s_rx_held_q && rx_bind_i[31] && !s_rx_closing &&
|
||||
!guard_fault_i[rx_bind_i[0]] && (rx_level_i[rx_bind_i[0]] != 4'd0);
|
||||
assign rx_fifo_sm_o = rx_bind_i[0];
|
||||
assign rx_valid_o = s_rx_held_q;
|
||||
assign rx_data_o = s_rx_data_q;
|
||||
assign rx_keep_o = 4'hf;
|
||||
assign rx_strb_o = 4'hf;
|
||||
assign rx_last_o = 1'b0;
|
||||
assign rx_id_o = 1'b0;
|
||||
assign rx_dest_o = 1'b0;
|
||||
assign rx_user_o = 1'b0;
|
||||
assign tx_held_o = s_tx_held_q;
|
||||
assign rx_held_o = s_rx_held_q;
|
||||
assign tx_closing_o = s_tx_closing;
|
||||
assign rx_closing_o = s_rx_closing;
|
||||
assign stream_fault_valid_o = s_tx_accept && !s_tx_closing && !s_tx_beat_valid;
|
||||
assign stream_fault_sm_o = tx_bind_i[0];
|
||||
assign s_unused = tx_last_i ^ ^{tx_bind_i[30:1], rx_bind_i[30:1]};
|
||||
|
||||
always_comb begin
|
||||
s_tx_held_d = s_tx_held_q;
|
||||
s_tx_sm_d = s_tx_sm_q;
|
||||
s_tx_data_d = s_tx_data_q;
|
||||
s_rx_held_d = s_rx_held_q;
|
||||
s_rx_sm_d = s_rx_sm_q;
|
||||
s_rx_data_d = s_rx_data_q;
|
||||
if (tx_fifo_accept_i) s_tx_held_d = 1'b0;
|
||||
if (s_tx_accept) begin
|
||||
if (!s_tx_closing && s_tx_beat_valid) begin
|
||||
s_tx_held_d = 1'b1;
|
||||
s_tx_sm_d = tx_bind_i[0];
|
||||
s_tx_data_d = tx_data_i;
|
||||
end else begin
|
||||
s_tx_held_d = 1'b0;
|
||||
end
|
||||
end
|
||||
if (s_tx_closing) s_tx_held_d = 1'b0;
|
||||
if (s_rx_accept) s_rx_held_d = 1'b0;
|
||||
if (rx_fifo_accept_i) begin
|
||||
s_rx_held_d = 1'b1;
|
||||
s_rx_sm_d = rx_fifo_sm_o;
|
||||
s_rx_data_d = rx_fifo_data_i;
|
||||
end
|
||||
if ((s_tx_held_q && sm_reset_i[s_tx_sm_q]) || block_reset_i) s_tx_held_d = 1'b0;
|
||||
if ((s_rx_held_q && sm_reset_i[s_rx_sm_q]) || block_reset_i) s_rx_held_d = 1'b0;
|
||||
end
|
||||
|
||||
dffr #(
|
||||
.DATA_WIDTH(1)
|
||||
) u_tx_held (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_tx_held_d),
|
||||
.dat_o (s_tx_held_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(1)
|
||||
) u_tx_sm (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_tx_sm_d),
|
||||
.dat_o (s_tx_sm_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(32)
|
||||
) u_tx_data (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_tx_data_d),
|
||||
.dat_o (s_tx_data_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(1)
|
||||
) u_rx_held (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_rx_held_d),
|
||||
.dat_o (s_rx_held_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(1)
|
||||
) u_rx_sm (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_rx_sm_d),
|
||||
.dat_o (s_rx_sm_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(32)
|
||||
) u_rx_data (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_rx_data_d),
|
||||
.dat_o (s_rx_data_q)
|
||||
);
|
||||
endmodule
|
||||
@@ -0,0 +1,51 @@
|
||||
module piolite_fifo (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic flush_i,
|
||||
input logic push_i,
|
||||
input logic [31:0] push_data_i,
|
||||
input logic pop_i,
|
||||
output logic [31:0] pop_data_o,
|
||||
output logic push_accept_o,
|
||||
output logic pop_accept_o,
|
||||
output logic full_o,
|
||||
output logic empty_o,
|
||||
output logic [ 3:0] count_o
|
||||
);
|
||||
logic s_push_accept;
|
||||
logic s_pop_accept;
|
||||
|
||||
// Common FIFO permits full push+pop; PIO-lite admits only pre-edge eligible operations.
|
||||
assign s_push_accept = push_i && !full_o;
|
||||
assign s_pop_accept = pop_i && !empty_o;
|
||||
assign push_accept_o = s_push_accept;
|
||||
assign pop_accept_o = s_pop_accept;
|
||||
|
||||
fifo #(
|
||||
.DATA_WIDTH (32),
|
||||
.BUFFER_DEPTH (8),
|
||||
.LOG_BUFFER_DEPTH(3)
|
||||
) u_fifo (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.flush_i(flush_i),
|
||||
.push_i (s_push_accept),
|
||||
.full_o (full_o),
|
||||
.dat_i (push_data_i),
|
||||
.pop_i (s_pop_accept),
|
||||
.empty_o(empty_o),
|
||||
.dat_o (pop_data_o),
|
||||
.cnt_o (count_o)
|
||||
);
|
||||
`ifdef FORMAL
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (rst_n_i) begin
|
||||
assert (count_o <= 4'd8);
|
||||
assert (full_o == (count_o == 4'd8));
|
||||
assert (empty_o == (count_o == 4'd0));
|
||||
assert (push_accept_o == (push_i && !full_o));
|
||||
assert (pop_accept_o == (pop_i && !empty_o));
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
@@ -0,0 +1,46 @@
|
||||
`include "piolite_define.svh"
|
||||
|
||||
module piolite_gpio_guard (
|
||||
input piolite_pkg::sm_config_t cfg_i [0:1],
|
||||
input piolite_pkg::sm_runtime_t runtime_i [0:1],
|
||||
input logic [ 1:0] session_live_i,
|
||||
input logic [31:0] user_select_i,
|
||||
input logic [31:0] user_status_i,
|
||||
input logic [31:0] filter_enable_i,
|
||||
input logic gpio_ready_i,
|
||||
output logic [ 1:0] start_guard_ok_o,
|
||||
output logic [ 1:0] cpu_guard_ok_o,
|
||||
output logic [ 1:0] fault_valid_o,
|
||||
output logic [ 7:0] fault_code_o [0:1],
|
||||
output logic [31:0] gpio_value_o,
|
||||
output logic [31:0] gpio_oe_o
|
||||
);
|
||||
logic [31:0] s_claimed_ready;
|
||||
logic [ 1:0] s_ownership_ok;
|
||||
logic [ 1:0] s_ready_ok;
|
||||
|
||||
assign s_claimed_ready = user_select_i & user_status_i & ~filter_enable_i;
|
||||
always_comb begin
|
||||
start_guard_ok_o = 2'd0;
|
||||
cpu_guard_ok_o = 2'd0;
|
||||
fault_valid_o = 2'd0;
|
||||
fault_code_o[0] = 8'd0;
|
||||
fault_code_o[1] = 8'd0;
|
||||
s_ownership_ok = 2'd0;
|
||||
s_ready_ok = 2'd0;
|
||||
gpio_value_o = runtime_i[0].output_value | runtime_i[1].output_value;
|
||||
gpio_oe_o = 32'd0;
|
||||
for (integer index = 0; index < 2; index++) begin
|
||||
s_ownership_ok[index] = ((cfg_i[index].claim_mask & ~user_select_i) == 32'd0) &&
|
||||
((cfg_i[index].claim_mask & filter_enable_i) == 32'd0);
|
||||
s_ready_ok[index] = gpio_ready_i && ((cfg_i[index].claim_mask & ~user_status_i) == 32'd0);
|
||||
start_guard_ok_o[index] = s_ownership_ok[index] && s_ready_ok[index];
|
||||
cpu_guard_ok_o[index] = start_guard_ok_o[index];
|
||||
fault_valid_o[index] = session_live_i[index] && !(s_ownership_ok[index] && s_ready_ok[index]);
|
||||
if (!s_ownership_ok[index]) fault_code_o[index] = `PIOLITE_FAULT_OWNERSHIP_FILTER;
|
||||
else if (!s_ready_ok[index]) fault_code_o[index] = `PIOLITE_FAULT_GPIO_READY;
|
||||
gpio_oe_o |= runtime_i[index].output_oe & cfg_i[index].claim_mask &
|
||||
cfg_i[index].output_mask & s_claimed_ready & {32{gpio_ready_i}};
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,47 @@
|
||||
interface piolite_if;
|
||||
piolite_pkg::sm_config_t cfg [0:1];
|
||||
piolite_pkg::sm_runtime_t runtime [0:1];
|
||||
logic [ 3:0] tx_level [0:1];
|
||||
logic [ 3:0] rx_level [0:1];
|
||||
logic [31:0] tx_bind;
|
||||
logic [31:0] rx_bind;
|
||||
logic [31:0] imem_valid;
|
||||
logic [ 3:0] flags;
|
||||
logic [ 1:0] cmd_start;
|
||||
logic [ 1:0] cmd_pause;
|
||||
logic [ 1:0] cmd_resume;
|
||||
logic [ 1:0] cmd_disable;
|
||||
logic [ 1:0] cmd_abort;
|
||||
logic [ 1:0] cmd_sm_reset;
|
||||
logic cmd_block_reset;
|
||||
logic [ 3:0] flag_set;
|
||||
logic [ 3:0] flag_clear;
|
||||
logic [ 5:0] event_clear [0:1];
|
||||
logic [ 1:0] cpu_tx_push;
|
||||
logic [ 1:0] cpu_rx_pop;
|
||||
logic [31:0] cpu_tx_data;
|
||||
logic [31:0] cpu_rx_data [0:1];
|
||||
logic [ 1:0] start_guard_ok;
|
||||
logic [ 1:0] cpu_guard_ok;
|
||||
logic [15:0] imem_read_data;
|
||||
logic imem_write;
|
||||
logic [ 4:0] imem_write_addr;
|
||||
logic [15:0] imem_write_data;
|
||||
logic [ 4:0] imem_read_addr;
|
||||
|
||||
modport registers(
|
||||
output cfg, tx_bind, rx_bind, cmd_start, cmd_pause, cmd_resume, cmd_disable,
|
||||
cmd_abort, cmd_sm_reset, cmd_block_reset, flag_set, flag_clear, event_clear,
|
||||
cpu_tx_push, cpu_rx_pop, cpu_tx_data, imem_write, imem_write_addr,
|
||||
imem_write_data, imem_read_addr,
|
||||
input runtime, tx_level, rx_level, imem_valid, flags, start_guard_ok, cpu_guard_ok,
|
||||
imem_read_data, cpu_rx_data
|
||||
);
|
||||
modport core(
|
||||
input cfg, tx_bind, rx_bind, cmd_start, cmd_pause, cmd_resume, cmd_disable, cmd_abort,
|
||||
cmd_sm_reset, cmd_block_reset, flag_set, flag_clear, event_clear, cpu_tx_push,
|
||||
cpu_rx_pop, cpu_tx_data, imem_write, imem_write_addr, imem_write_data,
|
||||
imem_read_addr, start_guard_ok, cpu_guard_ok,
|
||||
output runtime, tx_level, rx_level, imem_valid, flags, imem_read_data, cpu_rx_data
|
||||
);
|
||||
endinterface
|
||||
@@ -0,0 +1,48 @@
|
||||
module piolite_imem (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic clear_valid_i,
|
||||
input logic write_i,
|
||||
input logic [ 4:0] write_addr_i,
|
||||
input logic [15:0] write_data_i,
|
||||
input logic [ 4:0] sm0_addr_i,
|
||||
output logic [15:0] sm0_data_o,
|
||||
output logic sm0_valid_o,
|
||||
input logic [ 4:0] sm1_addr_i,
|
||||
output logic [15:0] sm1_data_o,
|
||||
output logic sm1_valid_o,
|
||||
input logic [ 4:0] apb_addr_i,
|
||||
output logic [15:0] apb_data_o,
|
||||
output logic [31:0] valid_o
|
||||
);
|
||||
logic [15:0] s_payload_q[0:31];
|
||||
logic [31:0] s_valid_d;
|
||||
logic [31:0] s_valid_q;
|
||||
|
||||
assign sm0_data_o = s_payload_q[sm0_addr_i];
|
||||
assign sm0_valid_o = s_valid_q[sm0_addr_i];
|
||||
assign sm1_data_o = s_payload_q[sm1_addr_i];
|
||||
assign sm1_valid_o = s_valid_q[sm1_addr_i];
|
||||
assign apb_data_o = s_valid_q[apb_addr_i] ? s_payload_q[apb_addr_i] : 16'd0;
|
||||
assign valid_o = s_valid_q;
|
||||
|
||||
always_comb begin
|
||||
s_valid_d = s_valid_q;
|
||||
if (clear_valid_i) s_valid_d = 32'd0;
|
||||
else if (write_i) s_valid_d[write_addr_i] = 1'b1;
|
||||
end
|
||||
|
||||
dffr #(
|
||||
.DATA_WIDTH(32)
|
||||
) u_valid (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_valid_d),
|
||||
.dat_o (s_valid_q)
|
||||
);
|
||||
|
||||
// Validity alone hides stale payload; the small inferred payload array is not reset.
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (write_i && !clear_valid_i) s_payload_q[write_addr_i] <= write_data_i;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,188 @@
|
||||
`include "piolite_define.svh"
|
||||
|
||||
package piolite_pkg;
|
||||
typedef enum logic [2:0] {
|
||||
Disabled = 3'd0,
|
||||
Running = 3'd1,
|
||||
Paused = 3'd2,
|
||||
Halted = 3'd3,
|
||||
Closing = 3'd4
|
||||
} sm_state_e;
|
||||
|
||||
typedef enum logic [2:0] {
|
||||
StallNone = 3'd0,
|
||||
StallPin = 3'd1,
|
||||
StallFlag = 3'd2,
|
||||
StallTx = 3'd3,
|
||||
StallRx = 3'd4
|
||||
} stall_kind_e;
|
||||
|
||||
typedef struct packed {
|
||||
logic [4:0] base;
|
||||
logic [5:0] width;
|
||||
} pin_window_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic [4:0] options;
|
||||
logic [15:0] divider;
|
||||
logic [4:0] base;
|
||||
logic [5:0] length;
|
||||
logic [4:0] entry;
|
||||
logic [4:0] wrap_bottom;
|
||||
logic [4:0] wrap_top;
|
||||
pin_window_t in_pins;
|
||||
pin_window_t out_pins;
|
||||
pin_window_t set_pins;
|
||||
logic [31:0] claim_mask;
|
||||
logic [31:0] output_mask;
|
||||
logic [31:0] initial_value;
|
||||
logic [31:0] initial_oe;
|
||||
logic [15:0] initial_x;
|
||||
logic [15:0] initial_y;
|
||||
logic [5:0] tx_bits;
|
||||
logic [5:0] rx_bits;
|
||||
logic [4:0] test_pin;
|
||||
logic [1:0] test_flag;
|
||||
logic [3:0] tx_low;
|
||||
logic [3:0] rx_high;
|
||||
} sm_config_t;
|
||||
|
||||
typedef struct packed {
|
||||
sm_state_e state;
|
||||
stall_kind_e stall;
|
||||
logic last_fifo_failed;
|
||||
logic [7:0] fault_code;
|
||||
logic [4:0] fault_pc;
|
||||
logic [4:0] pc;
|
||||
logic [31:0] isr;
|
||||
logic [31:0] osr;
|
||||
logic [5:0] isr_count;
|
||||
logic [5:0] osr_count;
|
||||
logic [15:0] x;
|
||||
logic [15:0] y;
|
||||
logic [15:0] divider_remaining;
|
||||
logic [3:0] delay_remaining;
|
||||
logic [31:0] tick_count;
|
||||
logic [31:0] tx_stall_count;
|
||||
logic [31:0] rx_stall_count;
|
||||
logic [31:0] retired_count;
|
||||
logic [5:0] events;
|
||||
logic [31:0] output_value;
|
||||
logic [31:0] output_oe;
|
||||
} sm_runtime_t;
|
||||
|
||||
typedef struct packed {
|
||||
logic [3:0] opcode;
|
||||
logic [3:0] delay;
|
||||
logic [7:0] operand;
|
||||
} instruction_t;
|
||||
|
||||
function automatic instruction_t decode_instruction(input logic [15:0] word);
|
||||
instruction_t decoded;
|
||||
decoded.opcode = word[15:12];
|
||||
decoded.delay = word[11:8];
|
||||
decoded.operand = word[7:0];
|
||||
return decoded;
|
||||
endfunction
|
||||
|
||||
function automatic logic instruction_legal(input instruction_t instruction);
|
||||
logic legal;
|
||||
legal = 1'b1;
|
||||
unique case (instruction.opcode)
|
||||
`PIOLITE_OPCODE_CTRL: begin
|
||||
if ((instruction.operand[4:0] != 5'd0) || (instruction.operand[7:5] > 3'd3)) legal = 1'b0;
|
||||
if ((instruction.operand[7:5] == `PIOLITE_CTRL_HALT) && (instruction.delay != 4'd0))
|
||||
legal = 1'b0;
|
||||
end
|
||||
`PIOLITE_OPCODE_SET_PIN, `PIOLITE_OPCODE_SET_DIR, `PIOLITE_OPCODE_SET_COUNT: ;
|
||||
`PIOLITE_OPCODE_IN: begin
|
||||
if (instruction.operand[7:5] > 3'd3) legal = 1'b0;
|
||||
end
|
||||
`PIOLITE_OPCODE_OUT: begin
|
||||
if (instruction.operand[7:5] > 3'd4) legal = 1'b0;
|
||||
if ((instruction.operand[7:5] inside {3'd2, 3'd3}) &&
|
||||
((instruction.operand[4:0] == 5'd0) || (instruction.operand[4:0] > 5'd16)))
|
||||
legal = 1'b0;
|
||||
end
|
||||
`PIOLITE_OPCODE_MOVE: begin
|
||||
if ((instruction.operand[2:0] != 3'd0) || (instruction.operand[5:3] > 3'd5)) legal = 1'b0;
|
||||
end
|
||||
`PIOLITE_OPCODE_FIFO: begin
|
||||
if (instruction.operand[5:0] != 6'd0) legal = 1'b0;
|
||||
end
|
||||
`PIOLITE_OPCODE_WAIT: begin
|
||||
if (instruction.operand[0] != 1'b0) legal = 1'b0;
|
||||
if (instruction.operand[7] && (instruction.operand[5:1] > 5'd3)) legal = 1'b0;
|
||||
end
|
||||
`PIOLITE_OPCODE_BRANCH: ;
|
||||
`PIOLITE_OPCODE_FLAG: begin
|
||||
if (instruction.operand[4:0] != 5'd0) legal = 1'b0;
|
||||
end
|
||||
default: legal = 1'b0;
|
||||
endcase
|
||||
return legal;
|
||||
endfunction
|
||||
|
||||
function automatic logic [5:0] encoded_count(input logic [4:0] field_value);
|
||||
return (field_value == 5'd0) ? 6'd32 : {1'b0, field_value};
|
||||
endfunction
|
||||
|
||||
function automatic logic [31:0] bit_mask(input logic [5:0] width);
|
||||
return (width == 6'd0) ? 32'd0 :
|
||||
((width >= 6'd32) ? 32'hffff_ffff : ((32'd1 << width) - 32'd1));
|
||||
endfunction
|
||||
|
||||
function automatic logic [31:0] window_mask(input pin_window_t window);
|
||||
return (window.width == 6'd0) ? 32'd0 : (bit_mask(window.width) << window.base);
|
||||
endfunction
|
||||
|
||||
function automatic logic [31:0] sat_increment(input logic [31:0] value);
|
||||
return (value == 32'hffff_ffff) ? value : (value + 32'd1);
|
||||
endfunction
|
||||
|
||||
function automatic logic config_scalar_legal(input sm_config_t cfg);
|
||||
logic legal;
|
||||
pin_window_t in_window;
|
||||
pin_window_t out_window;
|
||||
pin_window_t set_window;
|
||||
in_window = cfg.in_pins;
|
||||
out_window = cfg.out_pins;
|
||||
set_window = cfg.set_pins;
|
||||
legal = 1'b1;
|
||||
if ((cfg.divider == 16'd0) || (cfg.length == 6'd0) || (cfg.length > 6'd32)) legal = 1'b0;
|
||||
if (({1'b0, cfg.base} + cfg.length) > 7'd32) legal = 1'b0;
|
||||
if ((cfg.entry < cfg.base) || ({1'b0, cfg.entry} >= ({1'b0, cfg.base} + cfg.length)))
|
||||
legal = 1'b0;
|
||||
if ((cfg.wrap_bottom < cfg.base) ||
|
||||
({1'b0, cfg.wrap_bottom} >= ({1'b0, cfg.base} + cfg.length)) ||
|
||||
(cfg.wrap_top < cfg.base) ||
|
||||
({1'b0, cfg.wrap_top} >= ({1'b0, cfg.base} + cfg.length)) ||
|
||||
(cfg.wrap_bottom > cfg.wrap_top))
|
||||
legal = 1'b0;
|
||||
if ((cfg.tx_bits == 6'd0) || (cfg.tx_bits > 6'd32) ||
|
||||
(cfg.rx_bits == 6'd0) || (cfg.rx_bits > 6'd32))
|
||||
legal = 1'b0;
|
||||
if (({1'b0, in_window.base} + in_window.width) > 7'd32 ||
|
||||
({1'b0, out_window.base} + out_window.width) > 7'd32 ||
|
||||
({1'b0, set_window.base} + set_window.width) > 7'd32 ||
|
||||
(set_window.width > 6'd8))
|
||||
legal = 1'b0;
|
||||
if (((window_mask(
|
||||
in_window
|
||||
) | window_mask(
|
||||
out_window
|
||||
) | window_mask(
|
||||
set_window
|
||||
)) & ~cfg.claim_mask) != 32'd0)
|
||||
legal = 1'b0;
|
||||
if (((window_mask(out_window) | window_mask(set_window)) & ~cfg.output_mask) != 32'd0)
|
||||
legal = 1'b0;
|
||||
if (((cfg.output_mask & ~cfg.claim_mask) | (cfg.initial_value & ~cfg.output_mask) |
|
||||
(cfg.initial_oe & ~cfg.output_mask)) != 32'd0)
|
||||
legal = 1'b0;
|
||||
if (cfg.options[`PIOLITE_CONFIG_TEST_PIN_ENABLE] &&
|
||||
((cfg.claim_mask & (32'd1 << cfg.test_pin)) == 32'd0))
|
||||
legal = 1'b0;
|
||||
return legal;
|
||||
endfunction
|
||||
endpackage
|
||||
@@ -0,0 +1,689 @@
|
||||
`include "piolite_define.svh"
|
||||
|
||||
module piolite_reg (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic [31:0] clock_hz_i,
|
||||
input logic [31:0] user_select_i,
|
||||
input logic tx_job_i,
|
||||
input logic rx_job_i,
|
||||
input logic tx_held_i,
|
||||
input logic rx_held_i,
|
||||
input logic tx_closing_i,
|
||||
input logic rx_closing_i,
|
||||
output logic irq_o,
|
||||
apb4_if.slave apb4,
|
||||
piolite_if.registers ctrl
|
||||
);
|
||||
localparam piolite_pkg::sm_config_t ResetSmCfg = '{
|
||||
options: 5'd0,
|
||||
divider: 16'd1,
|
||||
base: 5'd0,
|
||||
length: 6'd0,
|
||||
entry: 5'd0,
|
||||
wrap_bottom: 5'd0,
|
||||
wrap_top: 5'd0,
|
||||
in_pins: '{base: 5'd0, width: 6'd0},
|
||||
out_pins: '{base: 5'd0, width: 6'd0},
|
||||
set_pins: '{base: 5'd0, width: 6'd0},
|
||||
claim_mask: 32'd0,
|
||||
output_mask: 32'd0,
|
||||
initial_value: 32'd0,
|
||||
initial_oe: 32'd0,
|
||||
initial_x: 16'd0,
|
||||
initial_y: 16'd0,
|
||||
tx_bits: 6'd32,
|
||||
rx_bits: 6'd32,
|
||||
test_pin: 5'd0,
|
||||
test_flag: 2'd0,
|
||||
tx_low: 4'd0,
|
||||
rx_high: 4'd1
|
||||
};
|
||||
|
||||
piolite_pkg::sm_config_t s_cfg_d [0:1];
|
||||
piolite_pkg::sm_config_t s_cfg_q [0:1];
|
||||
logic [31:0] s_tx_bind_d;
|
||||
logic [31:0] s_tx_bind_q;
|
||||
logic [31:0] s_rx_bind_d;
|
||||
logic [31:0] s_rx_bind_q;
|
||||
logic [ 5:0] s_event_en_d [0:1];
|
||||
logic [ 5:0] s_event_en_q [0:1];
|
||||
logic [27:0] s_irq_en_d;
|
||||
logic [27:0] s_irq_en_q;
|
||||
logic s_req;
|
||||
logic s_req_accept;
|
||||
logic s_access_err;
|
||||
logic s_ready_d;
|
||||
logic s_ready_q;
|
||||
logic s_resp_err_d;
|
||||
logic s_resp_err_q;
|
||||
logic [31:0] s_rdata_d;
|
||||
logic [31:0] s_rdata_q;
|
||||
logic [31:0] s_read_data;
|
||||
logic [11:0] s_offset;
|
||||
logic [ 6:0] s_sm_offset;
|
||||
logic s_sm_access;
|
||||
logic s_imem_access;
|
||||
logic s_sm_index;
|
||||
logic [ 1:0] s_cfg_writable;
|
||||
logic [ 1:0] s_dma_busy;
|
||||
logic s_start_ok;
|
||||
logic s_bind_ok;
|
||||
logic s_block_reset_ok;
|
||||
logic [31:0] s_region;
|
||||
logic [27:0] s_irq_sources;
|
||||
|
||||
assign s_req = apb4.psel && apb4.penable;
|
||||
assign s_req_accept = s_req && !s_ready_q;
|
||||
assign s_offset = apb4.paddr[11:0];
|
||||
assign s_sm_access = (apb4.paddr[11:9] == 3'b001) && !apb4.paddr[8];
|
||||
assign s_imem_access = (apb4.paddr[11:8] == 4'h1) && !apb4.paddr[7];
|
||||
assign s_sm_index = apb4.paddr[7];
|
||||
assign s_sm_offset = apb4.paddr[6:0];
|
||||
assign apb4.pready = s_ready_q;
|
||||
assign apb4.prdata = s_rdata_q;
|
||||
assign apb4.pslverr = s_resp_err_q;
|
||||
assign ctrl.tx_bind = s_tx_bind_q;
|
||||
assign ctrl.rx_bind = s_rx_bind_q;
|
||||
assign ctrl.cfg[0] = s_cfg_q[0];
|
||||
assign ctrl.cfg[1] = s_cfg_q[1];
|
||||
assign ctrl.imem_read_addr = apb4.paddr[6:2];
|
||||
assign irq_o = ((s_irq_sources & s_irq_en_q) != 28'd0);
|
||||
|
||||
always_comb begin
|
||||
s_irq_sources = 28'd0;
|
||||
s_irq_sources[11:8] = ctrl.flags[3:0];
|
||||
for (integer index = 0; index < 2; index++) begin
|
||||
s_irq_sources[index] = ((ctrl.runtime[index].events & s_event_en_q[index]) != 6'd0);
|
||||
s_irq_sources[16+index] = (ctrl.tx_level[index] <= s_cfg_q[index].tx_low);
|
||||
s_irq_sources[24+index] = (ctrl.rx_level[index] >= s_cfg_q[index].rx_high);
|
||||
end
|
||||
end
|
||||
|
||||
always_comb begin
|
||||
s_cfg_d = s_cfg_q;
|
||||
s_tx_bind_d = s_tx_bind_q;
|
||||
s_rx_bind_d = s_rx_bind_q;
|
||||
s_event_en_d = s_event_en_q;
|
||||
s_irq_en_d = s_irq_en_q;
|
||||
s_read_data = 32'd0;
|
||||
s_access_err = (apb4.paddr[1:0] != 2'd0) || (apb4.pwrite && (apb4.pstrb != 4'hf));
|
||||
s_cfg_writable = 2'd0;
|
||||
s_dma_busy = 2'd0;
|
||||
s_start_ok = 1'b1;
|
||||
s_bind_ok = 1'b1;
|
||||
s_block_reset_ok = 1'b1;
|
||||
s_region = 32'd0;
|
||||
ctrl.cmd_start = 2'd0;
|
||||
ctrl.cmd_pause = 2'd0;
|
||||
ctrl.cmd_resume = 2'd0;
|
||||
ctrl.cmd_disable = 2'd0;
|
||||
ctrl.cmd_abort = 2'd0;
|
||||
ctrl.cmd_sm_reset = 2'd0;
|
||||
ctrl.cmd_block_reset = 1'b0;
|
||||
ctrl.flag_set = 4'd0;
|
||||
ctrl.flag_clear = 4'd0;
|
||||
ctrl.event_clear[0] = 6'd0;
|
||||
ctrl.event_clear[1] = 6'd0;
|
||||
ctrl.cpu_tx_push = 2'd0;
|
||||
ctrl.cpu_rx_pop = 2'd0;
|
||||
ctrl.cpu_tx_data = apb4.pwdata;
|
||||
ctrl.imem_write = 1'b0;
|
||||
ctrl.imem_write_addr = apb4.paddr[6:2];
|
||||
ctrl.imem_write_data = apb4.pwdata[15:0];
|
||||
|
||||
for (integer index = 0; index < 2; index++) begin
|
||||
s_dma_busy[index] = (s_tx_bind_q[31] && (s_tx_bind_q[0] == index[0]) &&
|
||||
(tx_job_i || tx_held_i || tx_closing_i)) ||
|
||||
(s_rx_bind_q[31] && (s_rx_bind_q[0] == index[0]) &&
|
||||
(rx_job_i || rx_held_i || rx_closing_i));
|
||||
s_cfg_writable[index] = (ctrl.runtime[index].state == piolite_pkg::Disabled) &&
|
||||
!s_dma_busy[index];
|
||||
if (!s_block_reset_ok || (ctrl.runtime[index].state != piolite_pkg::Disabled))
|
||||
s_block_reset_ok = 1'b0;
|
||||
if (apb4.pwdata[index]) begin
|
||||
if ((ctrl.runtime[index].state != piolite_pkg::Disabled) ||
|
||||
(ctrl.runtime[index].fault_code != `PIOLITE_FAULT_NONE) ||
|
||||
!piolite_pkg::config_scalar_legal(
|
||||
s_cfg_q[index]
|
||||
) || !ctrl.start_guard_ok[index] || (ctrl.rx_level[index] != 4'd0))
|
||||
s_start_ok = 1'b0;
|
||||
s_region = piolite_pkg::bit_mask(s_cfg_q[index].length) << s_cfg_q[index].base;
|
||||
if ((ctrl.imem_valid & s_region) != s_region) s_start_ok = 1'b0;
|
||||
end
|
||||
end
|
||||
if (s_tx_bind_q[31] || s_rx_bind_q[31] || tx_job_i || rx_job_i || tx_held_i ||
|
||||
rx_held_i || tx_closing_i || rx_closing_i || (user_select_i != 32'd0))
|
||||
s_block_reset_ok = 1'b0;
|
||||
if ((apb4.pwdata[1:0] != 2'd0) && ((s_cfg_q[0].output_mask & s_cfg_q[1].output_mask) != 32'd0))
|
||||
s_start_ok = 1'b0;
|
||||
|
||||
if (s_sm_access) begin
|
||||
unique case ({
|
||||
5'd0, s_sm_offset
|
||||
})
|
||||
`APB4_PIOLITE__SM_STATUS: begin
|
||||
s_read_data = {
|
||||
23'd0,
|
||||
ctrl.runtime[s_sm_index].last_fifo_failed,
|
||||
1'b0,
|
||||
ctrl.runtime[s_sm_index].stall,
|
||||
1'b0,
|
||||
ctrl.runtime[s_sm_index].state
|
||||
};
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_CONFIG: s_read_data = {27'd0, s_cfg_q[s_sm_index].options};
|
||||
`APB4_PIOLITE__SM_DIV: s_read_data = {16'd0, s_cfg_q[s_sm_index].divider};
|
||||
`APB4_PIOLITE__SM_PROGRAM: begin
|
||||
s_read_data = {
|
||||
11'd0,
|
||||
s_cfg_q[s_sm_index].entry,
|
||||
2'd0,
|
||||
s_cfg_q[s_sm_index].length,
|
||||
3'd0,
|
||||
s_cfg_q[s_sm_index].base
|
||||
};
|
||||
end
|
||||
`APB4_PIOLITE__SM_WRAP: begin
|
||||
s_read_data = {
|
||||
19'd0, s_cfg_q[s_sm_index].wrap_top, 3'd0, s_cfg_q[s_sm_index].wrap_bottom
|
||||
};
|
||||
end
|
||||
`APB4_PIOLITE__SM_IN_PINS: begin
|
||||
s_read_data = {
|
||||
18'd0, s_cfg_q[s_sm_index].in_pins.width, 3'd0, s_cfg_q[s_sm_index].in_pins.base
|
||||
};
|
||||
end
|
||||
`APB4_PIOLITE__SM_OUT_PINS: begin
|
||||
s_read_data = {
|
||||
18'd0, s_cfg_q[s_sm_index].out_pins.width, 3'd0, s_cfg_q[s_sm_index].out_pins.base
|
||||
};
|
||||
end
|
||||
`APB4_PIOLITE__SM_SET_PINS: begin
|
||||
s_read_data = {
|
||||
18'd0, s_cfg_q[s_sm_index].set_pins.width, 3'd0, s_cfg_q[s_sm_index].set_pins.base
|
||||
};
|
||||
end
|
||||
`APB4_PIOLITE__SM_CLAIM_MASK: s_read_data = s_cfg_q[s_sm_index].claim_mask;
|
||||
`APB4_PIOLITE__SM_OUTPUT_MASK: s_read_data = s_cfg_q[s_sm_index].output_mask;
|
||||
`APB4_PIOLITE__SM_INITIAL_VALUE: s_read_data = s_cfg_q[s_sm_index].initial_value;
|
||||
`APB4_PIOLITE__SM_INITIAL_OE: s_read_data = s_cfg_q[s_sm_index].initial_oe;
|
||||
`APB4_PIOLITE__SM_INITIAL_XY: begin
|
||||
s_read_data = {s_cfg_q[s_sm_index].initial_y, s_cfg_q[s_sm_index].initial_x};
|
||||
end
|
||||
`APB4_PIOLITE__SM_SHIFT_LIMITS: begin
|
||||
s_read_data = {18'd0, s_cfg_q[s_sm_index].rx_bits, 2'd0, s_cfg_q[s_sm_index].tx_bits};
|
||||
end
|
||||
`APB4_PIOLITE__SM_TEST_SELECT: begin
|
||||
s_read_data = {22'd0, s_cfg_q[s_sm_index].test_flag, 3'd0, s_cfg_q[s_sm_index].test_pin};
|
||||
end
|
||||
`APB4_PIOLITE__SM_WATERMARKS: begin
|
||||
s_read_data = {20'd0, s_cfg_q[s_sm_index].rx_high, 4'd0, s_cfg_q[s_sm_index].tx_low};
|
||||
end
|
||||
`APB4_PIOLITE__SM_TXDATA: begin
|
||||
if (!apb4.pwrite || (ctrl.runtime[s_sm_index].state == piolite_pkg::Closing) ||
|
||||
!ctrl.cpu_guard_ok[s_sm_index] ||
|
||||
(s_tx_bind_q[31] && (s_tx_bind_q[0] == s_sm_index)) ||
|
||||
(ctrl.tx_level[s_sm_index] == 4'd8))
|
||||
s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_RXDATA: begin
|
||||
s_read_data = ctrl.cpu_rx_data[s_sm_index];
|
||||
if (apb4.pwrite || (ctrl.runtime[s_sm_index].state == piolite_pkg::Closing) ||
|
||||
!ctrl.cpu_guard_ok[s_sm_index] ||
|
||||
(s_rx_bind_q[31] && (s_rx_bind_q[0] == s_sm_index)) ||
|
||||
(ctrl.rx_level[s_sm_index] == 4'd0))
|
||||
s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_FIFO_LEVELS: begin
|
||||
s_read_data = {20'd0, ctrl.rx_level[s_sm_index], 4'd0, ctrl.tx_level[s_sm_index]};
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_EVENTS: s_read_data = {26'd0, ctrl.runtime[s_sm_index].events};
|
||||
`APB4_PIOLITE__SM_EVENT_ENABLE: s_read_data = {26'd0, s_event_en_q[s_sm_index]};
|
||||
`APB4_PIOLITE__SM_FAULT: begin
|
||||
s_read_data = {
|
||||
19'd0, ctrl.runtime[s_sm_index].fault_pc, ctrl.runtime[s_sm_index].fault_code
|
||||
};
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_PC: begin
|
||||
s_read_data = {27'd0, ctrl.runtime[s_sm_index].pc};
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_ISR: begin
|
||||
s_read_data = ctrl.runtime[s_sm_index].isr;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_OSR: begin
|
||||
s_read_data = ctrl.runtime[s_sm_index].osr;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_XY: begin
|
||||
s_read_data = {ctrl.runtime[s_sm_index].y, ctrl.runtime[s_sm_index].x};
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_SHIFT_COUNTS: begin
|
||||
s_read_data = {
|
||||
18'd0, ctrl.runtime[s_sm_index].osr_count, 2'd0, ctrl.runtime[s_sm_index].isr_count
|
||||
};
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_TICK_COUNT: begin
|
||||
s_read_data = ctrl.runtime[s_sm_index].tick_count;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_TX_STALL_COUNT: begin
|
||||
s_read_data = ctrl.runtime[s_sm_index].tx_stall_count;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_RX_STALL_COUNT: begin
|
||||
s_read_data = ctrl.runtime[s_sm_index].rx_stall_count;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_RETIRED_COUNT: begin
|
||||
s_read_data = ctrl.runtime[s_sm_index].retired_count;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_EXEC_PHASE: begin
|
||||
s_read_data = {
|
||||
12'd0,
|
||||
ctrl.runtime[s_sm_index].delay_remaining,
|
||||
ctrl.runtime[s_sm_index].divider_remaining
|
||||
};
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
default: s_access_err = 1'b1;
|
||||
endcase
|
||||
end else if (s_imem_access) begin
|
||||
s_read_data = {16'd0, ctrl.imem_read_data};
|
||||
end else begin
|
||||
unique case (s_offset)
|
||||
`APB4_PIOLITE__IP_ID: begin
|
||||
s_read_data = `APB4_PIOLITE__ID_VALUE;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__IP_VERSION: begin
|
||||
s_read_data = `APB4_PIOLITE__VERSION_VALUE;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__ISA_VERSION: begin
|
||||
s_read_data = `APB4_PIOLITE__ISA_VALUE;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__CAPABILITY: begin
|
||||
s_read_data = `APB4_PIOLITE__CAPABILITY_VALUE;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__ROUTABLE_MASK: begin
|
||||
s_read_data = `APB4_PIOLITE__ROUTES_VALUE;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__CLOCK_HZ: begin
|
||||
s_read_data = clock_hz_i;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__FLAGS: begin
|
||||
s_read_data = {28'd0, ctrl.flags[3:0]};
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__FLAG_SET, `APB4_PIOLITE__FLAG_CLEAR: begin
|
||||
if (!apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__IRQ_STATUS: begin
|
||||
s_read_data = {4'd0, s_irq_sources};
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__IRQ_ENABLE: s_read_data = {4'd0, s_irq_en_q};
|
||||
`APB4_PIOLITE__TX_BIND: s_read_data = s_tx_bind_q;
|
||||
`APB4_PIOLITE__RX_BIND: s_read_data = s_rx_bind_q;
|
||||
`APB4_PIOLITE__TRANSPORT_STATUS: begin
|
||||
s_read_data = {
|
||||
26'd0, rx_closing_i, tx_closing_i, rx_held_i, tx_held_i, rx_job_i, tx_job_i
|
||||
};
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__IMEM_VALID: begin
|
||||
s_read_data = ctrl.imem_valid;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__OWNED_MASK: begin
|
||||
s_read_data = user_select_i;
|
||||
if (apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__START: begin
|
||||
if (!apb4.pwrite || ((apb4.pwdata[1:0] != 2'd0) && !s_start_ok)) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__PAUSE: begin
|
||||
if (!apb4.pwrite) s_access_err = 1'b1;
|
||||
for (integer index = 0; index < 2; index++)
|
||||
if (apb4.pwdata[index] && (ctrl.runtime[index].state != piolite_pkg::Running))
|
||||
s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__RESUME: begin
|
||||
if (!apb4.pwrite) s_access_err = 1'b1;
|
||||
for (integer index = 0; index < 2; index++)
|
||||
if (apb4.pwdata[index] && (ctrl.runtime[index].state != piolite_pkg::Paused))
|
||||
s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__DISABLE: begin
|
||||
if (!apb4.pwrite) s_access_err = 1'b1;
|
||||
for (integer index = 0; index < 2; index++)
|
||||
if (apb4.pwdata[index] &&
|
||||
((ctrl.runtime[index].state == piolite_pkg::Closing) || s_dma_busy[index] &&
|
||||
((s_tx_bind_q[31] && (s_tx_bind_q[0] == index[0]) && tx_job_i) ||
|
||||
(s_rx_bind_q[31] && (s_rx_bind_q[0] == index[0]) && rx_job_i))))
|
||||
s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__ABORT: begin
|
||||
if (!apb4.pwrite) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_RESET: begin
|
||||
if (!apb4.pwrite) s_access_err = 1'b1;
|
||||
for (integer index = 0; index < 2; index++) begin
|
||||
if (apb4.pwdata[index] &&
|
||||
!((ctrl.runtime[index].state == piolite_pkg::Disabled) ||
|
||||
(ctrl.runtime[index].state == piolite_pkg::Closing)))
|
||||
s_access_err = 1'b1;
|
||||
if (apb4.pwdata[index] &&
|
||||
((s_tx_bind_q[31] && (s_tx_bind_q[0] == index[0]) && tx_job_i) ||
|
||||
(s_rx_bind_q[31] && (s_rx_bind_q[0] == index[0]) && rx_job_i)))
|
||||
s_access_err = 1'b1;
|
||||
end
|
||||
end
|
||||
`APB4_PIOLITE__BLOCK_RESET: begin
|
||||
if (!apb4.pwrite || (apb4.pwdata[0] && !s_block_reset_ok)) s_access_err = 1'b1;
|
||||
end
|
||||
default: s_access_err = 1'b1;
|
||||
endcase
|
||||
end
|
||||
|
||||
if (s_req_accept && apb4.pwrite) begin
|
||||
if ((s_offset inside {`APB4_PIOLITE__START, `APB4_PIOLITE__PAUSE,
|
||||
`APB4_PIOLITE__RESUME, `APB4_PIOLITE__DISABLE, `APB4_PIOLITE__ABORT,
|
||||
`APB4_PIOLITE__SM_RESET}) && (apb4.pwdata[31:2] != 30'd0))
|
||||
s_access_err = 1'b1;
|
||||
if ((s_offset == `APB4_PIOLITE__BLOCK_RESET) && (apb4.pwdata[31:1] != 31'd0))
|
||||
s_access_err = 1'b1;
|
||||
if (s_sm_access) begin
|
||||
if (!s_cfg_writable[s_sm_index] &&
|
||||
!({5'd0, s_sm_offset} inside {`APB4_PIOLITE__SM_TXDATA,
|
||||
`APB4_PIOLITE__SM_EVENTS, `APB4_PIOLITE__SM_EVENT_ENABLE}))
|
||||
s_access_err = 1'b1;
|
||||
unique case ({
|
||||
5'd0, s_sm_offset
|
||||
})
|
||||
`APB4_PIOLITE__SM_CONFIG: begin
|
||||
if (apb4.pwdata[31:5] != 27'd0) s_access_err = 1'b1;
|
||||
s_cfg_d[s_sm_index].options = apb4.pwdata[4:0];
|
||||
end
|
||||
`APB4_PIOLITE__SM_DIV: begin
|
||||
if ((apb4.pwdata[31:16] != 16'd0) || (apb4.pwdata[15:0] == 16'd0)) s_access_err = 1'b1;
|
||||
s_cfg_d[s_sm_index].divider = apb4.pwdata[15:0];
|
||||
end
|
||||
`APB4_PIOLITE__SM_PROGRAM: begin
|
||||
if ((apb4.pwdata & 32'hffe0_c0e0) != 32'd0 ||
|
||||
(apb4.pwdata[13:8] == 6'd0) || (apb4.pwdata[13:8] > 6'd32))
|
||||
s_access_err = 1'b1;
|
||||
s_cfg_d[s_sm_index].base = apb4.pwdata[4:0];
|
||||
s_cfg_d[s_sm_index].length = apb4.pwdata[13:8];
|
||||
s_cfg_d[s_sm_index].entry = apb4.pwdata[20:16];
|
||||
end
|
||||
`APB4_PIOLITE__SM_WRAP: begin
|
||||
if ((apb4.pwdata & 32'hffff_e0e0) != 32'd0) s_access_err = 1'b1;
|
||||
s_cfg_d[s_sm_index].wrap_bottom = apb4.pwdata[4:0];
|
||||
s_cfg_d[s_sm_index].wrap_top = apb4.pwdata[12:8];
|
||||
end
|
||||
`APB4_PIOLITE__SM_IN_PINS, `APB4_PIOLITE__SM_OUT_PINS, `APB4_PIOLITE__SM_SET_PINS: begin
|
||||
if ((apb4.pwdata & 32'hffff_c0e0) != 32'd0 ||
|
||||
({1'b0, apb4.pwdata[4:0]} + apb4.pwdata[13:8]) > 7'd32)
|
||||
s_access_err = 1'b1;
|
||||
if (({5'd0, s_sm_offset} == `APB4_PIOLITE__SM_SET_PINS) && (apb4.pwdata[13:8] > 6'd8))
|
||||
s_access_err = 1'b1;
|
||||
if ({5'd0, s_sm_offset} == `APB4_PIOLITE__SM_IN_PINS) begin
|
||||
s_cfg_d[s_sm_index].in_pins.base = apb4.pwdata[4:0];
|
||||
s_cfg_d[s_sm_index].in_pins.width = apb4.pwdata[13:8];
|
||||
end else if ({5'd0, s_sm_offset} == `APB4_PIOLITE__SM_OUT_PINS) begin
|
||||
s_cfg_d[s_sm_index].out_pins.base = apb4.pwdata[4:0];
|
||||
s_cfg_d[s_sm_index].out_pins.width = apb4.pwdata[13:8];
|
||||
end else begin
|
||||
s_cfg_d[s_sm_index].set_pins.base = apb4.pwdata[4:0];
|
||||
s_cfg_d[s_sm_index].set_pins.width = apb4.pwdata[13:8];
|
||||
end
|
||||
end
|
||||
`APB4_PIOLITE__SM_CLAIM_MASK: s_cfg_d[s_sm_index].claim_mask = apb4.pwdata;
|
||||
`APB4_PIOLITE__SM_OUTPUT_MASK: s_cfg_d[s_sm_index].output_mask = apb4.pwdata;
|
||||
`APB4_PIOLITE__SM_INITIAL_VALUE: begin
|
||||
if ((apb4.pwdata & ~s_cfg_q[s_sm_index].output_mask) != 32'd0) s_access_err = 1'b1;
|
||||
s_cfg_d[s_sm_index].initial_value = apb4.pwdata;
|
||||
end
|
||||
`APB4_PIOLITE__SM_INITIAL_OE: begin
|
||||
if ((apb4.pwdata & ~s_cfg_q[s_sm_index].output_mask) != 32'd0) s_access_err = 1'b1;
|
||||
s_cfg_d[s_sm_index].initial_oe = apb4.pwdata;
|
||||
end
|
||||
`APB4_PIOLITE__SM_INITIAL_XY: begin
|
||||
s_cfg_d[s_sm_index].initial_x = apb4.pwdata[15:0];
|
||||
s_cfg_d[s_sm_index].initial_y = apb4.pwdata[31:16];
|
||||
end
|
||||
`APB4_PIOLITE__SM_SHIFT_LIMITS: begin
|
||||
if ((apb4.pwdata & 32'hffff_c0c0) != 32'd0 ||
|
||||
(apb4.pwdata[5:0] == 6'd0) || (apb4.pwdata[5:0] > 6'd32) ||
|
||||
(apb4.pwdata[13:8] == 6'd0) || (apb4.pwdata[13:8] > 6'd32))
|
||||
s_access_err = 1'b1;
|
||||
s_cfg_d[s_sm_index].tx_bits = apb4.pwdata[5:0];
|
||||
s_cfg_d[s_sm_index].rx_bits = apb4.pwdata[13:8];
|
||||
end
|
||||
`APB4_PIOLITE__SM_TEST_SELECT: begin
|
||||
if ((apb4.pwdata & 32'hffff_fce0) != 32'd0) s_access_err = 1'b1;
|
||||
s_cfg_d[s_sm_index].test_pin = apb4.pwdata[4:0];
|
||||
s_cfg_d[s_sm_index].test_flag = apb4.pwdata[9:8];
|
||||
end
|
||||
`APB4_PIOLITE__SM_WATERMARKS: begin
|
||||
if ((apb4.pwdata & 32'hffff_f0f0) != 32'd0 || (apb4.pwdata[3:0] > 4'd8) ||
|
||||
(apb4.pwdata[11:8] == 4'd0) || (apb4.pwdata[11:8] > 4'd8))
|
||||
s_access_err = 1'b1;
|
||||
s_cfg_d[s_sm_index].tx_low = apb4.pwdata[3:0];
|
||||
s_cfg_d[s_sm_index].rx_high = apb4.pwdata[11:8];
|
||||
end
|
||||
`APB4_PIOLITE__SM_EVENTS: begin
|
||||
if (apb4.pwdata[31:6] != 26'd0) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__SM_EVENT_ENABLE: begin
|
||||
if (apb4.pwdata[31:6] != 26'd0) s_access_err = 1'b1;
|
||||
s_event_en_d[s_sm_index] = apb4.pwdata[5:0];
|
||||
end
|
||||
`APB4_PIOLITE__SM_TXDATA: ;
|
||||
default: s_access_err = 1'b1;
|
||||
endcase
|
||||
end else if (s_imem_access) begin
|
||||
if (apb4.pwdata[31:16] != 16'd0) s_access_err = 1'b1;
|
||||
for (integer index = 0; index < 2; index++)
|
||||
if (ctrl.runtime[index].state != piolite_pkg::Disabled) s_access_err = 1'b1;
|
||||
if (s_tx_bind_q[31] || s_rx_bind_q[31] || tx_job_i || rx_job_i || tx_held_i ||
|
||||
rx_held_i || tx_closing_i || rx_closing_i)
|
||||
s_access_err = 1'b1;
|
||||
end else begin
|
||||
unique case (s_offset)
|
||||
`APB4_PIOLITE__FLAG_SET, `APB4_PIOLITE__FLAG_CLEAR: begin
|
||||
if (apb4.pwdata[31:4] != 28'd0) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__IRQ_ENABLE: begin
|
||||
if ((apb4.pwdata & ~`APB4_PIOLITE__IRQ_MASK) != 32'd0) s_access_err = 1'b1;
|
||||
s_irq_en_d = apb4.pwdata[27:0];
|
||||
end
|
||||
`APB4_PIOLITE__TX_BIND, `APB4_PIOLITE__RX_BIND: begin
|
||||
if ((apb4.pwdata & 32'h7fff_f8fc) != 32'd0 || (apb4.pwdata[1:0] > 2'd1))
|
||||
s_access_err = 1'b1;
|
||||
if (ctrl.runtime[apb4.pwdata[0]].state != piolite_pkg::Disabled) s_bind_ok = 1'b0;
|
||||
if (s_offset == `APB4_PIOLITE__TX_BIND) begin
|
||||
if ((ctrl.runtime[s_tx_bind_q[0]].state != piolite_pkg::Disabled) ||
|
||||
tx_job_i || tx_held_i || tx_closing_i)
|
||||
s_bind_ok = 1'b0;
|
||||
s_tx_bind_d = apb4.pwdata;
|
||||
if (apb4.pwdata[31] && !ctrl.start_guard_ok[apb4.pwdata[0]]) s_bind_ok = 1'b0;
|
||||
if (apb4.pwdata[31] && s_rx_bind_q[31] && (apb4.pwdata[10:8] == s_rx_bind_q[10:8]))
|
||||
s_bind_ok = 1'b0;
|
||||
end else begin
|
||||
if ((ctrl.runtime[s_rx_bind_q[0]].state != piolite_pkg::Disabled) ||
|
||||
rx_job_i || rx_held_i || rx_closing_i)
|
||||
s_bind_ok = 1'b0;
|
||||
s_rx_bind_d = apb4.pwdata;
|
||||
if (apb4.pwdata[31] && !ctrl.start_guard_ok[apb4.pwdata[0]]) s_bind_ok = 1'b0;
|
||||
if (apb4.pwdata[31] && s_tx_bind_q[31] && (apb4.pwdata[10:8] == s_tx_bind_q[10:8]))
|
||||
s_bind_ok = 1'b0;
|
||||
end
|
||||
if (!s_bind_ok) s_access_err = 1'b1;
|
||||
end
|
||||
`APB4_PIOLITE__START, `APB4_PIOLITE__PAUSE, `APB4_PIOLITE__RESUME,
|
||||
`APB4_PIOLITE__DISABLE, `APB4_PIOLITE__ABORT, `APB4_PIOLITE__SM_RESET,
|
||||
`APB4_PIOLITE__BLOCK_RESET:
|
||||
;
|
||||
default: s_access_err = 1'b1;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
if (s_access_err) begin
|
||||
s_cfg_d = s_cfg_q;
|
||||
s_tx_bind_d = s_tx_bind_q;
|
||||
s_rx_bind_d = s_rx_bind_q;
|
||||
s_event_en_d = s_event_en_q;
|
||||
s_irq_en_d = s_irq_en_q;
|
||||
end
|
||||
|
||||
if (s_req_accept && !s_access_err) begin
|
||||
if (s_sm_access) begin
|
||||
if ({5'd0, s_sm_offset} == `APB4_PIOLITE__SM_TXDATA)
|
||||
ctrl.cpu_tx_push[s_sm_index] = apb4.pwrite;
|
||||
if ({5'd0, s_sm_offset} == `APB4_PIOLITE__SM_RXDATA)
|
||||
ctrl.cpu_rx_pop[s_sm_index] = !apb4.pwrite;
|
||||
if (apb4.pwrite && ({5'd0, s_sm_offset} == `APB4_PIOLITE__SM_EVENTS))
|
||||
ctrl.event_clear[s_sm_index] = apb4.pwdata[5:0];
|
||||
end else if (s_imem_access) begin
|
||||
ctrl.imem_write = apb4.pwrite;
|
||||
end else if (apb4.pwrite) begin
|
||||
unique case (s_offset)
|
||||
`APB4_PIOLITE__FLAG_SET: ctrl.flag_set = apb4.pwdata[3:0];
|
||||
`APB4_PIOLITE__FLAG_CLEAR: ctrl.flag_clear = apb4.pwdata[3:0];
|
||||
`APB4_PIOLITE__START: ctrl.cmd_start = apb4.pwdata[1:0];
|
||||
`APB4_PIOLITE__PAUSE: ctrl.cmd_pause = apb4.pwdata[1:0];
|
||||
`APB4_PIOLITE__RESUME: ctrl.cmd_resume = apb4.pwdata[1:0];
|
||||
`APB4_PIOLITE__DISABLE: ctrl.cmd_disable = apb4.pwdata[1:0];
|
||||
`APB4_PIOLITE__ABORT: ctrl.cmd_abort = apb4.pwdata[1:0];
|
||||
`APB4_PIOLITE__SM_RESET: ctrl.cmd_sm_reset = apb4.pwdata[1:0];
|
||||
`APB4_PIOLITE__BLOCK_RESET: ctrl.cmd_block_reset = apb4.pwdata[0];
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
for (integer index = 0; index < 2; index++) begin
|
||||
if (ctrl.cmd_sm_reset[index]) begin
|
||||
if (s_tx_bind_q[31] && (s_tx_bind_q[0] == index[0])) s_tx_bind_d = 32'd0;
|
||||
if (s_rx_bind_q[31] && (s_rx_bind_q[0] == index[0])) s_rx_bind_d = 32'd0;
|
||||
end
|
||||
end
|
||||
if (ctrl.cmd_block_reset) begin
|
||||
s_cfg_d[0] = ResetSmCfg;
|
||||
s_cfg_d[1] = ResetSmCfg;
|
||||
s_tx_bind_d = 32'd0;
|
||||
s_rx_bind_d = 32'd0;
|
||||
s_event_en_d[0] = 6'd0;
|
||||
s_event_en_d[1] = 6'd0;
|
||||
s_irq_en_d = 28'd0;
|
||||
end
|
||||
s_ready_d = s_req;
|
||||
s_rdata_d = s_req_accept ? s_read_data : s_rdata_q;
|
||||
s_resp_err_d = s_req_accept ? s_access_err : s_resp_err_q;
|
||||
end
|
||||
|
||||
for (genvar index = 0; index < 2; index++) begin : gen_registers
|
||||
dffrc #(
|
||||
.DATA_WIDTH($bits(piolite_pkg::sm_config_t)),
|
||||
.RESET_VAL (ResetSmCfg)
|
||||
) u_cfg (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_cfg_d[index]),
|
||||
.dat_o (s_cfg_q[index])
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(6)
|
||||
) u_event_en (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_event_en_d[index]),
|
||||
.dat_o (s_event_en_q[index])
|
||||
);
|
||||
end
|
||||
dffr #(
|
||||
.DATA_WIDTH(32)
|
||||
) u_tx_bind (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_tx_bind_d),
|
||||
.dat_o (s_tx_bind_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(32)
|
||||
) u_rx_bind (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_rx_bind_d),
|
||||
.dat_o (s_rx_bind_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(28)
|
||||
) u_irq_en (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_irq_en_d),
|
||||
.dat_o (s_irq_en_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(1)
|
||||
) u_response_ready (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_ready_d),
|
||||
.dat_o (s_ready_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(32)
|
||||
) u_response_data (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_rdata_d),
|
||||
.dat_o (s_rdata_q)
|
||||
);
|
||||
dffr #(
|
||||
.DATA_WIDTH(1)
|
||||
) u_response_error (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_resp_err_d),
|
||||
.dat_o (s_resp_err_q)
|
||||
);
|
||||
`ifdef FORMAL
|
||||
logic s_formal_past_q;
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (!rst_n_i) s_formal_past_q <= 1'b0;
|
||||
else begin
|
||||
s_formal_past_q <= 1'b1;
|
||||
if (s_formal_past_q && $past(s_req)) assert (apb4.pready);
|
||||
if (apb4.pready) begin
|
||||
assert (ctrl.cpu_tx_push == 2'd0 && ctrl.cpu_rx_pop == 2'd0);
|
||||
assert (ctrl.cmd_start == 2'd0 && ctrl.cmd_pause == 2'd0 &&
|
||||
ctrl.cmd_resume == 2'd0 && ctrl.cmd_disable == 2'd0 &&
|
||||
ctrl.cmd_abort == 2'd0 && ctrl.cmd_sm_reset == 2'd0 &&
|
||||
!ctrl.cmd_block_reset && !ctrl.imem_write);
|
||||
end
|
||||
if (s_formal_past_q && $past(apb4.pready)) begin
|
||||
assert (apb4.prdata == $past(apb4.prdata));
|
||||
assert (apb4.pslverr == $past(apb4.pslverr));
|
||||
end
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
@@ -0,0 +1,565 @@
|
||||
`include "piolite_define.svh"
|
||||
|
||||
module piolite_sm (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input piolite_pkg::sm_config_t cfg_i,
|
||||
input logic start_i,
|
||||
input logic pause_i,
|
||||
input logic resume_i,
|
||||
input logic disable_i,
|
||||
input logic abort_i,
|
||||
input logic sm_reset_i,
|
||||
input logic block_reset_i,
|
||||
`ifdef PIOLITE_EXHAUSTIVE_DV
|
||||
input logic counter_load_i,
|
||||
`endif
|
||||
input logic [ 5:0] event_clear_i,
|
||||
input logic guard_fault_i,
|
||||
input logic [ 7:0] guard_fault_code_i,
|
||||
input logic stream_fault_i,
|
||||
input logic [15:0] instruction_i,
|
||||
input logic instruction_valid_i,
|
||||
output logic [ 4:0] instruction_addr_o,
|
||||
input logic [31:0] flags_i,
|
||||
input logic [31:0] pin_inputs_i,
|
||||
output logic [ 3:0] flag_set_o,
|
||||
output logic [ 3:0] flag_clear_o,
|
||||
input logic tx_empty_i,
|
||||
input logic [31:0] tx_data_i,
|
||||
output logic tx_pop_o,
|
||||
input logic rx_full_i,
|
||||
output logic rx_push_o,
|
||||
output logic [31:0] rx_data_o,
|
||||
`ifdef PIOLITE_FORMAL_TOP
|
||||
output logic formal_commit_o,
|
||||
`endif
|
||||
output piolite_pkg::sm_runtime_t runtime_o
|
||||
);
|
||||
piolite_pkg::sm_runtime_t s_runtime_d;
|
||||
piolite_pkg::sm_runtime_t s_runtime_q;
|
||||
piolite_pkg::sm_runtime_t s_candidate;
|
||||
piolite_pkg::sm_runtime_t s_tick_base;
|
||||
piolite_pkg::instruction_t s_instruction;
|
||||
piolite_pkg::stall_kind_e s_stall_kind;
|
||||
logic [ 5:0] s_count;
|
||||
logic [ 6:0] s_result_count;
|
||||
logic [ 5:0] s_shift;
|
||||
logic [ 5:0] s_successor_pc;
|
||||
logic [31:0] s_value;
|
||||
logic [31:0] s_chunk;
|
||||
logic [31:0] s_update_mask;
|
||||
logic [31:0] s_source_value;
|
||||
logic [ 3:0] s_instr_flag_set;
|
||||
logic [ 3:0] s_instr_flag_clear;
|
||||
logic [ 7:0] s_fault_code;
|
||||
logic s_fault;
|
||||
logic s_stall;
|
||||
logic s_halted;
|
||||
logic s_taken;
|
||||
logic s_need_tx_pop;
|
||||
logic s_need_rx_push;
|
||||
logic [31:0] s_push_data;
|
||||
logic s_commit;
|
||||
logic s_unused;
|
||||
piolite_pkg::pin_window_t s_in_pins;
|
||||
piolite_pkg::pin_window_t s_out_pins;
|
||||
piolite_pkg::pin_window_t s_set_pins;
|
||||
typedef piolite_pkg::sm_runtime_t sm_runtime_t;
|
||||
|
||||
function automatic sm_runtime_t fault_runtime(input sm_runtime_t runtime, input logic [7:0] code);
|
||||
piolite_pkg::sm_runtime_t result;
|
||||
result = runtime;
|
||||
if (runtime.fault_code == `PIOLITE_FAULT_NONE) begin
|
||||
result.fault_code = code;
|
||||
result.fault_pc = runtime.pc;
|
||||
end
|
||||
result.state = piolite_pkg::Closing;
|
||||
result.output_oe = 32'd0;
|
||||
result.stall = piolite_pkg::StallNone;
|
||||
result.events[`PIOLITE_EVENTS_FAULT] = 1'b1;
|
||||
return result;
|
||||
endfunction
|
||||
|
||||
assign instruction_addr_o = s_runtime_q.pc;
|
||||
assign runtime_o = s_runtime_q;
|
||||
`ifdef PIOLITE_FORMAL_TOP
|
||||
assign formal_commit_o = s_commit;
|
||||
`endif
|
||||
assign s_unused = ^{cfg_i.tx_low, cfg_i.rx_high};
|
||||
assign s_in_pins = cfg_i.in_pins;
|
||||
assign s_out_pins = cfg_i.out_pins;
|
||||
assign s_set_pins = cfg_i.set_pins;
|
||||
|
||||
always_comb begin
|
||||
s_runtime_d = s_runtime_q;
|
||||
s_runtime_d.events = s_runtime_q.events & ~event_clear_i;
|
||||
`ifdef PIOLITE_EXHAUSTIVE_DV
|
||||
if (counter_load_i) begin
|
||||
s_runtime_d.tick_count = 32'hffff_ffff;
|
||||
s_runtime_d.tx_stall_count = 32'hffff_ffff;
|
||||
s_runtime_d.rx_stall_count = 32'hffff_ffff;
|
||||
s_runtime_d.retired_count = 32'hffff_ffff;
|
||||
end
|
||||
`endif
|
||||
s_candidate = s_runtime_d;
|
||||
s_tick_base = s_runtime_d;
|
||||
s_instruction = piolite_pkg::decode_instruction(instruction_i);
|
||||
s_stall_kind = piolite_pkg::StallNone;
|
||||
s_count = 6'd0;
|
||||
s_result_count = 7'd0;
|
||||
s_shift = 6'd0;
|
||||
s_successor_pc = 6'd0;
|
||||
s_value = 32'd0;
|
||||
s_chunk = 32'd0;
|
||||
s_update_mask = 32'd0;
|
||||
s_source_value = 32'd0;
|
||||
s_instr_flag_set = 4'd0;
|
||||
s_instr_flag_clear = 4'd0;
|
||||
s_fault_code = 8'd0;
|
||||
s_fault = 1'b0;
|
||||
s_stall = 1'b0;
|
||||
s_halted = 1'b0;
|
||||
s_taken = 1'b0;
|
||||
s_need_tx_pop = 1'b0;
|
||||
s_need_rx_push = 1'b0;
|
||||
s_push_data = 32'd0;
|
||||
s_commit = 1'b0;
|
||||
flag_set_o = 4'd0;
|
||||
flag_clear_o = 4'd0;
|
||||
tx_pop_o = 1'b0;
|
||||
rx_push_o = 1'b0;
|
||||
rx_data_o = 32'd0;
|
||||
|
||||
if (sm_reset_i || block_reset_i) begin
|
||||
s_runtime_d = '0;
|
||||
if (sm_reset_i && !block_reset_i) begin
|
||||
s_runtime_d.pc = cfg_i.entry;
|
||||
s_runtime_d.divider_remaining = cfg_i.divider;
|
||||
end
|
||||
end else begin
|
||||
if (guard_fault_i || stream_fault_i) begin
|
||||
s_fault_code = guard_fault_i ? guard_fault_code_i : `PIOLITE_FAULT_STREAM_BEAT;
|
||||
s_runtime_d = fault_runtime(s_runtime_d, s_fault_code);
|
||||
end
|
||||
if (abort_i) begin
|
||||
// Review: ABORT is accepted in every state, including resource-bearing DISABLED
|
||||
// and idempotently in CLOSING; every accepted command sets EVENT_ABORT.
|
||||
s_runtime_d.state = piolite_pkg::Closing;
|
||||
s_runtime_d.output_oe = 32'd0;
|
||||
s_runtime_d.stall = piolite_pkg::StallNone;
|
||||
s_runtime_d.events[`PIOLITE_EVENTS_ABORT] = 1'b1;
|
||||
end
|
||||
if (!guard_fault_i && !stream_fault_i && !abort_i) begin
|
||||
if (pause_i) begin
|
||||
s_runtime_d.state = piolite_pkg::Paused;
|
||||
s_runtime_d.stall = piolite_pkg::StallNone;
|
||||
end else if (disable_i) begin
|
||||
s_runtime_d.state = piolite_pkg::Disabled;
|
||||
s_runtime_d.output_oe = 32'd0;
|
||||
s_runtime_d.stall = piolite_pkg::StallNone;
|
||||
end else if (start_i) begin
|
||||
s_candidate = '0;
|
||||
s_candidate.state = piolite_pkg::Running;
|
||||
s_candidate.pc = cfg_i.entry;
|
||||
s_candidate.x = cfg_i.initial_x;
|
||||
s_candidate.y = cfg_i.initial_y;
|
||||
s_candidate.divider_remaining = cfg_i.divider;
|
||||
s_candidate.events = s_runtime_d.events;
|
||||
s_candidate.output_value = cfg_i.initial_value;
|
||||
s_candidate.output_oe = cfg_i.initial_oe;
|
||||
s_runtime_d = s_candidate;
|
||||
end else if (resume_i) begin
|
||||
s_runtime_d.state = piolite_pkg::Running;
|
||||
end else if (s_runtime_q.state == piolite_pkg::Running) begin
|
||||
if (s_runtime_q.divider_remaining > 16'd1) begin
|
||||
s_candidate.divider_remaining = s_runtime_q.divider_remaining - 16'd1;
|
||||
s_runtime_d = s_candidate;
|
||||
end else begin
|
||||
s_candidate.divider_remaining = cfg_i.divider;
|
||||
s_candidate.tick_count = piolite_pkg::sat_increment(s_runtime_d.tick_count);
|
||||
s_tick_base = s_candidate;
|
||||
if (s_candidate.delay_remaining != 4'd0) begin
|
||||
s_candidate.delay_remaining = s_candidate.delay_remaining - 4'd1;
|
||||
s_candidate.stall = piolite_pkg::StallNone;
|
||||
s_runtime_d = s_candidate;
|
||||
end else if ((s_candidate.pc < cfg_i.base) ||
|
||||
({1'b0, s_candidate.pc} >= ({1'b0, cfg_i.base} + cfg_i.length))) begin
|
||||
s_runtime_d = fault_runtime(s_tick_base, `PIOLITE_FAULT_PROGRAM_BOUNDS);
|
||||
end else if (!instruction_valid_i) begin
|
||||
s_runtime_d = fault_runtime(s_tick_base, `PIOLITE_FAULT_INVALID_FETCH);
|
||||
end else if (!piolite_pkg::instruction_legal(s_instruction)) begin
|
||||
s_runtime_d = fault_runtime(s_tick_base, `PIOLITE_FAULT_ILLEGAL_INSTRUCTION);
|
||||
end else begin
|
||||
unique case (s_instruction.opcode)
|
||||
`PIOLITE_OPCODE_CTRL: begin
|
||||
if (s_instruction.operand[7:5] == `PIOLITE_CTRL_CLEAR_ISR) begin
|
||||
s_candidate.isr = 32'd0;
|
||||
s_candidate.isr_count = 6'd0;
|
||||
end else if (s_instruction.operand[7:5] == `PIOLITE_CTRL_CLEAR_OSR) begin
|
||||
s_candidate.osr = 32'd0;
|
||||
s_candidate.osr_count = 6'd0;
|
||||
end
|
||||
end
|
||||
`PIOLITE_OPCODE_SET_PIN, `PIOLITE_OPCODE_SET_DIR: begin
|
||||
if ((s_set_pins.width == 6'd0) || ((piolite_pkg::window_mask(
|
||||
s_set_pins
|
||||
) & ~(cfg_i.claim_mask & cfg_i.output_mask)) != 32'd0)) begin
|
||||
s_fault = 1'b1;
|
||||
s_fault_code = `PIOLITE_FAULT_PIN_AUTHORIZATION;
|
||||
end else begin
|
||||
s_value = ({24'd0, s_instruction.operand} &
|
||||
piolite_pkg::bit_mask(s_set_pins.width)) << s_set_pins.base;
|
||||
s_update_mask = piolite_pkg::window_mask(s_set_pins);
|
||||
if (s_instruction.opcode == `PIOLITE_OPCODE_SET_PIN)
|
||||
s_candidate.output_value =
|
||||
(s_candidate.output_value & ~s_update_mask) | s_value;
|
||||
else s_candidate.output_oe = (s_candidate.output_oe & ~s_update_mask) | s_value;
|
||||
end
|
||||
end
|
||||
`PIOLITE_OPCODE_SET_COUNT: begin
|
||||
if (s_instruction.operand[7]) s_candidate.y = {9'd0, s_instruction.operand[6:0]};
|
||||
else s_candidate.x = {9'd0, s_instruction.operand[6:0]};
|
||||
end
|
||||
`PIOLITE_OPCODE_IN: begin
|
||||
s_count = piolite_pkg::encoded_count(s_instruction.operand[4:0]);
|
||||
s_result_count = {1'b0, s_candidate.isr_count} + {1'b0, s_count};
|
||||
if (s_result_count > {1'b0, (cfg_i.options[`PIOLITE_CONFIG_AUTOPUSH] ?
|
||||
cfg_i.rx_bits : 6'd32)}) begin
|
||||
s_fault = 1'b1;
|
||||
s_fault_code = `PIOLITE_FAULT_SHIFT_BOUNDS;
|
||||
end else if (cfg_i.options[`PIOLITE_CONFIG_AUTOPUSH] &&
|
||||
(s_result_count == {1'b0, cfg_i.rx_bits}) && rx_full_i) begin
|
||||
s_stall = 1'b1;
|
||||
s_stall_kind = piolite_pkg::StallRx;
|
||||
end else begin
|
||||
unique case (s_instruction.operand[7:5])
|
||||
`PIOLITE_IN_PINS: begin
|
||||
if ((s_count > s_in_pins.width) || ((piolite_pkg::window_mask(
|
||||
s_in_pins
|
||||
) & ~cfg_i.claim_mask) != 32'd0)) begin
|
||||
s_fault = 1'b1;
|
||||
s_fault_code = `PIOLITE_FAULT_PIN_AUTHORIZATION;
|
||||
end else
|
||||
s_source_value = (pin_inputs_i >> s_in_pins.base) & piolite_pkg::bit_mask(
|
||||
s_count
|
||||
);
|
||||
end
|
||||
`PIOLITE_IN_X:
|
||||
s_source_value = {16'd0, s_candidate.x} & piolite_pkg::bit_mask(s_count);
|
||||
`PIOLITE_IN_Y:
|
||||
s_source_value = {16'd0, s_candidate.y} & piolite_pkg::bit_mask(s_count);
|
||||
default: s_source_value = 32'd0;
|
||||
endcase
|
||||
if (!s_fault) begin
|
||||
if (cfg_i.options[`PIOLITE_CONFIG_RX_RIGHT])
|
||||
s_candidate.isr = (s_candidate.isr >> s_count) |
|
||||
(s_source_value << (6'd32 - s_count));
|
||||
else s_candidate.isr = (s_candidate.isr << s_count) | s_source_value;
|
||||
s_candidate.isr_count = s_result_count[5:0];
|
||||
if (cfg_i.options[`PIOLITE_CONFIG_AUTOPUSH] &&
|
||||
(s_result_count == {1'b0, cfg_i.rx_bits})) begin
|
||||
if (cfg_i.options[`PIOLITE_CONFIG_RX_RIGHT])
|
||||
s_push_data = s_candidate.isr >> (6'd32 - s_candidate.isr_count);
|
||||
else
|
||||
s_push_data = s_candidate.isr & piolite_pkg::bit_mask(
|
||||
s_candidate.isr_count
|
||||
);
|
||||
s_candidate.isr = 32'd0;
|
||||
s_candidate.isr_count = 6'd0;
|
||||
s_candidate.last_fifo_failed = 1'b0;
|
||||
s_need_rx_push = 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
`PIOLITE_OPCODE_OUT: begin
|
||||
s_count = piolite_pkg::encoded_count(s_instruction.operand[4:0]);
|
||||
if (s_candidate.osr_count == 6'd0) begin
|
||||
if (!cfg_i.options[`PIOLITE_CONFIG_AUTOPULL] || (s_count > cfg_i.tx_bits)) begin
|
||||
s_fault = 1'b1;
|
||||
s_fault_code = `PIOLITE_FAULT_SHIFT_BOUNDS;
|
||||
end else if (tx_empty_i) begin
|
||||
s_stall = 1'b1;
|
||||
s_stall_kind = piolite_pkg::StallTx;
|
||||
end else begin
|
||||
s_candidate.osr = tx_data_i & piolite_pkg::bit_mask(cfg_i.tx_bits);
|
||||
s_candidate.osr_count = cfg_i.tx_bits;
|
||||
s_candidate.last_fifo_failed = 1'b0;
|
||||
s_need_tx_pop = 1'b1;
|
||||
end
|
||||
end else if (s_candidate.osr_count < s_count) begin
|
||||
s_fault = 1'b1;
|
||||
s_fault_code = `PIOLITE_FAULT_SHIFT_BOUNDS;
|
||||
end
|
||||
if (!s_fault && !s_stall) begin
|
||||
if (cfg_i.options[`PIOLITE_CONFIG_TX_RIGHT]) begin
|
||||
s_chunk = s_candidate.osr & piolite_pkg::bit_mask(s_count);
|
||||
s_candidate.osr = (s_candidate.osr >> s_count) &
|
||||
piolite_pkg::bit_mask(s_candidate.osr_count - s_count);
|
||||
end else begin
|
||||
s_shift = s_candidate.osr_count - s_count;
|
||||
s_chunk = (s_candidate.osr >> s_shift) & piolite_pkg::bit_mask(s_count);
|
||||
s_candidate.osr = s_candidate.osr & piolite_pkg::bit_mask(s_shift);
|
||||
end
|
||||
s_candidate.osr_count = s_candidate.osr_count - s_count;
|
||||
unique case (s_instruction.operand[7:5])
|
||||
`PIOLITE_OUT_PINS, `PIOLITE_OUT_DIRECTIONS: begin
|
||||
if ((s_count > s_out_pins.width) || ((piolite_pkg::window_mask(
|
||||
s_out_pins
|
||||
) & ~(cfg_i.claim_mask & cfg_i.output_mask)) != 32'd0)) begin
|
||||
s_fault = 1'b1;
|
||||
s_fault_code = `PIOLITE_FAULT_PIN_AUTHORIZATION;
|
||||
end else begin
|
||||
s_value = s_chunk << s_out_pins.base;
|
||||
s_update_mask = piolite_pkg::bit_mask(s_count) << s_out_pins.base;
|
||||
if (s_instruction.operand[7:5] == `PIOLITE_OUT_PINS)
|
||||
s_candidate.output_value =
|
||||
(s_candidate.output_value & ~s_update_mask) | s_value;
|
||||
else
|
||||
s_candidate.output_oe =
|
||||
(s_candidate.output_oe & ~s_update_mask) | s_value;
|
||||
end
|
||||
end
|
||||
`PIOLITE_OUT_X: s_candidate.x = s_chunk[15:0];
|
||||
`PIOLITE_OUT_Y: s_candidate.y = s_chunk[15:0];
|
||||
default: ;
|
||||
endcase
|
||||
end
|
||||
end
|
||||
`PIOLITE_OPCODE_MOVE: begin
|
||||
unique case (s_instruction.operand[5:3])
|
||||
`PIOLITE_MOVE_ISR: s_source_value = s_candidate.isr;
|
||||
`PIOLITE_MOVE_OSR: s_source_value = s_candidate.osr;
|
||||
`PIOLITE_MOVE_X: s_source_value = {16'd0, s_candidate.x};
|
||||
`PIOLITE_MOVE_Y: s_source_value = {16'd0, s_candidate.y};
|
||||
`PIOLITE_MOVE_ZERO: s_source_value = 32'd0;
|
||||
default: s_source_value = 32'hffff_ffff;
|
||||
endcase
|
||||
unique case (s_instruction.operand[7:6])
|
||||
`PIOLITE_MOVE_ISR: begin
|
||||
s_candidate.isr = s_source_value;
|
||||
s_candidate.isr_count = 6'd32;
|
||||
end
|
||||
`PIOLITE_MOVE_OSR: begin
|
||||
s_candidate.osr = s_source_value;
|
||||
s_candidate.osr_count = 6'd32;
|
||||
end
|
||||
`PIOLITE_MOVE_X: s_candidate.x = s_source_value[15:0];
|
||||
default: s_candidate.y = s_source_value[15:0];
|
||||
endcase
|
||||
end
|
||||
`PIOLITE_OPCODE_FIFO: begin
|
||||
if (s_instruction.operand[7] == `PIOLITE_FIFO_PULL) begin
|
||||
if (tx_empty_i) begin
|
||||
if (s_instruction.operand[6] == `PIOLITE_FIFO_BLOCKING) begin
|
||||
s_stall = 1'b1;
|
||||
s_stall_kind = piolite_pkg::StallTx;
|
||||
end else begin
|
||||
s_candidate.last_fifo_failed = 1'b1;
|
||||
s_candidate.events[`PIOLITE_EVENTS_FIFO_FAIL] = 1'b1;
|
||||
end
|
||||
end else begin
|
||||
s_candidate.osr = tx_data_i & piolite_pkg::bit_mask(cfg_i.tx_bits);
|
||||
s_candidate.osr_count = cfg_i.tx_bits;
|
||||
s_candidate.last_fifo_failed = 1'b0;
|
||||
s_need_tx_pop = 1'b1;
|
||||
end
|
||||
end else begin
|
||||
if (s_candidate.isr_count == 6'd0) begin
|
||||
s_fault = 1'b1;
|
||||
s_fault_code = `PIOLITE_FAULT_SHIFT_BOUNDS;
|
||||
end else if (rx_full_i) begin
|
||||
if (s_instruction.operand[6] == `PIOLITE_FIFO_BLOCKING) begin
|
||||
s_stall = 1'b1;
|
||||
s_stall_kind = piolite_pkg::StallRx;
|
||||
end else begin
|
||||
s_candidate.last_fifo_failed = 1'b1;
|
||||
s_candidate.events[`PIOLITE_EVENTS_FIFO_FAIL] = 1'b1;
|
||||
end
|
||||
end else begin
|
||||
if (cfg_i.options[`PIOLITE_CONFIG_RX_RIGHT])
|
||||
s_push_data = s_candidate.isr >> (6'd32 - s_candidate.isr_count);
|
||||
else
|
||||
s_push_data = s_candidate.isr & piolite_pkg::bit_mask(
|
||||
s_candidate.isr_count
|
||||
);
|
||||
s_candidate.isr = 32'd0;
|
||||
s_candidate.isr_count = 6'd0;
|
||||
s_candidate.last_fifo_failed = 1'b0;
|
||||
s_need_rx_push = 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
`PIOLITE_OPCODE_WAIT: begin
|
||||
if (!s_instruction.operand[7]) begin
|
||||
if ((cfg_i.claim_mask & (32'd1 << s_instruction.operand[5:1])) == 32'd0) begin
|
||||
s_fault = 1'b1;
|
||||
s_fault_code = `PIOLITE_FAULT_PIN_AUTHORIZATION;
|
||||
end else if (pin_inputs_i[s_instruction.operand[5:1]] !=
|
||||
s_instruction.operand[6]) begin
|
||||
s_stall = 1'b1;
|
||||
s_stall_kind = piolite_pkg::StallPin;
|
||||
end
|
||||
end else if (flags_i[{2'd0, s_instruction.operand[3:1]}] !=
|
||||
s_instruction.operand[6]) begin
|
||||
s_stall = 1'b1;
|
||||
s_stall_kind = piolite_pkg::StallFlag;
|
||||
end
|
||||
end
|
||||
`PIOLITE_OPCODE_BRANCH: begin
|
||||
if ((s_instruction.operand[7:5] == `PIOLITE_BRANCH_DEC_X_NONZERO) &&
|
||||
(s_candidate.x != 16'd0))
|
||||
s_candidate.x = s_candidate.x - 16'd1;
|
||||
else if ((s_instruction.operand[7:5] == `PIOLITE_BRANCH_DEC_Y_NONZERO) &&
|
||||
(s_candidate.y != 16'd0))
|
||||
s_candidate.y = s_candidate.y - 16'd1;
|
||||
end
|
||||
default: begin
|
||||
if (s_instruction.operand[7] == `PIOLITE_FLAG_SET)
|
||||
s_instr_flag_set = 4'd1 << s_instruction.operand[6:5];
|
||||
else s_instr_flag_clear = 4'd1 << s_instruction.operand[6:5];
|
||||
end
|
||||
endcase
|
||||
|
||||
if (!s_stall) begin
|
||||
s_successor_pc = {1'b0, s_candidate.pc} + 6'd1;
|
||||
if ((s_instruction.opcode == `PIOLITE_OPCODE_CTRL) &&
|
||||
(s_instruction.operand[7:5] == `PIOLITE_CTRL_HALT)) begin
|
||||
s_halted = 1'b1;
|
||||
end else begin
|
||||
if (s_instruction.opcode == `PIOLITE_OPCODE_BRANCH) begin
|
||||
if (({1'b0, s_instruction.operand[4:0]} < {1'b0, cfg_i.base}) ||
|
||||
({1'b0, s_instruction.operand[4:0]} >=
|
||||
({1'b0, cfg_i.base} + cfg_i.length))) begin
|
||||
if (!s_fault || (`PIOLITE_FAULT_PROGRAM_BOUNDS < s_fault_code))
|
||||
s_fault_code = `PIOLITE_FAULT_PROGRAM_BOUNDS;
|
||||
s_fault = 1'b1;
|
||||
end
|
||||
unique case (s_instruction.operand[7:5])
|
||||
`PIOLITE_BRANCH_ALWAYS: s_taken = 1'b1;
|
||||
`PIOLITE_BRANCH_X_ZERO: s_taken = (s_candidate.x == 16'd0);
|
||||
`PIOLITE_BRANCH_Y_ZERO: s_taken = (s_candidate.y == 16'd0);
|
||||
`PIOLITE_BRANCH_DEC_X_NONZERO, `PIOLITE_BRANCH_DEC_Y_NONZERO: begin
|
||||
s_taken = (s_instruction.operand[7:5] == `PIOLITE_BRANCH_DEC_X_NONZERO) ?
|
||||
(s_candidate.x != 16'd0) : (s_candidate.y != 16'd0);
|
||||
end
|
||||
`PIOLITE_BRANCH_TEST_PIN_HIGH: begin
|
||||
if (!cfg_i.options[`PIOLITE_CONFIG_TEST_PIN_ENABLE] ||
|
||||
((cfg_i.claim_mask & (32'd1 << cfg_i.test_pin)) == 32'd0)) begin
|
||||
if (!s_fault || (`PIOLITE_FAULT_PIN_AUTHORIZATION < s_fault_code))
|
||||
s_fault_code = `PIOLITE_FAULT_PIN_AUTHORIZATION;
|
||||
s_fault = 1'b1;
|
||||
end
|
||||
s_taken = pin_inputs_i[cfg_i.test_pin];
|
||||
end
|
||||
`PIOLITE_BRANCH_FIFO_FAILED: s_taken = s_candidate.last_fifo_failed;
|
||||
default: s_taken = flags_i[{3'd0, cfg_i.test_flag}];
|
||||
endcase
|
||||
if (s_taken) s_successor_pc = {1'b0, s_instruction.operand[4:0]};
|
||||
else if (s_candidate.pc == cfg_i.wrap_top)
|
||||
s_successor_pc = {1'b0, cfg_i.wrap_bottom};
|
||||
end else if (s_candidate.pc == cfg_i.wrap_top) begin
|
||||
s_successor_pc = {1'b0, cfg_i.wrap_bottom};
|
||||
end
|
||||
if (!s_taken && ((s_successor_pc < {1'b0, cfg_i.base}) ||
|
||||
(s_successor_pc >= ({1'b0, cfg_i.base} + cfg_i.length)))) begin
|
||||
if (!s_fault || (`PIOLITE_FAULT_PROGRAM_BOUNDS < s_fault_code))
|
||||
s_fault_code = `PIOLITE_FAULT_PROGRAM_BOUNDS;
|
||||
s_fault = 1'b1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (s_stall) begin
|
||||
s_runtime_d = s_tick_base;
|
||||
s_runtime_d.stall = s_stall_kind;
|
||||
if (s_stall_kind == piolite_pkg::StallTx) begin
|
||||
s_runtime_d.events[`PIOLITE_EVENTS_TX_STALL] = 1'b1;
|
||||
s_runtime_d.tx_stall_count =
|
||||
piolite_pkg::sat_increment(s_tick_base.tx_stall_count);
|
||||
end else if (s_stall_kind == piolite_pkg::StallRx) begin
|
||||
s_runtime_d.events[`PIOLITE_EVENTS_RX_STALL] = 1'b1;
|
||||
s_runtime_d.rx_stall_count =
|
||||
piolite_pkg::sat_increment(s_tick_base.rx_stall_count);
|
||||
end
|
||||
end else if (s_fault) begin
|
||||
s_runtime_d = fault_runtime(s_tick_base, s_fault_code);
|
||||
end else begin
|
||||
s_commit = 1'b1;
|
||||
s_candidate.delay_remaining = s_instruction.delay;
|
||||
s_candidate.stall = piolite_pkg::StallNone;
|
||||
s_candidate.retired_count = piolite_pkg::sat_increment(s_candidate.retired_count);
|
||||
if (s_halted) begin
|
||||
s_candidate.state = piolite_pkg::Halted;
|
||||
s_candidate.events[`PIOLITE_EVENTS_HALT] = 1'b1;
|
||||
end else begin
|
||||
s_candidate.pc = s_successor_pc[4:0];
|
||||
end
|
||||
s_runtime_d = s_candidate;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if (s_commit) begin
|
||||
flag_set_o = s_instr_flag_set;
|
||||
flag_clear_o = s_instr_flag_clear;
|
||||
tx_pop_o = s_need_tx_pop;
|
||||
rx_push_o = s_need_rx_push;
|
||||
rx_data_o = s_push_data;
|
||||
end
|
||||
end
|
||||
|
||||
dffr #(
|
||||
.DATA_WIDTH($bits(piolite_pkg::sm_runtime_t))
|
||||
) u_runtime (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i(rst_n_i),
|
||||
.dat_i (s_runtime_d),
|
||||
.dat_o (s_runtime_q)
|
||||
);
|
||||
`ifdef FORMAL
|
||||
logic s_formal_past_q;
|
||||
always_ff @(posedge clk_i) begin
|
||||
if (!rst_n_i) s_formal_past_q <= 1'b0;
|
||||
else begin
|
||||
s_formal_past_q <= 1'b1;
|
||||
assert (!tx_pop_o || s_commit);
|
||||
assert (!rx_push_o || s_commit);
|
||||
if (s_formal_past_q && $past(s_stall)) begin
|
||||
assert (s_runtime_q.pc == $past(s_runtime_q.pc));
|
||||
assert (s_runtime_q.isr == $past(s_runtime_q.isr));
|
||||
assert (s_runtime_q.osr == $past(s_runtime_q.osr));
|
||||
assert (s_runtime_q.x == $past(s_runtime_q.x) && s_runtime_q.y == $past(s_runtime_q.y));
|
||||
assert (s_runtime_q.isr_count == $past(s_runtime_q.isr_count));
|
||||
assert (s_runtime_q.osr_count == $past(s_runtime_q.osr_count));
|
||||
assert (s_runtime_q.retired_count == $past(s_runtime_q.retired_count));
|
||||
end
|
||||
if (s_formal_past_q && !$past(
|
||||
sm_reset_i || block_reset_i
|
||||
) && ($past(
|
||||
s_runtime_q.fault_code
|
||||
) != `PIOLITE_FAULT_NONE)) begin
|
||||
assert (s_runtime_q.fault_code == $past(s_runtime_q.fault_code));
|
||||
assert (s_runtime_q.fault_pc == $past(s_runtime_q.fault_pc));
|
||||
end
|
||||
if (s_formal_past_q && !$past(
|
||||
sm_reset_i || block_reset_i
|
||||
) && $past(
|
||||
guard_fault_i || stream_fault_i
|
||||
) && ($past(
|
||||
s_runtime_q.fault_code
|
||||
) == `PIOLITE_FAULT_NONE)) begin
|
||||
if ($past(guard_fault_i)) begin
|
||||
assert (s_runtime_q.fault_code == $past(guard_fault_code_i));
|
||||
end else begin
|
||||
assert (s_runtime_q.fault_code == `PIOLITE_FAULT_STREAM_BEAT);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
@@ -1,6 +1,23 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"targets": [
|
||||
{
|
||||
"name": "piolite-p2",
|
||||
"paths": ["rtl/ip/piolite"],
|
||||
"status": "prototype",
|
||||
"baseline_revision": "cd39bdb837c4815636ac931fba0033a260aa146e",
|
||||
"configuration_digest": null,
|
||||
"configuration_profiles": ["configs/ci/ics55-tiny.mk", "configs/ci/ihp130-tiny.mk"],
|
||||
"required_evidence": [],
|
||||
"synthesis_intent": {
|
||||
"registers": "Common scalar/configuration registers and two SM candidate-commit runtime registers",
|
||||
"reset": "validity and runtime reset without payload-array initialization; CLOSING requires explicit recovery",
|
||||
"clock_enable": "PCLK integer enables only; no generated SM clock",
|
||||
"memory": "one 32x16 dual-read IMEM and four 8x32 Common FIFO payload arrays",
|
||||
"pipeline": "registered APB response, combinational dual IMEM reads, atomic SM commit, held stream beats"
|
||||
},
|
||||
"waivers": []
|
||||
},
|
||||
{
|
||||
"name": "spi-p2",
|
||||
"paths": ["rtl/ip/serial/spi_dma_pacing.sv", "rtl/ip/peripheral/dma_core.sv",
|
||||
|
||||
@@ -227,6 +227,19 @@
|
||||
"npu_scheduler.sv",
|
||||
"npu_vector.sv"
|
||||
],
|
||||
"rtl/ip/piolite": [
|
||||
"apb4_piolite.sv",
|
||||
"piolite_core.sv",
|
||||
"piolite_define.svh",
|
||||
"piolite_dma_adapter.sv",
|
||||
"piolite_fifo.sv",
|
||||
"piolite_gpio_guard.sv",
|
||||
"piolite_if.sv",
|
||||
"piolite_imem.sv",
|
||||
"piolite_pkg.sv",
|
||||
"piolite_reg.sv",
|
||||
"piolite_sm.sv"
|
||||
],
|
||||
"rtl/ip/peripheral": [
|
||||
"apb4_clint.sv",
|
||||
"apb4_dma.sv",
|
||||
|
||||
@@ -122,6 +122,7 @@
|
||||
"I2C_",
|
||||
"I2S_",
|
||||
"PSRAM_",
|
||||
"PIOLITE_",
|
||||
"SDIO_",
|
||||
"SDRAM_",
|
||||
"SPISD_",
|
||||
|
||||
@@ -0,0 +1,561 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Fail-closed standalone PIOLITE-P2 flows; no Tiny integration is implied."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
from scripts.check_simulation import DEFAULT_FAILURE # noqa: E402
|
||||
from scripts.setup_helpers import atomic_write # noqa: E402
|
||||
from scripts.yosys_period import period_ps # noqa: E402
|
||||
|
||||
COMMON = ROOT / "rtl/managed/clusterip/common/rtl"
|
||||
IP = ROOT / "rtl/ip/piolite"
|
||||
TB = ROOT / "tests/rtl"
|
||||
TOP = "apb4_piolite_block_top"
|
||||
WRAPPER = ROOT / f"physical/smoke/syn/yosys/{TOP}.sv"
|
||||
RTL_SOURCES = [
|
||||
COMMON / "interface/apb4_if.sv",
|
||||
COMMON / "utils/register.sv",
|
||||
ROOT / "rtl/ip/util/soc_common_fifo.sv",
|
||||
*[
|
||||
IP / f"{name}.sv"
|
||||
for name in (
|
||||
"piolite_pkg", "piolite_if", "piolite_imem", "piolite_fifo", "piolite_sm",
|
||||
"piolite_core", "piolite_gpio_guard", "piolite_dma_adapter", "piolite_reg",
|
||||
"apb4_piolite",
|
||||
)
|
||||
],
|
||||
]
|
||||
SOURCES = [*RTL_SOURCES, WRAPPER]
|
||||
INCLUDES = [COMMON, COMMON / "utils", IP, TB, ROOT / "physical/smoke/syn/yosys"]
|
||||
CASES = ("piolite_exhaustive_tb", "piolite_isa_tb", "piolite_core_tb", "piolite_fifo_tb",
|
||||
"piolite_apb_tb", "piolite_lifecycle_tb")
|
||||
COVERAGE = {
|
||||
"PL-V01": ("PL-V01 exhaustive-legal-and-reserved-semantics",),
|
||||
"PL-V03": ("PL-V03 dual-tick-divider-delay-and-pause-resume",),
|
||||
"PL-V04": ("PL-V04 counters-move-wrap-and-branches",),
|
||||
"PL-V05": ("PL-V05 exact-autopush-threshold-and-atomic-stall",),
|
||||
"PL-V06": ("PL-V06 pre-sample-backpressure-and-stall-accounting",
|
||||
"PL-V06 randomized-preedge-fifo-conservation"),
|
||||
"PL-V07": ("PL-V07 all-csr-reset-access-fields-and-errors",
|
||||
"PL-V07 live-config-imem-write-atomicity"),
|
||||
"PL-V08": ("PL-V08 same-edge-command-commit-and-set-wins-w1c",
|
||||
"PL-V08 pause-suppresses-all-candidate-side-effects",
|
||||
"PL-V08 first-fault-priority-and-retention",
|
||||
"PL-V08 saturating-tick-stall-retired-counters",
|
||||
"PL-V08 paused-snapshot-while-dma-progresses"),
|
||||
}
|
||||
|
||||
|
||||
def digest(path: Path) -> str:
|
||||
return hashlib.sha256(path.read_bytes()).hexdigest()
|
||||
|
||||
|
||||
def dump(path: Path, data: dict) -> None:
|
||||
atomic_write(path, json.dumps(data, indent=2, sort_keys=True) + "\n")
|
||||
|
||||
|
||||
def inputs() -> dict[str, str]:
|
||||
files = set(SOURCES) | {
|
||||
COMMON / "utils/fifo.sv", COMMON / "config.svh", IP / "piolite_define.svh",
|
||||
ROOT / "Makefile", ROOT / "scripts/piolite_p2.py", ROOT / "scripts/run_flow.py",
|
||||
ROOT / "scripts/check_simulation.py", ROOT / "scripts/setup_helpers.py",
|
||||
ROOT / "scripts/piolite_model.py",
|
||||
ROOT / "scripts/piolite_isa.py", ROOT / "scripts/piolite_asm.py",
|
||||
ROOT / "scripts/piolite_p2_vectors.py", ROOT / "scripts/piolite_p2_exhaustive.py",
|
||||
ROOT / "scripts/yosys_period.py", ROOT / "dependencies/dependencies.lock.json",
|
||||
ROOT / "docs/ip/piolite.md", ROOT / "configs/ci/ics55-tiny.mk",
|
||||
ROOT / "configs/ci/ihp130-tiny.mk", ROOT / "crt/include/retrosoc/hal/piolite_regs.h",
|
||||
ROOT / "tests/test_piolite_register_parity.py", ROOT / "rtl/rtl_style_audit.json",
|
||||
ROOT / "rtl/rtl_readiness.json", ROOT / "rtl/rtl_style_manifest.json",
|
||||
ROOT / "rtl/tiny/integration/clock_reset_domains.json",
|
||||
ROOT / "physical/smoke/sta/opensta/piolite_block.tcl",
|
||||
ROOT / "physical/smoke/sta/opensta/piolite_block.sdc",
|
||||
}
|
||||
# The normative contract is a producer input. The verification ledger is
|
||||
# post-run reporting output, so updating it cannot invalidate producer hashes.
|
||||
files.update(TB.glob("piolite_*"))
|
||||
files.update((ROOT / "physical/smoke/syn/yosys/script").glob("*"))
|
||||
return {str(path.relative_to(ROOT)): digest(path) for path in sorted(files) if path.is_file()}
|
||||
|
||||
|
||||
def tool_inputs() -> dict[str, dict[str, str]]:
|
||||
result = {}
|
||||
for name in ("verilator", "iverilog", "vvp", "sv2v", "yosys", "sby", "bitwuzla", "sta"):
|
||||
executable = shutil.which(name)
|
||||
if executable is None:
|
||||
raise ValueError(f"missing PIOLITE tool: {name}")
|
||||
path = Path(executable).resolve()
|
||||
version_arg = "-V" if name in {"iverilog", "vvp"} else "--version"
|
||||
version = subprocess.run([str(path), version_arg], text=True, capture_output=True,
|
||||
check=False, timeout=30)
|
||||
result[name] = {
|
||||
"path": str(path), "sha256": digest(path),
|
||||
"version": (version.stdout + version.stderr).splitlines()[0],
|
||||
}
|
||||
result["python3"] = {
|
||||
"path": str(Path(sys.executable).resolve()),
|
||||
"sha256": digest(Path(sys.executable).resolve()),
|
||||
"version": sys.version.splitlines()[0],
|
||||
}
|
||||
return result
|
||||
|
||||
|
||||
def execute(directory: Path, name: str, command: list[str], timeout: int = 300,
|
||||
environment: dict | None = None) -> tuple[dict, str]:
|
||||
log = directory / f"{name}.log"
|
||||
result_path = directory / f"{name}.json"
|
||||
args = [sys.executable, str(ROOT / "scripts/run_flow.py"), "--tool", name,
|
||||
"--log", str(log), "--result", str(result_path), "--cwd", str(ROOT)]
|
||||
for key, value in (environment or {}).items():
|
||||
args += ["--env", f"{key}={value}"]
|
||||
args += ["--", "timeout", "--foreground", "--kill-after=5s", f"{timeout}s", *command]
|
||||
completed = subprocess.run(args, cwd=ROOT, check=False)
|
||||
data = json.loads(result_path.read_text())
|
||||
content = log.read_text(errors="replace")
|
||||
if completed.returncode != 0 or data.get("exit_code") != 0 or data.get("status") != "passed":
|
||||
raise RuntimeError(f"{name} failed; see {log}")
|
||||
if re.search(r"(?im)^Error:", content):
|
||||
raise RuntimeError(f"{name} reported a tool error; see {log}")
|
||||
return data, content
|
||||
|
||||
|
||||
def require_pass(content: str, result: dict, marker: str) -> None:
|
||||
if result.get("status") != "passed" or result.get("exit_code") != 0:
|
||||
raise ValueError("command did not pass")
|
||||
if content.count(marker) != 1 or DEFAULT_FAILURE.search(content):
|
||||
raise ValueError(f"missing/duplicate success or forbidden marker: {marker}")
|
||||
|
||||
|
||||
def verilator_config(directory: Path) -> str:
|
||||
path = directory / "common.vlt"
|
||||
atomic_write(path, "`verilator_config\n"
|
||||
f'lint_off -rule DECLFILENAME -file "{COMMON}/utils/register.sv"\n'
|
||||
f'lint_off -rule DECLFILENAME -file "{COMMON}/utils/fifo.sv"\n'
|
||||
f'lint_off -rule UNUSEDSIGNAL -file "{COMMON}/interface/apb4_if.sv"\n'
|
||||
f'lint_off -rule UNUSEDSIGNAL -file "{IP}/piolite_pkg.sv"\n')
|
||||
return str(path)
|
||||
|
||||
|
||||
def vector_file(directory: Path, top: str) -> Path:
|
||||
if top == "piolite_exhaustive_tb":
|
||||
execute(directory, "exhaustive-vectors",
|
||||
[sys.executable, str(ROOT / "scripts/piolite_p2_exhaustive.py"),
|
||||
str(directory / "exhaustive")])
|
||||
return directory / "exhaustive/exhaustive.hex"
|
||||
execute(directory, "vectors", [sys.executable, str(ROOT / "scripts/piolite_p2_vectors.py"),
|
||||
str(directory / "vectors")])
|
||||
return directory / "vectors/vectors.hex"
|
||||
|
||||
|
||||
def simulate(directory: Path, simulator: str, top: str, jobs: int) -> None:
|
||||
vectors = vector_file(directory, top) if top in {"piolite_exhaustive_tb", "piolite_isa_tb"} else None
|
||||
if top == "piolite_exhaustive_tb":
|
||||
sources = [COMMON / "utils/register.sv", IP / "piolite_pkg.sv", IP / "piolite_sm.sv"]
|
||||
extra = []
|
||||
elif top == "piolite_fifo_tb":
|
||||
sources = [COMMON / "utils/register.sv", ROOT / "rtl/ip/util/soc_common_fifo.sv",
|
||||
IP / "piolite_fifo.sv"]
|
||||
extra = []
|
||||
else:
|
||||
sources = RTL_SOURCES
|
||||
extra = [TB / "piolite_pin_bfm.sv", TB / "piolite_stream_bfm.sv"]
|
||||
extra.append(TB / f"{top}.sv")
|
||||
model = directory / top
|
||||
defines = [f"-I{path}" for path in INCLUDES]
|
||||
if top == "piolite_exhaustive_tb":
|
||||
defines.append("-DPIOLITE_EXHAUSTIVE_DV")
|
||||
if simulator == "VERILATOR":
|
||||
command = ["verilator", "--binary", "--timing", "--timescale", "1ns/1ps",
|
||||
verilator_config(directory), "--assert", "-j", str(jobs), "--top-module", top,
|
||||
"--Mdir", str(directory / f"obj-{top}"), "-o", str(model), *defines,
|
||||
*map(str, sources + extra)]
|
||||
else:
|
||||
converted = directory / f"{top}.v"
|
||||
conversion = subprocess.run(["sv2v", "-DSV_ASSRT_DISABLE", *defines,
|
||||
*map(str, sources + extra)], capture_output=True, text=True,
|
||||
timeout=180, check=False)
|
||||
atomic_write(directory / f"{top}-sv2v.log", conversion.stderr)
|
||||
if conversion.returncode:
|
||||
raise RuntimeError(conversion.stderr)
|
||||
atomic_write(converted, "`timescale 1ns/1ps\n" + conversion.stdout)
|
||||
command = ["iverilog", "-g2012", "-s", top, "-o", str(model), str(converted)]
|
||||
execute(directory, f"{top}-compile", command, environment={"CCACHE_DISABLE": "1"})
|
||||
launch = [str(model)] if simulator == "VERILATOR" else ["vvp", str(model)]
|
||||
if vectors is not None:
|
||||
launch.append(f"+PIOLITE_{'EXHAUSTIVE' if top == 'piolite_exhaustive_tb' else 'VECTORS'}={vectors}")
|
||||
result, log = execute(directory, top, launch, timeout=600)
|
||||
require_pass(log, result, f"PIOLITE_P2_PASS {top}")
|
||||
if top == "piolite_exhaustive_tb":
|
||||
if "PIOLITE_P2_EXHAUSTIVE legal=22257 rejected=43279 total=65536" not in log:
|
||||
raise ValueError("PIOLITE exhaustive count marker is missing or incorrect")
|
||||
counts = json.loads((directory / "exhaustive/exhaustive.json").read_text())
|
||||
if (counts["legal"], counts["rejected"], counts["total"]) != (22257, 43279, 65536):
|
||||
raise ValueError("PIOLITE exhaustive vector counts are incorrect")
|
||||
dump(directory / "exhaustive-counts.json", counts)
|
||||
|
||||
|
||||
def check_coverage(directory: Path) -> None:
|
||||
logs = {case: (directory / f"{case}.log").read_text() for case in CASES}
|
||||
rows = []
|
||||
for requirement, markers in COVERAGE.items():
|
||||
for marker in markers:
|
||||
full_marker = f"PIOLITE_P2_COVER {marker}"
|
||||
evidence = [case for case, content in logs.items() if full_marker in content]
|
||||
count = sum(content.count(full_marker) for content in logs.values())
|
||||
if count != 1:
|
||||
raise ValueError(f"coverage marker count {count}, expected 1: {full_marker}")
|
||||
rows.append({"requirement": requirement, "marker": marker,
|
||||
"status": "passed", "evidence": evidence})
|
||||
dump(directory / "pl-v-checklist.json", {
|
||||
"requirements": sorted(COVERAGE), "rows": rows,
|
||||
"all_p2_rows_closed": True, "phase_accepted": False,
|
||||
})
|
||||
|
||||
|
||||
EXPECTED_ASSERTIONS = {
|
||||
"fifo_prove": (17, "76f37737c5e120b5eeec892966ba2a055670831c9a49111baa8d58a08b5096c0"),
|
||||
"top_bmc": (28, "0a1313b860da992ffb6593959b8aa5e90bacfd4f22538833146eb1bc5bc1a140"),
|
||||
}
|
||||
EXPECTED_COVERS = [
|
||||
"pready",
|
||||
"formal_sm0[43:41] == 3'd1",
|
||||
"formal_sm0[40]",
|
||||
"formal_sm0[43:41] == 3'd4",
|
||||
"formal_sm0[39:32] != 8'd0",
|
||||
"formal_sm0[31:0] != 32'd0",
|
||||
]
|
||||
|
||||
|
||||
def formal_model_counts(path: Path) -> dict[str, int]:
|
||||
content = path.read_text()
|
||||
return {kind: len(re.findall(rf"^; yosys-smt2-{kind}\b", content, re.M))
|
||||
for kind in ("assert", "assume", "cover")}
|
||||
|
||||
|
||||
def validate_assertions(path: Path, label: str) -> str:
|
||||
lines = [line for line in path.read_text().splitlines()
|
||||
if line.startswith("; yosys-smt2-assert ")]
|
||||
actual = hashlib.sha256("\n".join(lines).encode()).hexdigest()
|
||||
expected_count, expected_digest = EXPECTED_ASSERTIONS[label]
|
||||
if len(lines) != expected_count or actual != expected_digest:
|
||||
raise ValueError(f"PIOLITE {label} assertion set changed: count={len(lines)}, sha256={actual}")
|
||||
return actual
|
||||
|
||||
|
||||
def validate_cover(directory: Path, label: str, converted: Path, model: Path) -> list[dict]:
|
||||
expressions = re.findall(r"^\s*cover\s*\((.*?)\);", converted.read_text(), re.M)
|
||||
expected_present = [expression for expression in expressions if expression in EXPECTED_COVERS]
|
||||
if expected_present != EXPECTED_COVERS:
|
||||
raise ValueError(f"PIOLITE {label} cover goals changed: {expected_present}")
|
||||
comments = re.findall(r"^; yosys-smt2-cover\s+(\d+)\s+(\S+)", model.read_text(), re.M)
|
||||
if [int(index) for index, _ in comments] != list(range(len(EXPECTED_COVERS))):
|
||||
raise ValueError(f"PIOLITE {label} cover-site indices changed: {comments}")
|
||||
sites = []
|
||||
for index, name in comments:
|
||||
goal = EXPECTED_COVERS[int(index)]
|
||||
witness = directory / label / "engine_0" / f"trace{index}.yw"
|
||||
if not witness.is_file():
|
||||
raise ValueError(f"PIOLITE cover goal has no witness: {goal} ({witness})")
|
||||
data = json.loads(witness.read_text())
|
||||
if data.get("format") != "Yosys Witness Trace" or not data.get("steps"):
|
||||
raise ValueError(f"PIOLITE cover witness is empty/invalid: {witness}")
|
||||
sites.append({"goal": goal, "smt2_site": name, "witness": witness.name,
|
||||
"steps": len(data["steps"])})
|
||||
if len(sites) != len(EXPECTED_COVERS):
|
||||
raise ValueError(f"PIOLITE {label} cover witness count changed")
|
||||
return sites
|
||||
|
||||
|
||||
def formal_task(directory: Path, label: str, mode: str, depth: int, timeout: int,
|
||||
sources: list[Path], top: bool) -> dict[str, int]:
|
||||
converted = directory / f"{label}.v"
|
||||
defines = ["-DFORMAL", "-DSV_ASSRT_DISABLE"] + (["-DPIOLITE_FORMAL_TOP"] if top else [])
|
||||
conversion = subprocess.run(["sv2v", "-E", "Assert", *defines,
|
||||
*[f"-I{path}" for path in INCLUDES], *map(str, sources)],
|
||||
capture_output=True, text=True, timeout=180, check=False)
|
||||
atomic_write(directory / f"{label}-sv2v.log", conversion.stderr)
|
||||
if conversion.returncode or not re.search(r"\bassert\s*\(", conversion.stdout):
|
||||
raise RuntimeError(f"PIOLITE {label} formal conversion removed assertions")
|
||||
if mode == "cover" and not re.search(r"\bcover\s*\(", conversion.stdout):
|
||||
raise RuntimeError(f"PIOLITE {label} formal conversion removed cover goals")
|
||||
atomic_write(converted, conversion.stdout)
|
||||
config = directory / f"{label}.sby"
|
||||
engine = "smtbmc --nopresat bitwuzla" if mode == "cover" else "smtbmc bitwuzla"
|
||||
waveform = "vcd off\n" if mode == "cover" else ""
|
||||
atomic_write(config, f"[options]\nmode {mode}\ndepth {depth}\ntimeout {timeout}\n{waveform}\n"
|
||||
f"[engines]\n{engine}\n\n[script]\n"
|
||||
f"read_verilog -formal -sv {label}.v\nprep -top piolite_formal\nasync2sync\ndffunmap\n\n"
|
||||
f"[files]\n{label}.v {converted}\n")
|
||||
execute(directory, label, ["sby", "-d", str(directory / label), str(config)], timeout + 60)
|
||||
if (directory / label / "status").read_text().split()[0] != "PASS":
|
||||
raise ValueError(f"PIOLITE {label} formal proof did not pass")
|
||||
model = directory / label / "model/design_smt2.smt2"
|
||||
counts = formal_model_counts(model)
|
||||
required = "cover" if mode == "cover" else "assert"
|
||||
if counts[required] == 0:
|
||||
raise ValueError(f"PIOLITE {label} has no active {required} properties")
|
||||
if mode == "cover":
|
||||
counts["sites"] = validate_cover(directory, label, converted, model)
|
||||
counts["witnesses"] = len(counts["sites"])
|
||||
else:
|
||||
counts["assertion_sha256"] = validate_assertions(model, label)
|
||||
return counts
|
||||
|
||||
|
||||
def formal_selfcheck(directory: Path) -> None:
|
||||
negative = directory / "selfcheck"
|
||||
negative.mkdir()
|
||||
original = directory / "top_bmc/model/design_smt2.smt2"
|
||||
lines = original.read_text().splitlines()
|
||||
removed = next(line for line in lines if line.startswith("; yosys-smt2-assert "))
|
||||
(negative / "deleted-assertion.smt2").write_text("\n".join(
|
||||
line for line in lines if line != removed) + "\n")
|
||||
try:
|
||||
validate_assertions(negative / "deleted-assertion.smt2", "top_bmc")
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
raise ValueError("PIOLITE selfcheck accepted a deleted assertion")
|
||||
|
||||
converted = negative / "top_cover.v"
|
||||
model = negative / "design_smt2.smt2"
|
||||
engine = negative / "engine_0"
|
||||
engine.mkdir()
|
||||
shutil.copy(directory / "top_cover.v", converted)
|
||||
shutil.copy(directory / "top_cover/model/design_smt2.smt2", model)
|
||||
for index in range(6):
|
||||
shutil.copy(directory / f"top_cover/engine_0/trace{index}.yw", engine / f"trace{index}.yw")
|
||||
(engine / "trace5.yw").unlink()
|
||||
try:
|
||||
validate_cover(negative, "", converted, model)
|
||||
except (ValueError, FileNotFoundError):
|
||||
pass
|
||||
else:
|
||||
raise ValueError("PIOLITE selfcheck accepted a missing named cover witness")
|
||||
dump(negative / "verdict.json", {
|
||||
"deleted_assertion_rejected": True,
|
||||
"missing_named_witness_rejected": True,
|
||||
"status": "passed",
|
||||
})
|
||||
|
||||
|
||||
def formal(directory: Path) -> None:
|
||||
fifo_sources = [COMMON / "utils/register.sv", ROOT / "rtl/ip/util/soc_common_fifo.sv",
|
||||
IP / "piolite_fifo.sv", TB / "piolite_formal_props.sv", TB / "piolite_formal.sv"]
|
||||
results = {"fifo_prove": formal_task(directory, "fifo_prove", "prove", 16, 300,
|
||||
fifo_sources, False)}
|
||||
top_sources = [*SOURCES, TB / "piolite_formal_props.sv", TB / "piolite_formal.sv"]
|
||||
results["top_bmc"] = formal_task(directory, "top_bmc", "bmc", 12, 600, top_sources, True)
|
||||
results["top_cover"] = formal_task(directory, "top_cover", "cover", 24, 1800,
|
||||
top_sources, True)
|
||||
formal_selfcheck(directory)
|
||||
dump(directory / "formal-bounds.json", {
|
||||
"models": results,
|
||||
"bounds": {"fifo_prove": 16, "top_bmc": 12, "top_cover": 24},
|
||||
"assumptions": [
|
||||
"reset is low for the first edge then high",
|
||||
"APB addresses are aligned and restricted to the implemented 4 KiB window",
|
||||
"write strobes are full-word when PWRITE is asserted",
|
||||
"GPIO readiness/filter inputs are stable; USER ownership may be lost arbitrarily",
|
||||
"DMA job-owned inputs are inactive; stream validity and sidebands are symbolic",
|
||||
"bounded proofs do not establish external liveness, analog CDC, or product timing",
|
||||
],
|
||||
"required_top_witnesses": [
|
||||
"APB PREADY", "SM RUNNING", "SM commit", "CLOSING", "nonzero fault", "nonzero OE"
|
||||
],
|
||||
"phase_accepted": False,
|
||||
})
|
||||
|
||||
|
||||
def synth(directory: Path, pdk: str) -> None:
|
||||
period = period_ps(json.loads((ROOT / "rtl/tiny/integration/clock_reset_domains.json").read_text()),
|
||||
"peripheral")
|
||||
for name in ("out", "tmp", "rpt"):
|
||||
(directory / name).mkdir()
|
||||
filelist = directory / "source.fl"
|
||||
atomic_write(filelist, "\n".join([*[f"+incdir+{path}" for path in INCLUDES],
|
||||
*map(str, SOURCES)]) + "\n")
|
||||
execute(directory, "yosys", ["yosys", "-t", "--perffile", str(directory / "yosys-perf.json"),
|
||||
"-c", str(ROOT / "physical/smoke/syn/yosys/script/synth.tcl")], 10800, {
|
||||
"PDK": pdk, "SOC": "TINY", "SYNTH_RECIPE": "balanced", "HAVE_SRAM_MACRO": "NO",
|
||||
"YOSYS_TARGET_PERIOD_PS": str(period), "SRAM_SIZE_KIB": "128", "SV_FLIST": str(filelist),
|
||||
"TOP_DESIGN": TOP, "PROJ_NAME": TOP, "WORK": str(directory / "tmp"),
|
||||
"BUILD": str(directory / "out"), "REPORTS": str(directory / "rpt"),
|
||||
"NETLIST": str(directory / "out/netlist.v"), "CONFIG": str(directory / "out/config"),
|
||||
"YOSYS_KEEP_HIER_INST": "", "YOSYS_REPORT_INSTS": "", "YOSYS_FLATTEN_HIER": "1"})
|
||||
mapped = json.loads((directory / "out/netlist.v.json").read_text())["modules"][TOP]
|
||||
cells = [cell["type"] for cell in mapped["cells"].values()]
|
||||
if any(not (cell.startswith("sg13g2_") if pdk == "IHP130" else cell.endswith("H7R"))
|
||||
for cell in cells):
|
||||
raise ValueError("foreign, unresolved or macro cell in PIOLITE block netlist")
|
||||
generic = (directory / f"rpt/{TOP}_generic.rpt").read_text()
|
||||
if "$dlatch" in generic.lower() or "$_dlatch" in generic.lower():
|
||||
raise ValueError("PIOLITE inferred latch")
|
||||
check = (directory / f"rpt/{TOP}_synth.rpt").read_text()
|
||||
if "Found and reported 0 problems." not in check:
|
||||
raise ValueError("PIOLITE netlist structural check did not pass")
|
||||
memory_text = (directory / f"rpt/{TOP}_pre_memory.json").read_text()
|
||||
memory = json.loads(re.sub(r"^\[\d+\.\d+\]\s?", "", memory_text, flags=re.M))["modules"]
|
||||
imem_bits = sum(item.get("num_memory_bits", 0) for name, item in memory.items()
|
||||
if "piolite_imem" in name)
|
||||
fifo_bits = sorted(item.get("num_memory_bits", 0) for name, item in memory.items()
|
||||
if name.startswith("\\fifo$") and item.get("num_memory_bits", 0))
|
||||
sm_instances = sum(name.startswith("\\piolite_sm$") for name in memory)
|
||||
fifo_instances = sum(name.startswith("\\fifo$") for name in memory)
|
||||
if (imem_bits != 512 or fifo_bits != [256, 256, 256, 256] or
|
||||
sm_instances != 2 or fifo_instances != 4):
|
||||
raise ValueError(f"PIOLITE geometry/mapping differs: sm={sm_instances}, "
|
||||
f"fifo={fifo_instances}/{fifo_bits}, imem={imem_bits}")
|
||||
area_text = (directory / f"rpt/{TOP}_area.json").read_text()
|
||||
area = json.loads(re.sub(r"^\[\d+\.\d+\]\s?", "", area_text, flags=re.M))["design"]["area"]
|
||||
dump(directory / "mapping.json", {"top": TOP, "pdk": pdk, "period_ps": period,
|
||||
"sm_instances": sm_instances, "fifo_instances": fifo_instances, "imem_bits": imem_bits,
|
||||
"fifo_memory_types": fifo_bits, "cells": len(cells), "area": area, "sram_macros": 0})
|
||||
|
||||
|
||||
def netlist_adapter(netlist: Path, directory: Path) -> Path:
|
||||
ports = json.loads(Path(str(netlist) + ".json").read_text())["modules"][TOP]["ports"]
|
||||
groups = {}
|
||||
connections = []
|
||||
for name, value in ports.items():
|
||||
match = re.fullmatch(r"(.*?)(?:__v(\d+)|_(\d+)_|\[(\d+)\])", name)
|
||||
if match:
|
||||
base = match[1]
|
||||
index = int(next(item for item in match.groups()[1:] if item is not None))
|
||||
group = groups.setdefault(base, {"direction": value["direction"], "indices": []})
|
||||
if group["direction"] != value["direction"] or len(value["bits"]) != 1:
|
||||
raise ValueError("invalid split-port direction/width")
|
||||
group["indices"].append(index)
|
||||
target = f"{base}[{index}]"
|
||||
else:
|
||||
base = name
|
||||
groups[base] = {"direction": value["direction"], "indices": list(range(len(value["bits"])))}
|
||||
target = name
|
||||
escaped = f"\\{name} " if not re.fullmatch(r"[A-Za-z_]\w*", name) else name
|
||||
connections.append(f".{escaped}({target})")
|
||||
declarations = []
|
||||
for name, value in groups.items():
|
||||
indices = sorted(value["indices"])
|
||||
if indices != list(range(len(indices))):
|
||||
raise ValueError("noncontiguous split port")
|
||||
width = "" if len(indices) == 1 else f"[{len(indices)-1}:0] "
|
||||
declarations.append(f'{value["direction"]} wire {width}{name}')
|
||||
path = directory / "adapter.v"
|
||||
atomic_write(path, "module piolite_netlist_adapter(\n" + ",\n".join(declarations) + ");\n" +
|
||||
f"{TOP} u_mapped(\n" + ",\n".join(connections) + ");\nendmodule\n")
|
||||
return path
|
||||
|
||||
|
||||
def technology(pdk: str) -> tuple[Path, list[Path]]:
|
||||
if pdk == "ICS55":
|
||||
return (ROOT / ".cache/retrosoc/pdk/ics55/ics55_h7cr_ss.lib",
|
||||
[ROOT / ".cache/retrosoc/pdk/ics55/ics55_h7cr_functional.v"])
|
||||
base = ROOT / "physical/pdk/IHP-Open-PDK/ihp-sg13g2/libs.ref/sg13g2_stdcell"
|
||||
return base / "lib/sg13g2_stdcell_slow_1p08V_125C.lib", [
|
||||
base / "verilog/sg13g2_udp.v", base / "verilog/sg13g2_stdcell.v"]
|
||||
|
||||
|
||||
def netsim(directory: Path, netlist: Path, pdk: str) -> None:
|
||||
_, models = technology(pdk)
|
||||
adapter = netlist_adapter(netlist, directory)
|
||||
top = "piolite_netlist_tb"
|
||||
model = directory / top
|
||||
command = ["iverilog", "-g2012", "-gno-specify", "-s", top, "-o", str(model),
|
||||
"-DPIOLITE_DUT=piolite_netlist_adapter", "-DPIOLITE_SCALAR_APB",
|
||||
*[f"-I{path}" for path in INCLUDES],
|
||||
*map(str, [COMMON / "interface/apb4_if.sv"] + models +
|
||||
[netlist, adapter, TB / "piolite_pin_bfm.sv",
|
||||
TB / "piolite_stream_bfm.sv", TB / f"{top}.sv"])]
|
||||
execute(directory, f"{top}-compile", command)
|
||||
result, log = execute(directory, top, ["vvp", str(model)], timeout=600)
|
||||
require_pass(log, result, f"PIOLITE_P2_PASS {top}")
|
||||
|
||||
|
||||
def checked_record(path: Path, source: dict) -> dict:
|
||||
data = json.loads(path.read_text())
|
||||
if data.get("status") != "passed" or data.get("source") != source:
|
||||
raise ValueError(f"incomplete or stale PIOLITE evidence: {path}")
|
||||
for name, value in data.get("artifacts", {}).items():
|
||||
if digest(Path(name)) != value:
|
||||
raise ValueError(f"changed PIOLITE artifact: {name}")
|
||||
return data
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("stage", choices=("lint", "rtl", "formal", "synth", "netsim", "sta", "report"))
|
||||
parser.add_argument("--variant-root", type=Path, required=True)
|
||||
parser.add_argument("--pdk", choices=("ICS55", "IHP130"), required=True)
|
||||
parser.add_argument("--simulator", choices=("VERILATOR", "IVERILOG"), default="VERILATOR")
|
||||
parser.add_argument("--jobs", type=int, default=4)
|
||||
args = parser.parse_args()
|
||||
variant = args.variant_root.resolve()
|
||||
variant.relative_to(ROOT / "build")
|
||||
manifest = json.loads((variant / "meta/manifest.json").read_text())
|
||||
if manifest["configuration"]["SOC"] != "TINY" or manifest["configuration"]["PDK"] != args.pdk:
|
||||
raise ValueError("profile/PDK mismatch")
|
||||
phase = variant / "meta/piolite-p2"
|
||||
suffix = args.stage + (f"-{args.simulator.lower()}" if args.stage == "rtl" else "")
|
||||
directory = phase / suffix
|
||||
if directory.exists():
|
||||
raise ValueError(f"existing attempt: {directory}; use a fresh timestamp")
|
||||
directory.mkdir(parents=True)
|
||||
source = inputs()
|
||||
record = {"phase": "PIOLITE-P2", "stage": args.stage, "status": "running",
|
||||
"source": source, "manifest": manifest, "phase_accepted": False}
|
||||
if args.stage == "rtl":
|
||||
record["cases"] = list(CASES)
|
||||
dump(directory / "record.json", record)
|
||||
try:
|
||||
record["tools"] = tool_inputs()
|
||||
if args.stage == "lint":
|
||||
execute(directory, "lint", ["verilator", "--lint-only", "--Wall", verilator_config(directory),
|
||||
"--top-module", TOP, *[f"-I{path}" for path in INCLUDES], *map(str, SOURCES)])
|
||||
elif args.stage == "rtl":
|
||||
for top in CASES:
|
||||
simulate(directory, args.simulator, top, args.jobs)
|
||||
check_coverage(directory)
|
||||
elif args.stage == "formal":
|
||||
formal(directory)
|
||||
elif args.stage == "synth":
|
||||
synth(directory, args.pdk)
|
||||
elif args.stage in ("netsim", "sta"):
|
||||
checked_record(phase / "synth/record.json", source)
|
||||
netlist = phase / "synth/out/netlist.v"
|
||||
if args.stage == "netsim":
|
||||
netsim(directory, netlist, args.pdk)
|
||||
else:
|
||||
liberty, _ = technology(args.pdk)
|
||||
execute(directory, "sta", ["sta", "-exit", str(ROOT /
|
||||
"physical/smoke/sta/opensta/piolite_block.tcl")], environment={
|
||||
"PIOLITE_NETLIST": str(netlist), "PIOLITE_LIBERTY": str(liberty),
|
||||
"PIOLITE_REPORT_DIR": str(directory)})
|
||||
else:
|
||||
record["stages"] = {name: checked_record(phase / name / "record.json", source)
|
||||
for name in ("lint", "rtl-verilator", "rtl-iverilog", "formal", "synth", "netsim", "sta")}
|
||||
if source != inputs():
|
||||
raise ValueError("source changed during execution")
|
||||
record["status"] = "passed"
|
||||
except (OSError, ValueError, RuntimeError, subprocess.SubprocessError) as error:
|
||||
record.update(status="failed", error=str(error))
|
||||
print(error, file=sys.stderr)
|
||||
return 1
|
||||
finally:
|
||||
record["artifacts"] = {str(path): digest(path) for path in directory.rglob("*")
|
||||
if path.is_file() and path.name != "record.json"}
|
||||
dump(directory / "record.json", record)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,133 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate one-tick P1 differential records for all 65536 PIO-lite instruction words."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
from piolite_isa import Instruction, PinWindow # noqa: E402
|
||||
from piolite_model import Machine, SMConfig # noqa: E402
|
||||
|
||||
RECORD_WORDS = 23
|
||||
LEGAL_COUNT = 22257
|
||||
REJECTED_COUNT = 43279
|
||||
TX_WORD = 0xD31A5A69
|
||||
|
||||
|
||||
def config() -> SMConfig:
|
||||
return SMConfig(
|
||||
base=0,
|
||||
length=1,
|
||||
entry=0,
|
||||
wrap_bottom=0,
|
||||
wrap_top=0,
|
||||
divider=1,
|
||||
autopull=True,
|
||||
autopush=True,
|
||||
tx_right=True,
|
||||
rx_right=True,
|
||||
tx_bits=32,
|
||||
rx_bits=8,
|
||||
in_window=PinWindow(0, 32),
|
||||
out_window=PinWindow(0, 32),
|
||||
set_window=PinWindow(0, 8),
|
||||
claim_mask=0xFFFFFFFF,
|
||||
output_mask=0xFFFFFFFF,
|
||||
initial_value=0,
|
||||
initial_oe=0,
|
||||
initial_x=1,
|
||||
initial_y=0,
|
||||
test_pin_enable=True,
|
||||
test_pin=0,
|
||||
test_flag=0,
|
||||
)
|
||||
|
||||
|
||||
def record(word: int) -> tuple[list[int], bool]:
|
||||
try:
|
||||
Instruction.decode(word)
|
||||
legal = True
|
||||
except ValueError:
|
||||
legal = False
|
||||
machine = Machine([config(), SMConfig(0, 1, 0, 0, 0)])
|
||||
machine.imem[0] = word
|
||||
machine.imem_valid = 1
|
||||
machine.flags = 1
|
||||
machine.pin_inputs = 1
|
||||
machine.write_tx(0, TX_WORD)
|
||||
machine.start(1)
|
||||
set_mask, clear_mask, _ = machine._execution_tick(0, machine.flags, machine.pin_inputs)
|
||||
sm = machine.sms[0]
|
||||
sm.tick_count = 1
|
||||
machine.flags = (machine.flags & ~clear_mask) | set_mask
|
||||
values = [
|
||||
word,
|
||||
int(legal),
|
||||
(int(sm.last_fifo_failed) << 8) | (int(sm.stall) << 4) | int(sm.state),
|
||||
(sm.fault_pc << 8) | int(sm.fault_code),
|
||||
sm.pc,
|
||||
sm.isr,
|
||||
sm.osr,
|
||||
(sm.y << 16) | sm.x,
|
||||
(sm.osr_count << 8) | sm.isr_count,
|
||||
sm.tick_count,
|
||||
sm.tx_stall_count,
|
||||
sm.rx_stall_count,
|
||||
sm.retired_count,
|
||||
(sm.delay_remaining << 16) | sm.divider_remaining,
|
||||
sm.events,
|
||||
sm.output_value,
|
||||
sm.output_oe,
|
||||
machine.flags,
|
||||
set_mask,
|
||||
clear_mask,
|
||||
int(not sm.tx.items),
|
||||
len(sm.rx.items),
|
||||
sm.rx.items[0] if sm.rx.items else 0,
|
||||
]
|
||||
assert len(values) == RECORD_WORDS
|
||||
return values, legal
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("output", type=Path)
|
||||
args = parser.parse_args()
|
||||
args.output.mkdir(parents=True, exist_ok=True)
|
||||
legal_count = 0
|
||||
digest = hashlib.sha256()
|
||||
with (args.output / "exhaustive.hex").open("w", encoding="ascii", newline="\n") as stream:
|
||||
for word in range(65536):
|
||||
values, legal = record(word)
|
||||
legal_count += int(legal)
|
||||
text = "".join(f"{value & 0xFFFFFFFF:08x}\n" for value in values)
|
||||
stream.write(text)
|
||||
digest.update(text.encode("ascii"))
|
||||
rejected_count = 65536 - legal_count
|
||||
if (legal_count, rejected_count) != (LEGAL_COUNT, REJECTED_COUNT):
|
||||
raise ValueError(f"unexpected ISA partition: {legal_count}/{rejected_count}")
|
||||
metadata = {
|
||||
"total": 65536,
|
||||
"legal": legal_count,
|
||||
"rejected": rejected_count,
|
||||
"record_words": RECORD_WORDS,
|
||||
"records": 65536,
|
||||
"sha256": digest.hexdigest(),
|
||||
"context": {
|
||||
"autopull": True, "autopush": True, "tx_bits": 32, "rx_bits": 8,
|
||||
"tx_word": TX_WORD, "initial_x": 1, "initial_y": 0, "flags": 1, "pins": 1,
|
||||
},
|
||||
}
|
||||
(args.output / "exhaustive.json").write_text(json.dumps(metadata, indent=2) + "\n")
|
||||
print(f"PIOLITE_P2_EXHAUSTIVE_VECTORS legal={legal_count} rejected={rejected_count} total=65536")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,300 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate fixed-seed P1-model differential records for standalone PIO-lite P2 RTL."""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0, str(ROOT / "scripts"))
|
||||
|
||||
from piolite_model import Machine, SMConfig # noqa: E402
|
||||
from piolite_isa import PinWindow # noqa: E402
|
||||
|
||||
SEED = 0x50494F32
|
||||
RECORD_WORDS = 78
|
||||
|
||||
|
||||
def instruction(opcode: int, operand: int = 0, delay: int = 0) -> int:
|
||||
return (opcode << 12) | (delay << 8) | operand
|
||||
|
||||
|
||||
def pack_window(window: PinWindow) -> int:
|
||||
return (window.width << 8) | window.base
|
||||
|
||||
|
||||
def generate() -> tuple[list[int], list[dict]]:
|
||||
records: list[int] = []
|
||||
metadata: list[dict] = []
|
||||
|
||||
def add(
|
||||
name: str,
|
||||
words: list[int],
|
||||
steps: int,
|
||||
*,
|
||||
divider: int = 1,
|
||||
wrap_bottom: int = 0,
|
||||
wrap_top: int | None = None,
|
||||
autopull: bool = False,
|
||||
autopush: bool = False,
|
||||
tx_right: bool = True,
|
||||
rx_right: bool = True,
|
||||
tx_bits: int = 32,
|
||||
rx_bits: int = 32,
|
||||
in_window: PinWindow = PinWindow(0, 0),
|
||||
out_window: PinWindow = PinWindow(0, 0),
|
||||
set_window: PinWindow = PinWindow(0, 0),
|
||||
claim_mask: int = 0,
|
||||
output_mask: int = 0,
|
||||
initial_value: int = 0,
|
||||
initial_oe: int = 0,
|
||||
initial_x: int = 0,
|
||||
initial_y: int = 0,
|
||||
tx_words: list[int] | None = None,
|
||||
pin_inputs: int = 0,
|
||||
image_count: int | None = None,
|
||||
) -> None:
|
||||
cfg = SMConfig(
|
||||
base=0,
|
||||
length=len(words),
|
||||
entry=0,
|
||||
wrap_bottom=wrap_bottom,
|
||||
wrap_top=len(words) - 1 if wrap_top is None else wrap_top,
|
||||
divider=divider,
|
||||
autopull=autopull,
|
||||
autopush=autopush,
|
||||
tx_right=tx_right,
|
||||
rx_right=rx_right,
|
||||
tx_bits=tx_bits,
|
||||
rx_bits=rx_bits,
|
||||
in_window=in_window,
|
||||
out_window=out_window,
|
||||
set_window=set_window,
|
||||
claim_mask=claim_mask,
|
||||
output_mask=output_mask,
|
||||
initial_value=initial_value,
|
||||
initial_oe=initial_oe,
|
||||
initial_x=initial_x,
|
||||
initial_y=initial_y,
|
||||
)
|
||||
machine = Machine([cfg, SMConfig(0, 1, 0, 0, 0)])
|
||||
for index, word in enumerate(words):
|
||||
machine.imem[index] = word
|
||||
machine.imem_valid |= 1 << index
|
||||
for word in tx_words or []:
|
||||
machine.write_tx(0, word)
|
||||
machine.pin_inputs = pin_inputs
|
||||
machine.start(1)
|
||||
if image_count is not None and image_count < len(words):
|
||||
machine.imem_valid = (1 << image_count) - 1
|
||||
for _ in range(steps):
|
||||
machine.step()
|
||||
sm = machine.sms[0]
|
||||
options = (
|
||||
int(autopull)
|
||||
| (int(autopush) << 1)
|
||||
| (int(tx_right) << 2)
|
||||
| (int(rx_right) << 3)
|
||||
)
|
||||
rx_words = list(sm.rx.items) + [0] * (8 - len(sm.rx.items))
|
||||
record = [
|
||||
divider,
|
||||
(len(words) << 8),
|
||||
(wrap_top << 8) | wrap_bottom if wrap_top is not None else ((len(words) - 1) << 8),
|
||||
options,
|
||||
pack_window(in_window),
|
||||
pack_window(out_window),
|
||||
pack_window(set_window),
|
||||
claim_mask,
|
||||
output_mask,
|
||||
initial_value,
|
||||
initial_oe,
|
||||
(initial_y << 16) | initial_x,
|
||||
(rx_bits << 8) | tx_bits,
|
||||
0,
|
||||
(tx_words or [0])[0],
|
||||
(tx_words or [0, 0])[1] if len(tx_words or []) > 1 else 0,
|
||||
len(tx_words or []),
|
||||
pin_inputs,
|
||||
steps,
|
||||
(int(sm.last_fifo_failed) << 8) | (int(sm.stall) << 4) | int(sm.state),
|
||||
(sm.fault_pc << 8) | int(sm.fault_code),
|
||||
sm.pc,
|
||||
sm.isr,
|
||||
sm.osr,
|
||||
(sm.y << 16) | sm.x,
|
||||
(sm.osr_count << 8) | sm.isr_count,
|
||||
sm.tick_count,
|
||||
sm.tx_stall_count,
|
||||
sm.rx_stall_count,
|
||||
sm.retired_count,
|
||||
(sm.delay_remaining << 16) | sm.divider_remaining,
|
||||
len(sm.tx.items),
|
||||
len(sm.rx.items),
|
||||
sm.events,
|
||||
machine.flags,
|
||||
sm.output_value,
|
||||
sm.output_oe,
|
||||
len(words) if image_count is None else image_count,
|
||||
]
|
||||
assert len(record) == 37 + 1
|
||||
record += words + [0] * (32 - len(words)) + rx_words
|
||||
assert len(record) == RECORD_WORDS
|
||||
records.extend(record)
|
||||
metadata.append(
|
||||
{
|
||||
"name": name,
|
||||
"index": len(metadata),
|
||||
"steps": steps,
|
||||
"expected": {
|
||||
"status": record[19],
|
||||
"fault": record[20],
|
||||
"pc": record[21],
|
||||
"isr": record[22],
|
||||
"osr": record[23],
|
||||
"xy": record[24],
|
||||
"tick_count": record[26],
|
||||
"retired_count": record[29],
|
||||
"rx_words": list(sm.rx.items),
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
halt = instruction(0, 0x20)
|
||||
add("nop_halt", [instruction(0), halt], 2)
|
||||
add("delay_15", [instruction(0, delay=15), halt], 17)
|
||||
add(
|
||||
"counters_and_move",
|
||||
[instruction(3, 127), instruction(3, 0x85), instruction(6, 0x10), instruction(6, 0x48), halt],
|
||||
5,
|
||||
)
|
||||
add(
|
||||
"pull_out_right",
|
||||
[instruction(7, 0x40), instruction(5, 4), halt],
|
||||
3,
|
||||
out_window=PinWindow(4, 4),
|
||||
claim_mask=0xF0,
|
||||
output_mask=0xF0,
|
||||
tx_words=[0x12345678],
|
||||
)
|
||||
add(
|
||||
"in_right_autopush",
|
||||
[instruction(4, 8), instruction(4, 0x28), instruction(4, 0x68), instruction(4, 0x48), halt],
|
||||
5,
|
||||
autopush=True,
|
||||
rx_bits=8,
|
||||
in_window=PinWindow(0, 8),
|
||||
claim_mask=0xFF,
|
||||
initial_x=0xA5,
|
||||
initial_y=0x5A,
|
||||
pin_inputs=0x3C,
|
||||
)
|
||||
add(
|
||||
"decrement_loop",
|
||||
[instruction(3, 2), instruction(0), instruction(9, 0x61), halt],
|
||||
6,
|
||||
)
|
||||
add(
|
||||
"nonblocking_fifo_failure",
|
||||
[instruction(7, 0), instruction(9, 0xC3), instruction(0), halt],
|
||||
3,
|
||||
)
|
||||
add("reserved_opcode", [0xB000], 1)
|
||||
add("taken_branch_then_halt", [instruction(9, 1), halt], 2, wrap_top=0)
|
||||
add("false_branch_target_bounds", [instruction(9, 0x22), halt], 1, initial_x=1)
|
||||
add("empty_manual_out", [instruction(5, 0x80)], 1)
|
||||
add(
|
||||
"partial_push_right",
|
||||
[instruction(4, 0x28), instruction(7, 0x80), halt],
|
||||
3,
|
||||
initial_x=0xAB,
|
||||
)
|
||||
add("move_ones_zero", [instruction(6, 0xA8), instruction(6, 0xE0), halt], 3)
|
||||
add("div_3_delay", [instruction(0, delay=1), halt], 9, divider=3)
|
||||
add("div_65535", [halt], 65535, divider=65535)
|
||||
add(
|
||||
"left_pack_tail",
|
||||
[instruction(4, 0x28), instruction(4, 0x48), instruction(7, 0x80), halt],
|
||||
4,
|
||||
rx_right=False,
|
||||
initial_x=0x12,
|
||||
initial_y=0x34,
|
||||
)
|
||||
add(
|
||||
"left_out_chunks",
|
||||
[instruction(7, 0x40), instruction(5, 0x48), instruction(5, 0x68), halt],
|
||||
4,
|
||||
tx_right=False,
|
||||
tx_bits=16,
|
||||
tx_words=[0x1234],
|
||||
)
|
||||
add(
|
||||
"zero_is_valid_fifo_data",
|
||||
[instruction(4, 0x68), instruction(7, 0x80), halt],
|
||||
3,
|
||||
)
|
||||
add("decrement_zero_saturates", [instruction(3, 0), instruction(9, 0x62), halt], 3)
|
||||
add("invalid_fetch", [instruction(0), halt], 2, image_count=1)
|
||||
add("reserved_fifo_bits", [instruction(7, 1)], 1)
|
||||
add(
|
||||
"partial_osr_underrun_atomic",
|
||||
[instruction(7, 0x40), instruction(5, 0x50), halt],
|
||||
2,
|
||||
tx_bits=8,
|
||||
tx_words=[0xA5],
|
||||
)
|
||||
add(
|
||||
"autopull_impossible_width_no_dequeue",
|
||||
[instruction(5, 0x50)],
|
||||
1,
|
||||
autopull=True,
|
||||
tx_bits=8,
|
||||
tx_words=[0x5A],
|
||||
)
|
||||
for name, count, value in (("right_1", 1, 1), ("right_16", 16, 0xBEEF),
|
||||
("right_31", 31, 0x7FFF)):
|
||||
add(
|
||||
f"pack_{name}",
|
||||
[instruction(4, 0x20 | (count & 0x1F)), instruction(7, 0x80), halt],
|
||||
3,
|
||||
initial_x=value,
|
||||
)
|
||||
add(
|
||||
"pack_left_31",
|
||||
[instruction(4, 0x3F), instruction(7, 0x80), halt],
|
||||
3,
|
||||
rx_right=False,
|
||||
initial_x=0x7FFF,
|
||||
)
|
||||
add(
|
||||
"out_discard_count_32",
|
||||
[instruction(7, 0x40), instruction(5, 0x80), halt],
|
||||
3,
|
||||
tx_words=[0xDEADBEEF],
|
||||
)
|
||||
add(
|
||||
"in_zero_count_32",
|
||||
[instruction(4, 0x60), instruction(7, 0x80), halt],
|
||||
3,
|
||||
)
|
||||
return [len(metadata), *records], metadata
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("output", type=Path)
|
||||
args = parser.parse_args()
|
||||
words, metadata = generate()
|
||||
args.output.mkdir(parents=True, exist_ok=True)
|
||||
(args.output / "vectors.hex").write_text("".join(f"{word & 0xFFFFFFFF:08x}\n" for word in words))
|
||||
(args.output / "vectors.json").write_text(
|
||||
json.dumps({"seed": SEED, "record_words": RECORD_WORDS, "cases": metadata}, indent=2) + "\n"
|
||||
)
|
||||
print(f"PIOLITE_P2_VECTORS cases={len(metadata)} seed=0x{SEED:08X}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -11,6 +11,23 @@ tooling.
|
||||
UART sampling, pulse-phase, and logic-capture cadence/count behavior. These
|
||||
are P1 host-model checks, not RTL, protocol-waveform, firmware, or
|
||||
operating-rate qualification.
|
||||
- `test_piolite_register_parity.py` checks exact handwritten SVH/C dictionaries,
|
||||
the 119-entry APB register matrix, resets, fields, and ISA enums. Standalone
|
||||
`piolite_exhaustive_tb.sv` executes all 65536 instruction words in the real SM
|
||||
with 22257 legal and 43279 rejected one-tick differential outcomes. The
|
||||
`piolite_{isa,core,fifo,apb,lifecycle,netlist}_tb.sv` fixtures add 29 directed
|
||||
programs and independent pin/stream/FIFO/APB oracles on Verilator and Icarus.
|
||||
The PL-V checklist covers CSR/access/error matrices, randomized pre-edge FIFO
|
||||
activity, command/commit and first-fault races, saturation, and paused-DMA
|
||||
progress. PAUSE candidates use a two-word region and a delay-1 SET_COUNT to
|
||||
prove PC/delay retention, and cover IN/OUT/SET_COUNT/FLAG/HALT side effects;
|
||||
live configuration/IMEM writes are rejected atomically in RUNNING, CLOSING,
|
||||
and transport-active states. Formal runs preserve FIFO induction and add
|
||||
full-top BMC/cover witnesses, exact assertion identities, and negative
|
||||
deleted-assertion/missing-witness self-checks.
|
||||
`piolite-p2-{lint,rtl,formal,synth,netsim,sta,report}` retains
|
||||
source/tool/artifact-bound standalone evidence, not Tiny integration or a
|
||||
qualified product operating point.
|
||||
- `test_npu_framework_reference.py` checks model-specific Softmax scaling,
|
||||
all 65536 VWW two-class input combinations, seeded KWS vectors, unchanged
|
||||
hardware artifacts and complete sample-model outputs against the locked
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
`timescale 1ns / 1ps
|
||||
module piolite_apb_tb;
|
||||
localparam integer TestKind = 3;
|
||||
localparam TestName = "piolite_apb_tb";
|
||||
`include "piolite_test_body.svh"
|
||||
endmodule
|
||||
@@ -0,0 +1,6 @@
|
||||
`timescale 1ns / 1ps
|
||||
module piolite_core_tb;
|
||||
localparam integer TestKind = 1;
|
||||
localparam TestName = "piolite_core_tb";
|
||||
`include "piolite_test_body.svh"
|
||||
endmodule
|
||||
@@ -0,0 +1,228 @@
|
||||
`timescale 1ns / 1ps
|
||||
module piolite_exhaustive_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
piolite_pkg::sm_config_t cfg;
|
||||
logic start_i = 1'b0;
|
||||
logic [ 5:0] event_clear_i = 6'd0;
|
||||
logic guard_fault_i = 1'b0;
|
||||
logic [ 7:0] guard_fault_code_i = 8'd0;
|
||||
logic stream_fault_i = 1'b0;
|
||||
logic [ 15:0] instruction_i = 16'd0;
|
||||
logic instruction_valid_i = 1'b1;
|
||||
logic [ 4:0] instruction_addr_o;
|
||||
logic [ 31:0] flags_i = 32'd1;
|
||||
logic [ 31:0] pin_inputs_i = 32'd1;
|
||||
logic [ 3:0] flag_set_o;
|
||||
logic [ 3:0] flag_clear_o;
|
||||
logic tx_empty_i = 1'b0;
|
||||
logic [ 31:0] tx_data_i = 32'hd31a5a69;
|
||||
logic tx_pop_o;
|
||||
logic rx_full_i = 1'b0;
|
||||
logic rx_push_o;
|
||||
logic [ 31:0] rx_data_o;
|
||||
piolite_pkg::sm_runtime_t runtime;
|
||||
logic [ 31:0] vector_mem [0:1507327];
|
||||
logic [ 3:0] observed_set;
|
||||
logic [ 3:0] observed_clear;
|
||||
logic observed_tx_pop;
|
||||
logic observed_rx_push;
|
||||
logic counter_load = 1'b0;
|
||||
logic [ 31:0] observed_rx_data;
|
||||
integer legal_count = 0;
|
||||
integer rejected_count = 0;
|
||||
reg [4095:0] vector_path;
|
||||
always #1 clk_i = ~clk_i;
|
||||
|
||||
piolite_sm u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.cfg_i (cfg),
|
||||
.start_i (start_i),
|
||||
.pause_i (1'b0),
|
||||
.resume_i (1'b0),
|
||||
.disable_i (1'b0),
|
||||
.abort_i (1'b0),
|
||||
.sm_reset_i (1'b0),
|
||||
.block_reset_i (1'b0),
|
||||
`ifdef PIOLITE_EXHAUSTIVE_DV
|
||||
.counter_load_i (counter_load),
|
||||
`endif
|
||||
.event_clear_i (event_clear_i),
|
||||
.guard_fault_i (guard_fault_i),
|
||||
.guard_fault_code_i (guard_fault_code_i),
|
||||
.stream_fault_i (stream_fault_i),
|
||||
.instruction_i (instruction_i),
|
||||
.instruction_valid_i(instruction_valid_i),
|
||||
.instruction_addr_o (instruction_addr_o),
|
||||
.flags_i (flags_i),
|
||||
.pin_inputs_i (pin_inputs_i),
|
||||
.flag_set_o (flag_set_o),
|
||||
.flag_clear_o (flag_clear_o),
|
||||
.tx_empty_i (tx_empty_i),
|
||||
.tx_data_i (tx_data_i),
|
||||
.tx_pop_o (tx_pop_o),
|
||||
.rx_full_i (rx_full_i),
|
||||
.rx_push_o (rx_push_o),
|
||||
.rx_data_o (rx_data_o),
|
||||
.runtime_o (runtime)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
observed_set = flag_set_o;
|
||||
observed_clear = flag_clear_o;
|
||||
observed_tx_pop = tx_pop_o;
|
||||
observed_rx_push = rx_push_o;
|
||||
observed_rx_data = rx_data_o;
|
||||
end
|
||||
|
||||
task automatic check_field(input integer word, input integer field, input logic [31:0] actual,
|
||||
input logic [31:0] expected);
|
||||
if (actual !== expected)
|
||||
$fatal(
|
||||
1,
|
||||
"PIOLITE exhaustive word=%h field=%0d got=%h expected=%h",
|
||||
word,
|
||||
field,
|
||||
actual,
|
||||
expected
|
||||
);
|
||||
endtask
|
||||
|
||||
task automatic run_word(input integer word);
|
||||
integer base;
|
||||
base = word * 23;
|
||||
instruction_i = vector_mem[base][15:0];
|
||||
rst_n_i = 1'b0;
|
||||
start_i = 1'b0;
|
||||
repeat (2) @(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
@(negedge clk_i);
|
||||
check_field(word, 2, {23'd0, runtime.last_fifo_failed, 1'b0, runtime.stall, 1'b0, runtime.state
|
||||
}, vector_mem[base+2]);
|
||||
check_field(word, 3, {19'd0, runtime.fault_pc, runtime.fault_code}, vector_mem[base+3]);
|
||||
check_field(word, 4, {27'd0, runtime.pc}, vector_mem[base+4]);
|
||||
check_field(word, 5, runtime.isr, vector_mem[base+5]);
|
||||
check_field(word, 6, runtime.osr, vector_mem[base+6]);
|
||||
check_field(word, 7, {runtime.y, runtime.x}, vector_mem[base+7]);
|
||||
check_field(word, 8, {18'd0, runtime.osr_count, 2'd0, runtime.isr_count}, vector_mem[base+8]);
|
||||
check_field(word, 9, runtime.tick_count, vector_mem[base+9]);
|
||||
check_field(word, 10, runtime.tx_stall_count, vector_mem[base+10]);
|
||||
check_field(word, 11, runtime.rx_stall_count, vector_mem[base+11]);
|
||||
check_field(word, 12, runtime.retired_count, vector_mem[base+12]);
|
||||
check_field(word, 13, {12'd0, runtime.delay_remaining, runtime.divider_remaining},
|
||||
vector_mem[base+13]);
|
||||
check_field(word, 14, {26'd0, runtime.events}, vector_mem[base+14]);
|
||||
check_field(word, 15, runtime.output_value, vector_mem[base+15]);
|
||||
check_field(word, 16, runtime.output_oe, vector_mem[base+16]);
|
||||
check_field(word, 17, (flags_i & ~{28'd0, observed_clear}) | {28'd0, observed_set},
|
||||
vector_mem[base+17]);
|
||||
check_field(word, 18, {28'd0, observed_set}, vector_mem[base+18]);
|
||||
check_field(word, 19, {28'd0, observed_clear}, vector_mem[base+19]);
|
||||
check_field(word, 20, {31'd0, observed_tx_pop}, vector_mem[base+20]);
|
||||
check_field(word, 21, {31'd0, observed_rx_push}, vector_mem[base+21]);
|
||||
check_field(word, 22, observed_rx_data, vector_mem[base+22]);
|
||||
if (vector_mem[base+1] == 32'd1) legal_count++;
|
||||
else if (vector_mem[base+1] == 32'd0) rejected_count++;
|
||||
else $fatal(1, "PIOLITE exhaustive legality class is invalid");
|
||||
if ((word & 4095) == 4095) $display("PIOLITE_P2_EXHAUSTIVE_PROGRESS words=%0d", word + 1);
|
||||
endtask
|
||||
|
||||
task automatic raw_start(input logic [15:0] word);
|
||||
instruction_i = word;
|
||||
rst_n_i = 1'b0;
|
||||
start_i = 1'b0;
|
||||
repeat (2) @(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
start_i = 1'b0;
|
||||
endtask
|
||||
|
||||
task automatic first_fault_priority;
|
||||
raw_start(16'hb000);
|
||||
guard_fault_i = 1'b1;
|
||||
guard_fault_code_i = 8'd6;
|
||||
stream_fault_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
if ((runtime.fault_code != 8'd6) || (runtime.state != piolite_pkg::Closing))
|
||||
$fatal(1, "PIOLITE guard/stream/execution first-fault priority");
|
||||
guard_fault_code_i = 8'd7;
|
||||
@(negedge clk_i);
|
||||
if ((runtime.fault_code != 8'd6) || (runtime.fault_pc != 5'd0))
|
||||
$fatal(1, "PIOLITE first fault was overwritten");
|
||||
guard_fault_i = 1'b0;
|
||||
stream_fault_i = 1'b0;
|
||||
$display("PIOLITE_P2_COVER PL-V08 first-fault-priority-and-retention");
|
||||
endtask
|
||||
|
||||
task automatic saturating_counters;
|
||||
raw_start(16'h0000);
|
||||
counter_load = 1'b1;
|
||||
@(negedge clk_i);
|
||||
counter_load = 1'b0;
|
||||
if ((runtime.tick_count != 32'hffff_ffff) ||
|
||||
(runtime.retired_count != 32'hffff_ffff) ||
|
||||
(runtime.tx_stall_count != 32'hffff_ffff) ||
|
||||
(runtime.rx_stall_count != 32'hffff_ffff))
|
||||
$fatal(1, "PIOLITE commit counters wrapped");
|
||||
instruction_i = 16'h7040;
|
||||
tx_empty_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
if (runtime.tx_stall_count != 32'hffff_ffff) $fatal(1, "PIOLITE stall counter wrapped");
|
||||
tx_empty_i = 1'b0;
|
||||
$display("PIOLITE_P2_COVER PL-V08 saturating-tick-stall-retired-counters");
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
cfg = '0;
|
||||
cfg.options = 5'h1f;
|
||||
cfg.divider = 16'd1;
|
||||
cfg.base = 5'd0;
|
||||
cfg.length = 6'd1;
|
||||
cfg.entry = 5'd0;
|
||||
cfg.wrap_bottom = 5'd0;
|
||||
cfg.wrap_top = 5'd0;
|
||||
cfg.in_pins.base = 5'd0;
|
||||
cfg.in_pins.width = 6'd32;
|
||||
cfg.out_pins.base = 5'd0;
|
||||
cfg.out_pins.width = 6'd32;
|
||||
cfg.set_pins.base = 5'd0;
|
||||
cfg.set_pins.width = 6'd8;
|
||||
cfg.claim_mask = 32'hffff_ffff;
|
||||
cfg.output_mask = 32'hffff_ffff;
|
||||
cfg.initial_value = 32'd0;
|
||||
cfg.initial_oe = 32'd0;
|
||||
cfg.initial_x = 16'd1;
|
||||
cfg.initial_y = 16'd0;
|
||||
cfg.tx_bits = 6'd32;
|
||||
cfg.rx_bits = 6'd8;
|
||||
cfg.test_pin = 5'd0;
|
||||
cfg.test_flag = 2'd0;
|
||||
cfg.tx_low = 4'd0;
|
||||
cfg.rx_high = 4'd1;
|
||||
if (!$value$plusargs("PIOLITE_EXHAUSTIVE=%s", vector_path))
|
||||
$fatal(1, "PIOLITE exhaustive vector path is required");
|
||||
$readmemh(vector_path, vector_mem);
|
||||
for (integer word = 0; word < 65536; word++) run_word(word);
|
||||
if ((legal_count != 22257) || (rejected_count != 43279))
|
||||
$fatal(1, "PIOLITE exhaustive counts legal=%0d rejected=%0d", legal_count, rejected_count);
|
||||
$display("PIOLITE_P2_EXHAUSTIVE legal=%0d rejected=%0d total=65536", legal_count,
|
||||
rejected_count);
|
||||
first_fault_priority();
|
||||
saturating_counters();
|
||||
$display("PIOLITE_P2_COVER PL-V01 exhaustive-legal-and-reserved-semantics");
|
||||
$display("PIOLITE_P2_PASS piolite_exhaustive_tb");
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
#10000000;
|
||||
$fatal(1, "SIM_TEST_TIMEOUT PIOLITE exhaustive");
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,133 @@
|
||||
`timescale 1ns / 1ps
|
||||
module piolite_fifo_tb;
|
||||
logic clk_i = 1'b0;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic flush_i = 1'b0;
|
||||
logic push_i = 1'b0;
|
||||
logic [31:0] push_data_i = 32'd0;
|
||||
logic pop_i = 1'b0;
|
||||
logic [31:0] pop_data_o;
|
||||
logic push_accept_o;
|
||||
logic pop_accept_o;
|
||||
logic full_o;
|
||||
logic empty_o;
|
||||
logic [ 3:0] count_o;
|
||||
logic [31:0] ref_items [0:7];
|
||||
logic [31:0] random_state;
|
||||
integer ref_count;
|
||||
integer ref_read;
|
||||
integer ref_write;
|
||||
always #5 clk_i = ~clk_i;
|
||||
|
||||
piolite_fifo u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.flush_i (flush_i),
|
||||
.push_i (push_i),
|
||||
.push_data_i (push_data_i),
|
||||
.pop_i (pop_i),
|
||||
.pop_data_o (pop_data_o),
|
||||
.push_accept_o(push_accept_o),
|
||||
.pop_accept_o (pop_accept_o),
|
||||
.full_o (full_o),
|
||||
.empty_o (empty_o),
|
||||
.count_o (count_o)
|
||||
);
|
||||
|
||||
task automatic drive(input logic push, input logic pop, input logic [31:0] word);
|
||||
@(negedge clk_i);
|
||||
push_i = push;
|
||||
pop_i = pop;
|
||||
push_data_i = word;
|
||||
#1;
|
||||
if (push_accept_o !== (push && !full_o)) $fatal(1, "PIOLITE FIFO push admission");
|
||||
if (pop_accept_o !== (pop && !empty_o)) $fatal(1, "PIOLITE FIFO pop admission");
|
||||
@(negedge clk_i);
|
||||
push_i = 1'b0;
|
||||
pop_i = 1'b0;
|
||||
endtask
|
||||
|
||||
task automatic expect_count(input logic [3:0] expected);
|
||||
if (count_o !== expected)
|
||||
$fatal(1, "PIOLITE FIFO count got=%0d expected=%0d", count_o, expected);
|
||||
if ((count_o > 8) || (full_o !== (count_o == 8)) || (empty_o !== (count_o == 0)))
|
||||
$fatal(1, "PIOLITE FIFO bounds flags");
|
||||
endtask
|
||||
|
||||
task automatic random_fifo;
|
||||
logic push;
|
||||
logic pop;
|
||||
logic take_push;
|
||||
logic take_pop;
|
||||
ref_count = 0;
|
||||
ref_read = 0;
|
||||
ref_write = 0;
|
||||
random_state = 32'h50494f36;
|
||||
for (integer iteration = 0; iteration < 512; iteration++) begin
|
||||
random_state = (random_state >> 1) ^ (random_state[0] ? 32'h8020_0003 : 32'd0);
|
||||
push = random_state[0];
|
||||
pop = random_state[1];
|
||||
take_push = push && (ref_count < 8);
|
||||
take_pop = pop && (ref_count > 0);
|
||||
if (ref_count > 0 && pop_data_o !== ref_items[ref_read])
|
||||
$fatal(1, "PIOLITE random FIFO head mismatch iteration=%0d", iteration);
|
||||
drive(push, pop, random_state);
|
||||
if (take_pop) ref_read = (ref_read + 1) & 7;
|
||||
if (take_push) begin
|
||||
ref_items[ref_write] = random_state;
|
||||
ref_write = (ref_write + 1) & 7;
|
||||
end
|
||||
case ({
|
||||
take_push, take_pop
|
||||
})
|
||||
2'b10: ref_count++;
|
||||
2'b01: ref_count--;
|
||||
default: ;
|
||||
endcase
|
||||
expect_count(ref_count[3:0]);
|
||||
if (ref_count > 0 && pop_data_o !== ref_items[ref_read])
|
||||
$fatal(1, "PIOLITE random FIFO post-edge ordering iteration=%0d", iteration);
|
||||
end
|
||||
$display("PIOLITE_P2_COVER PL-V06 randomized-preedge-fifo-conservation");
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
repeat (3) @(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
repeat (2) @(negedge clk_i);
|
||||
expect_count(0);
|
||||
drive(1'b1, 1'b1, 32'd0);
|
||||
expect_count(1);
|
||||
drive(1'b0, 1'b1, 32'd0);
|
||||
expect_count(0);
|
||||
for (integer index = 0; index < 8; index++) drive(1'b1, 1'b0, 32'(index + 1));
|
||||
expect_count(8);
|
||||
if (pop_data_o !== 1) $fatal(1, "PIOLITE FIFO oldest head");
|
||||
drive(1'b1, 1'b1, 32'd99);
|
||||
expect_count(7);
|
||||
for (integer index = 1; index < 8; index++) begin
|
||||
if (pop_data_o !== 32'(index + 1)) $fatal(1, "PIOLITE full pre-edge ordering");
|
||||
drive(1'b0, 1'b1, 32'd0);
|
||||
end
|
||||
expect_count(0);
|
||||
drive(1'b1, 1'b0, 32'd10);
|
||||
drive(1'b1, 1'b0, 32'd11);
|
||||
drive(1'b1, 1'b0, 32'd12);
|
||||
if (pop_data_o !== 10) $fatal(1, "PIOLITE FIFO intermediate head");
|
||||
drive(1'b1, 1'b1, 32'd13);
|
||||
expect_count(3);
|
||||
if (pop_data_o !== 11) $fatal(1, "PIOLITE simultaneous pop was not old head");
|
||||
flush_i = 1'b1;
|
||||
@(negedge clk_i);
|
||||
flush_i = 1'b0;
|
||||
expect_count(0);
|
||||
random_fifo();
|
||||
$display("PIOLITE_P2_PASS piolite_fifo_tb");
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
#100000;
|
||||
$fatal(1, "SIM_TEST_TIMEOUT PIOLITE FIFO");
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,138 @@
|
||||
module piolite_formal (
|
||||
input wire clk_i
|
||||
);
|
||||
reg rst_n = 1'b0;
|
||||
(* anyseq *)reg push;
|
||||
(* anyseq *)reg pop;
|
||||
(* anyseq *)reg [31:0] push_data;
|
||||
wire [31:0] pop_data;
|
||||
wire push_accept;
|
||||
wire pop_accept;
|
||||
wire full;
|
||||
wire empty;
|
||||
wire [ 3:0] count;
|
||||
always @(posedge clk_i) rst_n <= 1'b1;
|
||||
|
||||
`ifndef PIOLITE_FORMAL_TOP
|
||||
piolite_fifo u_fifo (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n),
|
||||
.flush_i (1'b0),
|
||||
.push_i (push),
|
||||
.push_data_i (push_data),
|
||||
.pop_i (pop),
|
||||
.pop_data_o (pop_data),
|
||||
.push_accept_o(push_accept),
|
||||
.pop_accept_o (pop_accept),
|
||||
.full_o (full),
|
||||
.empty_o (empty),
|
||||
.count_o (count)
|
||||
);
|
||||
piolite_formal_props u_props (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n),
|
||||
.push_i (push),
|
||||
.pop_i (pop),
|
||||
.push_accept_i(push_accept),
|
||||
.pop_accept_i (pop_accept),
|
||||
.full_i (full),
|
||||
.empty_i (empty),
|
||||
.count_i (count)
|
||||
);
|
||||
`endif
|
||||
`ifdef PIOLITE_FORMAL_TOP
|
||||
(* anyseq *)reg [31:0] gpio_input;
|
||||
(* anyseq *)reg guard_loss;
|
||||
(* anyseq *)reg dma_tx_valid;
|
||||
(* anyseq *)reg [31:0] dma_tx_data;
|
||||
(* anyseq *)reg [ 3:0] dma_tx_keep;
|
||||
(* anyseq *)reg [ 3:0] dma_tx_strb;
|
||||
(* anyseq *)reg dma_tx_id;
|
||||
(* anyseq *)reg dma_tx_dest;
|
||||
(* anyseq *)reg dma_tx_user;
|
||||
(* anyseq *)reg dma_tx_last;
|
||||
(* anyseq *)reg dma_rx_ready;
|
||||
(* anyseq *)reg [31:0] paddr;
|
||||
(* anyseq *)reg [31:0] pwdata;
|
||||
(* anyseq *)reg psel;
|
||||
(* anyseq *)reg penable;
|
||||
(* anyseq *)reg pwrite;
|
||||
wire [31:0] gpio_value;
|
||||
wire [31:0] gpio_oe;
|
||||
wire dma_tx_ready;
|
||||
wire dma_rx_valid;
|
||||
wire [31:0] dma_rx_data;
|
||||
wire [ 3:0] dma_rx_keep;
|
||||
wire [ 3:0] dma_rx_strb;
|
||||
wire dma_rx_last;
|
||||
wire dma_rx_id;
|
||||
wire dma_rx_dest;
|
||||
wire dma_rx_user;
|
||||
wire dma_tx_admit;
|
||||
wire dma_rx_admit;
|
||||
wire irq;
|
||||
wire pready;
|
||||
wire [31:0] prdata;
|
||||
wire pslverr;
|
||||
wire [43:0] formal_sm0;
|
||||
|
||||
apb4_piolite_block_top u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n),
|
||||
.clock_hz_i (32'd24000000),
|
||||
.user_select_i (guard_loss ? 32'd0 : 32'hffff_ffff),
|
||||
.user_status_i (guard_loss ? 32'd0 : 32'hffff_ffff),
|
||||
.filter_enable_i(32'd0),
|
||||
.gpio_ready_i (1'b1),
|
||||
.gpio_input_i (gpio_input),
|
||||
.gpio_value_o (gpio_value),
|
||||
.gpio_oe_o (gpio_oe),
|
||||
.dma_tx_job_i (1'b0),
|
||||
.dma_rx_job_i (1'b0),
|
||||
.dma_tx_valid_i (dma_tx_valid),
|
||||
.dma_tx_ready_o (dma_tx_ready),
|
||||
.dma_tx_data_i (dma_tx_data),
|
||||
.dma_tx_keep_i (dma_tx_keep),
|
||||
.dma_tx_strb_i (dma_tx_strb),
|
||||
.dma_tx_id_i (dma_tx_id),
|
||||
.dma_tx_dest_i (dma_tx_dest),
|
||||
.dma_tx_user_i (dma_tx_user),
|
||||
.dma_tx_last_i (dma_tx_last),
|
||||
.dma_rx_valid_o (dma_rx_valid),
|
||||
.dma_rx_ready_i (dma_rx_ready),
|
||||
.dma_rx_data_o (dma_rx_data),
|
||||
.dma_rx_keep_o (dma_rx_keep),
|
||||
.dma_rx_strb_o (dma_rx_strb),
|
||||
.dma_rx_last_o (dma_rx_last),
|
||||
.dma_rx_id_o (dma_rx_id),
|
||||
.dma_rx_dest_o (dma_rx_dest),
|
||||
.dma_rx_user_o (dma_rx_user),
|
||||
.dma_tx_admit_o (dma_tx_admit),
|
||||
.dma_rx_admit_o (dma_rx_admit),
|
||||
.irq_o (irq),
|
||||
.paddr_i (paddr),
|
||||
.pprot_i (3'd0),
|
||||
.psel_i (psel),
|
||||
.penable_i (penable),
|
||||
.pwrite_i (pwrite),
|
||||
.pwdata_i (pwdata),
|
||||
.pstrb_i (pwrite ? 4'hf : 4'd0),
|
||||
.pready_o (pready),
|
||||
.prdata_o (prdata),
|
||||
.pslverr_o (pslverr),
|
||||
.formal_sm0_o (formal_sm0)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
assume (paddr[31:12] == 20'd0 && paddr[11:2] <= 10'hff && paddr[1:0] == 2'd0);
|
||||
if (rst_n) begin
|
||||
cover (pready);
|
||||
cover (formal_sm0[43:41] == 3'd1);
|
||||
cover (formal_sm0[40]);
|
||||
cover (formal_sm0[43:41] == 3'd4);
|
||||
cover (formal_sm0[39:32] != 8'd0);
|
||||
cover (formal_sm0[31:0] != 32'd0);
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endmodule
|
||||
@@ -0,0 +1,43 @@
|
||||
module piolite_formal_props (
|
||||
input wire clk_i,
|
||||
input wire rst_n_i,
|
||||
input wire push_i,
|
||||
input wire pop_i,
|
||||
input wire push_accept_i,
|
||||
input wire pop_accept_i,
|
||||
input wire full_i,
|
||||
input wire empty_i,
|
||||
input wire [3:0] count_i
|
||||
);
|
||||
reg past_valid = 1'b0;
|
||||
always @(posedge clk_i) begin
|
||||
past_valid <= rst_n_i;
|
||||
if (rst_n_i) begin
|
||||
assert (count_i <= 8);
|
||||
assert (full_i == (count_i == 8));
|
||||
assert (empty_i == (count_i == 0));
|
||||
assert (push_accept_i == (push_i && !full_i));
|
||||
assert (pop_accept_i == (pop_i && !empty_i));
|
||||
if (past_valid) begin
|
||||
if ($past(full_i && push_i && pop_i && !empty_i)) begin
|
||||
assert (!$past(push_accept_i));
|
||||
assert ($past(pop_accept_i));
|
||||
end
|
||||
if ($past(empty_i && push_i && pop_i)) begin
|
||||
assert ($past(push_accept_i));
|
||||
assert (!$past(pop_accept_i));
|
||||
end
|
||||
case ({
|
||||
$past(push_accept_i), $past(pop_accept_i)
|
||||
})
|
||||
2'b10: assert (count_i == $past(count_i) + 1);
|
||||
2'b01: assert (count_i == $past(count_i) - 1);
|
||||
default: assert (count_i == $past(count_i));
|
||||
endcase
|
||||
end
|
||||
cover (full_i);
|
||||
cover (empty_i);
|
||||
cover (push_accept_i && pop_accept_i);
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,6 @@
|
||||
`timescale 1ns / 1ps
|
||||
module piolite_isa_tb;
|
||||
localparam integer TestKind = 0;
|
||||
localparam TestName = "piolite_isa_tb";
|
||||
`include "piolite_test_body.svh"
|
||||
endmodule
|
||||
@@ -0,0 +1,6 @@
|
||||
`timescale 1ns / 1ps
|
||||
module piolite_lifecycle_tb;
|
||||
localparam integer TestKind = 4;
|
||||
localparam TestName = "piolite_lifecycle_tb";
|
||||
`include "piolite_test_body.svh"
|
||||
endmodule
|
||||
@@ -0,0 +1,6 @@
|
||||
`timescale 1ns / 1ps
|
||||
module piolite_netlist_tb;
|
||||
localparam integer TestKind = 5;
|
||||
localparam TestName = "piolite_netlist_tb";
|
||||
`include "piolite_test_body.svh"
|
||||
endmodule
|
||||
@@ -0,0 +1,42 @@
|
||||
module piolite_pin_bfm (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
input logic [31:0] value_i,
|
||||
input logic [31:0] oe_i,
|
||||
output logic [31:0] cycle_count_o,
|
||||
output logic [31:0] transition_count_o
|
||||
);
|
||||
logic [31:0] s_value_q;
|
||||
logic [31:0] s_oe_q;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
cycle_count_o <= 32'd0;
|
||||
transition_count_o <= 32'd0;
|
||||
s_value_q <= 32'd0;
|
||||
s_oe_q <= 32'd0;
|
||||
end else begin
|
||||
cycle_count_o <= cycle_count_o + 32'd1;
|
||||
s_value_q <= value_i;
|
||||
s_oe_q <= oe_i;
|
||||
if ((value_i != s_value_q) || (oe_i != s_oe_q))
|
||||
transition_count_o <= transition_count_o + 32'd1;
|
||||
end
|
||||
end
|
||||
|
||||
task automatic expect_pins(input logic [31:0] expected_value, input logic [31:0] expected_oe);
|
||||
if ((value_i !== expected_value) || (oe_i !== expected_oe))
|
||||
$fatal(
|
||||
1,
|
||||
"PIOLITE pin value/OE got=%h/%h expected=%h/%h",
|
||||
value_i,
|
||||
oe_i,
|
||||
expected_value,
|
||||
expected_oe
|
||||
);
|
||||
endtask
|
||||
|
||||
task automatic expect_quiescent;
|
||||
if (oe_i !== 32'd0) $fatal(1, "PIOLITE output OE is not clamped: %h", oe_i);
|
||||
endtask
|
||||
endmodule
|
||||
@@ -0,0 +1,110 @@
|
||||
module piolite_stream_bfm (
|
||||
input logic clk_i,
|
||||
input logic rst_n_i,
|
||||
output logic tx_valid_o,
|
||||
input logic tx_ready_i,
|
||||
output logic [31:0] tx_data_o,
|
||||
output logic [ 3:0] tx_keep_o,
|
||||
output logic [ 3:0] tx_strb_o,
|
||||
output logic tx_id_o,
|
||||
output logic tx_dest_o,
|
||||
output logic tx_user_o,
|
||||
output logic tx_last_o,
|
||||
input logic rx_valid_i,
|
||||
output logic rx_ready_o,
|
||||
input logic [31:0] rx_data_i,
|
||||
input logic [ 3:0] rx_keep_i,
|
||||
input logic [ 3:0] rx_strb_i,
|
||||
input logic rx_last_i,
|
||||
input logic rx_id_i,
|
||||
input logic rx_dest_i,
|
||||
input logic rx_user_i,
|
||||
output logic [31:0] tx_accepted_count_o,
|
||||
output logic [31:0] rx_accepted_count_o
|
||||
);
|
||||
logic s_rx_wait_q;
|
||||
logic [31:0] s_rx_data_q;
|
||||
logic s_allow_rx;
|
||||
|
||||
assign rx_ready_o = s_allow_rx;
|
||||
|
||||
always_ff @(posedge clk_i or negedge rst_n_i) begin
|
||||
if (!rst_n_i) begin
|
||||
tx_accepted_count_o <= 32'd0;
|
||||
rx_accepted_count_o <= 32'd0;
|
||||
s_rx_wait_q <= 1'b0;
|
||||
s_rx_data_q <= 32'd0;
|
||||
end else begin
|
||||
if (tx_valid_o && tx_ready_i) tx_accepted_count_o <= tx_accepted_count_o + 32'd1;
|
||||
if (rx_valid_i && rx_ready_o) rx_accepted_count_o <= rx_accepted_count_o + 32'd1;
|
||||
if (s_rx_wait_q) begin
|
||||
if (!rx_valid_i || (rx_data_i !== s_rx_data_q))
|
||||
$fatal(
|
||||
1,
|
||||
"PIOLITE RX changed under backpressure valid=%b data=%h previous=%h",
|
||||
rx_valid_i,
|
||||
rx_data_i,
|
||||
s_rx_data_q
|
||||
);
|
||||
if ((rx_keep_i !== 4'hf) || (rx_strb_i !== 4'hf) || rx_last_i || rx_id_i ||
|
||||
rx_dest_i || rx_user_i)
|
||||
$fatal(1, "PIOLITE RX sidebands changed under backpressure");
|
||||
end
|
||||
s_rx_wait_q <= rx_valid_i && !rx_ready_o;
|
||||
s_rx_data_q <= rx_data_i;
|
||||
if (rx_valid_i && ((rx_keep_i !== 4'hf) || (rx_strb_i !== 4'hf) || rx_last_i ||
|
||||
rx_id_i || rx_dest_i || rx_user_i))
|
||||
$fatal(1, "PIOLITE RX constant sidebands are invalid");
|
||||
end
|
||||
end
|
||||
|
||||
task automatic reset_bfm;
|
||||
tx_valid_o = 1'b0;
|
||||
tx_data_o = 32'd0;
|
||||
tx_keep_o = 4'hf;
|
||||
tx_strb_o = 4'hf;
|
||||
tx_id_o = 1'b0;
|
||||
tx_dest_o = 1'b0;
|
||||
tx_user_o = 1'b0;
|
||||
tx_last_o = 1'b0;
|
||||
s_allow_rx = 1'b0;
|
||||
endtask
|
||||
|
||||
task automatic send_tx(input logic [31:0] word, input logic [3:0] keep, input logic [3:0] strb,
|
||||
input logic identity);
|
||||
@(negedge clk_i);
|
||||
tx_data_o = word;
|
||||
tx_keep_o = keep;
|
||||
tx_strb_o = strb;
|
||||
tx_id_o = identity;
|
||||
tx_dest_o = 1'b0;
|
||||
tx_user_o = 1'b0;
|
||||
tx_valid_o = 1'b1;
|
||||
do @(posedge clk_i); while (!tx_ready_i);
|
||||
@(negedge clk_i);
|
||||
tx_valid_o = 1'b0;
|
||||
tx_keep_o = 4'hf;
|
||||
tx_strb_o = 4'hf;
|
||||
tx_id_o = 1'b0;
|
||||
endtask
|
||||
|
||||
task automatic expect_rx(input logic [31:0] expected, input integer timeout_cycles);
|
||||
integer cycles;
|
||||
logic observed;
|
||||
cycles = 0;
|
||||
observed = 1'b0;
|
||||
s_allow_rx = 1'b0;
|
||||
while (!observed && (cycles < timeout_cycles)) begin
|
||||
@(negedge clk_i);
|
||||
cycles++;
|
||||
if (rx_valid_i) begin
|
||||
if (rx_data_i !== expected) $fatal(1, "PIOLITE RX got=%h expected=%h", rx_data_i, expected);
|
||||
observed = 1'b1;
|
||||
end
|
||||
end
|
||||
if (!observed) $fatal(1, "PIOLITE RX timeout");
|
||||
s_allow_rx = 1'b1;
|
||||
@(negedge clk_i);
|
||||
s_allow_rx = 1'b0;
|
||||
endtask
|
||||
endmodule
|
||||
@@ -0,0 +1,986 @@
|
||||
logic clk_i = 1'b0;
|
||||
always #5 clk_i = ~clk_i;
|
||||
logic rst_n_i = 1'b0;
|
||||
logic [ 31:0] clock_hz_i = 32'd24000000;
|
||||
logic [ 31:0] user_select_i = 32'hffff_ffff;
|
||||
logic [ 31:0] user_status_i = 32'hffff_ffff;
|
||||
logic [ 31:0] filter_enable_i = 32'd0;
|
||||
logic gpio_ready_i = 1'b1;
|
||||
logic [ 31:0] gpio_input_i = 32'd0;
|
||||
logic [ 31:0] gpio_value_o;
|
||||
logic [ 31:0] gpio_oe_o;
|
||||
logic dma_tx_job_i = 1'b0;
|
||||
logic dma_rx_job_i = 1'b0;
|
||||
logic dma_tx_ready_o;
|
||||
logic dma_rx_valid_o;
|
||||
logic [ 31:0] dma_rx_data_o;
|
||||
logic [ 3:0] dma_rx_keep_o;
|
||||
logic [ 3:0] dma_rx_strb_o;
|
||||
logic dma_rx_last_o;
|
||||
logic dma_rx_id_o;
|
||||
logic dma_rx_dest_o;
|
||||
logic dma_rx_user_o;
|
||||
logic dma_tx_admit_o;
|
||||
logic dma_rx_admit_o;
|
||||
logic irq_o;
|
||||
logic dma_tx_valid;
|
||||
logic [ 31:0] dma_tx_data;
|
||||
logic [ 3:0] dma_tx_keep;
|
||||
logic [ 3:0] dma_tx_strb;
|
||||
logic dma_tx_id;
|
||||
logic dma_tx_dest;
|
||||
logic dma_tx_user;
|
||||
logic dma_tx_last;
|
||||
logic dma_rx_ready;
|
||||
logic [ 31:0] pin_cycles;
|
||||
logic [ 31:0] pin_transitions;
|
||||
logic [ 31:0] stream_tx_count;
|
||||
logic [ 31:0] stream_rx_count;
|
||||
logic [ 31:0] value;
|
||||
logic [ 31:0] expected;
|
||||
logic [ 31:0] vector_mem [0:4095];
|
||||
logic [ 31:0] saved_phase;
|
||||
integer vector_count;
|
||||
integer record_base;
|
||||
integer vector_steps;
|
||||
reg [4095:0] vector_path;
|
||||
|
||||
apb4_if u_apb (
|
||||
.pclk (clk_i),
|
||||
.presetn(rst_n_i)
|
||||
);
|
||||
`ifndef PIOLITE_DUT
|
||||
`define PIOLITE_DUT apb4_piolite
|
||||
`endif
|
||||
`PIOLITE_DUT u_dut (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.clock_hz_i (clock_hz_i),
|
||||
.user_select_i (user_select_i),
|
||||
.user_status_i (user_status_i),
|
||||
.filter_enable_i(filter_enable_i),
|
||||
.gpio_ready_i (gpio_ready_i),
|
||||
.gpio_input_i (gpio_input_i),
|
||||
.gpio_value_o (gpio_value_o),
|
||||
.gpio_oe_o (gpio_oe_o),
|
||||
.dma_tx_job_i (dma_tx_job_i),
|
||||
.dma_rx_job_i (dma_rx_job_i),
|
||||
.dma_tx_valid_i (dma_tx_valid),
|
||||
.dma_tx_ready_o (dma_tx_ready_o),
|
||||
.dma_tx_data_i (dma_tx_data),
|
||||
.dma_tx_keep_i (dma_tx_keep),
|
||||
.dma_tx_strb_i (dma_tx_strb),
|
||||
.dma_tx_id_i (dma_tx_id),
|
||||
.dma_tx_dest_i (dma_tx_dest),
|
||||
.dma_tx_user_i (dma_tx_user),
|
||||
.dma_tx_last_i (dma_tx_last),
|
||||
.dma_rx_valid_o (dma_rx_valid_o),
|
||||
.dma_rx_ready_i (dma_rx_ready),
|
||||
.dma_rx_data_o (dma_rx_data_o),
|
||||
.dma_rx_keep_o (dma_rx_keep_o),
|
||||
.dma_rx_strb_o (dma_rx_strb_o),
|
||||
.dma_rx_last_o (dma_rx_last_o),
|
||||
.dma_rx_id_o (dma_rx_id_o),
|
||||
.dma_rx_dest_o (dma_rx_dest_o),
|
||||
.dma_rx_user_o (dma_rx_user_o),
|
||||
.dma_tx_admit_o (dma_tx_admit_o),
|
||||
.dma_rx_admit_o (dma_rx_admit_o),
|
||||
.irq_o (irq_o),
|
||||
`ifdef PIOLITE_SCALAR_APB
|
||||
.paddr_i (u_apb.paddr),
|
||||
.pprot_i (u_apb.pprot),
|
||||
.psel_i (u_apb.psel),
|
||||
.penable_i (u_apb.penable),
|
||||
.pwrite_i (u_apb.pwrite),
|
||||
.pwdata_i (u_apb.pwdata),
|
||||
.pstrb_i (u_apb.pstrb),
|
||||
.pready_o (u_apb.pready),
|
||||
.prdata_o (u_apb.prdata),
|
||||
.pslverr_o (u_apb.pslverr)
|
||||
`else
|
||||
.apb4 (u_apb)
|
||||
`endif
|
||||
);
|
||||
|
||||
piolite_pin_bfm u_pin_bfm (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.value_i (gpio_value_o),
|
||||
.oe_i (gpio_oe_o),
|
||||
.cycle_count_o (pin_cycles),
|
||||
.transition_count_o(pin_transitions)
|
||||
);
|
||||
|
||||
piolite_stream_bfm u_stream_bfm (
|
||||
.clk_i (clk_i),
|
||||
.rst_n_i (rst_n_i),
|
||||
.tx_valid_o (dma_tx_valid),
|
||||
.tx_ready_i (dma_tx_ready_o),
|
||||
.tx_data_o (dma_tx_data),
|
||||
.tx_keep_o (dma_tx_keep),
|
||||
.tx_strb_o (dma_tx_strb),
|
||||
.tx_id_o (dma_tx_id),
|
||||
.tx_dest_o (dma_tx_dest),
|
||||
.tx_user_o (dma_tx_user),
|
||||
.tx_last_o (dma_tx_last),
|
||||
.rx_valid_i (dma_rx_valid_o),
|
||||
.rx_ready_o (dma_rx_ready),
|
||||
.rx_data_i (dma_rx_data_o),
|
||||
.rx_keep_i (dma_rx_keep_o),
|
||||
.rx_strb_i (dma_rx_strb_o),
|
||||
.rx_last_i (dma_rx_last_o),
|
||||
.rx_id_i (dma_rx_id_o),
|
||||
.rx_dest_i (dma_rx_dest_o),
|
||||
.rx_user_i (dma_rx_user_o),
|
||||
.tx_accepted_count_o(stream_tx_count),
|
||||
.rx_accepted_count_o(stream_rx_count)
|
||||
);
|
||||
|
||||
always @(posedge clk_i) begin
|
||||
if (rst_n_i && ((gpio_oe_o & ~(user_select_i & user_status_i & ~filter_enable_i)) != 0))
|
||||
$fatal(1, "PIOLITE output authorization clamp failed: %h", gpio_oe_o);
|
||||
if (rst_n_i && !gpio_ready_i && (gpio_oe_o != 0)) $fatal(1, "PIOLITE readiness clamp failed");
|
||||
end
|
||||
|
||||
task automatic access (input logic write_access, input logic [11:0] offset, input logic [31:0] data,
|
||||
input logic [3:0] strobe, input logic error_expected,
|
||||
output logic [31:0] result_data);
|
||||
@(negedge clk_i);
|
||||
u_apb.psel = 1'b1;
|
||||
u_apb.penable = 1'b0;
|
||||
u_apb.pwrite = write_access;
|
||||
u_apb.paddr = {20'd0, offset};
|
||||
u_apb.pwdata = data;
|
||||
u_apb.pstrb = strobe;
|
||||
u_apb.pprot = 3'd0;
|
||||
@(negedge clk_i);
|
||||
u_apb.penable = 1'b1;
|
||||
for (integer timeout_cycles = 0; !u_apb.pready; timeout_cycles++) begin
|
||||
if (timeout_cycles == 3) $fatal(1, "PIOLITE APB response exceeded bound");
|
||||
@(negedge clk_i);
|
||||
end
|
||||
if (u_apb.pslverr !== error_expected)
|
||||
$fatal(1, "PIOLITE APB offset=%h error=%b expected=%b", offset, u_apb.pslverr, error_expected);
|
||||
result_data = u_apb.prdata;
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
u_apb.psel = 1'b0;
|
||||
u_apb.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
endtask
|
||||
|
||||
task automatic wr(input logic [11:0] offset, input logic [31:0] data, input logic err);
|
||||
logic [31:0] unused_data;
|
||||
access (1'b1, offset, data, 4'hf, err, unused_data);
|
||||
endtask
|
||||
|
||||
task automatic rd(input logic [11:0] offset, output logic [31:0] data);
|
||||
access (1'b0, offset, 32'd0, 4'd0, 1'b0, data);
|
||||
endtask
|
||||
|
||||
task automatic check_reg(input logic [11:0] offset, input logic [31:0] expected_data);
|
||||
logic [31:0] observed;
|
||||
rd(offset, observed);
|
||||
if (observed !== expected_data)
|
||||
$fatal(1, "PIOLITE register %h got=%h expected=%h", offset, observed, expected_data);
|
||||
endtask
|
||||
|
||||
task automatic reset_dut;
|
||||
rst_n_i = 1'b0;
|
||||
user_select_i = 32'hffff_ffff;
|
||||
user_status_i = 32'hffff_ffff;
|
||||
filter_enable_i = 32'd0;
|
||||
gpio_ready_i = 1'b1;
|
||||
gpio_input_i = 32'd0;
|
||||
dma_tx_job_i = 1'b0;
|
||||
dma_rx_job_i = 1'b0;
|
||||
u_apb.psel = 1'b0;
|
||||
u_apb.penable = 1'b0;
|
||||
u_apb.pwrite = 1'b0;
|
||||
u_apb.paddr = 32'd0;
|
||||
u_apb.pwdata = 32'd0;
|
||||
u_apb.pstrb = 4'd0;
|
||||
u_apb.pprot = 3'd0;
|
||||
u_stream_bfm.reset_bfm();
|
||||
repeat (3) @(negedge clk_i);
|
||||
rst_n_i = 1'b1;
|
||||
repeat (3) @(negedge clk_i);
|
||||
endtask
|
||||
|
||||
task automatic write_program(input integer sm, input logic [4:0] base, input logic [5:0] length,
|
||||
input logic [4:0] wrap_bottom, input logic [4:0] wrap_top);
|
||||
logic [11:0] sm_base;
|
||||
sm_base = sm == 0 ? 12'h200 : 12'h280;
|
||||
wr(sm_base + 12'h00c, (32'(length) << 8) | (32'(base) << 16) | 32'(base), 1'b0);
|
||||
wr(sm_base + 12'h010, (32'(wrap_top) << 8) | 32'(wrap_bottom), 1'b0);
|
||||
endtask
|
||||
|
||||
task automatic run_vectors;
|
||||
if (!$value$plusargs("PIOLITE_VECTORS=%s", vector_path))
|
||||
$fatal(1, "PIOLITE vector file plusarg is required");
|
||||
$readmemh(vector_path, vector_mem);
|
||||
if (vector_mem[0] !== 32'd29) $fatal(1, "PIOLITE vector file/count is invalid");
|
||||
vector_count = vector_mem[0];
|
||||
for (integer vector_index = 0; vector_index < vector_count; vector_index++) begin
|
||||
$display("PIOLITE_P2_VECTOR index=%0d", vector_index);
|
||||
reset_dut();
|
||||
record_base = 1 + vector_index * 78;
|
||||
vector_steps = vector_mem[record_base+18];
|
||||
wr(12'h208, vector_mem[record_base+0], 1'b0);
|
||||
wr(12'h204, vector_mem[record_base+3], 1'b0);
|
||||
wr(12'h20c, vector_mem[record_base+1], 1'b0);
|
||||
wr(12'h210, vector_mem[record_base+2], 1'b0);
|
||||
wr(12'h214, vector_mem[record_base+4], 1'b0);
|
||||
wr(12'h218, vector_mem[record_base+5], 1'b0);
|
||||
wr(12'h21c, vector_mem[record_base+6], 1'b0);
|
||||
wr(12'h220, vector_mem[record_base+7], 1'b0);
|
||||
wr(12'h224, vector_mem[record_base+8], 1'b0);
|
||||
wr(12'h228, vector_mem[record_base+9], 1'b0);
|
||||
wr(12'h22c, vector_mem[record_base+10], 1'b0);
|
||||
wr(12'h230, vector_mem[record_base+11], 1'b0);
|
||||
wr(12'h234, vector_mem[record_base+12], 1'b0);
|
||||
wr(12'h238, vector_mem[record_base+13], 1'b0);
|
||||
for (integer word_index = 0; word_index < (vector_mem[record_base+1] >> 8); word_index++)
|
||||
wr(12'h100 + 12'(word_index * 4), vector_mem[record_base+38+word_index], 1'b0);
|
||||
for (integer tx_index = 0; tx_index < vector_mem[record_base+16]; tx_index++)
|
||||
wr(12'h240, vector_mem[record_base+14+tx_index], 1'b0);
|
||||
gpio_input_i = vector_mem[record_base+17];
|
||||
fork
|
||||
begin
|
||||
if (vector_mem[record_base+37] < (vector_mem[record_base+1] >> 8)) begin
|
||||
@(posedge clk_i);
|
||||
while (!(u_apb.psel && u_apb.penable && !u_apb.pready && u_apb.pwrite &&
|
||||
u_apb.paddr[11:0] == 12'h040))
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
`ifndef PIOLITE_SCALAR_APB
|
||||
force u_dut.u_core.u_imem.s_valid_q = (32'd1 << vector_mem[record_base+37]) - 32'd1;
|
||||
repeat (2) @(negedge clk_i);
|
||||
release u_dut.u_core.u_imem.s_valid_q;
|
||||
`endif
|
||||
end
|
||||
end
|
||||
begin
|
||||
wr(12'h040, 32'd1, 1'b0);
|
||||
end
|
||||
join
|
||||
repeat (vector_steps + 4) @(negedge clk_i);
|
||||
check_reg(12'h200, vector_mem[record_base+19]);
|
||||
check_reg(12'h254, vector_mem[record_base+20]);
|
||||
check_reg(12'h258, vector_mem[record_base+21]);
|
||||
check_reg(12'h25c, vector_mem[record_base+22]);
|
||||
check_reg(12'h260, vector_mem[record_base+23]);
|
||||
check_reg(12'h264, vector_mem[record_base+24]);
|
||||
check_reg(12'h268, vector_mem[record_base+25]);
|
||||
check_reg(12'h26c, vector_mem[record_base+26]);
|
||||
check_reg(12'h270, vector_mem[record_base+27]);
|
||||
check_reg(12'h274, vector_mem[record_base+28]);
|
||||
check_reg(12'h278, vector_mem[record_base+29]);
|
||||
check_reg(12'h27c, vector_mem[record_base+30]);
|
||||
check_reg(12'h248, vector_mem[record_base+31] | (vector_mem[record_base+32] << 8));
|
||||
check_reg(12'h24c, vector_mem[record_base+33]);
|
||||
check_reg(12'h018, vector_mem[record_base+34]);
|
||||
u_pin_bfm.expect_pins(vector_mem[record_base+35], vector_mem[record_base+36]);
|
||||
for (integer rx_index = 0; rx_index < vector_mem[record_base+32]; rx_index++)
|
||||
check_reg(12'h244, vector_mem[record_base+70+rx_index]);
|
||||
$display("PIOLITE_P2_CYCLES vector=%0d edges=%0d", vector_index, vector_steps);
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic core_dual_halt;
|
||||
reset_dut();
|
||||
wr(12'h100, 32'h00000020, 1'b0);
|
||||
wr(12'h104, 32'h00000020, 1'b0);
|
||||
write_program(0, 0, 1, 0, 0);
|
||||
write_program(1, 1, 1, 1, 1);
|
||||
wr(12'h040, 32'h3, 1'b0);
|
||||
repeat (4) @(negedge clk_i);
|
||||
check_reg(12'h200, 32'h3);
|
||||
check_reg(12'h280, 32'h3);
|
||||
check_reg(12'h26c, 32'd1);
|
||||
check_reg(12'h2ec, 32'd1);
|
||||
check_reg(12'h278, 32'd1);
|
||||
check_reg(12'h2f8, 32'd1);
|
||||
$display("PIOLITE_P2_CYCLES case=dual_halt edges=1");
|
||||
endtask
|
||||
|
||||
task automatic core_flag_event_race;
|
||||
logic race_seen;
|
||||
`ifndef PIOLITE_SCALAR_APB
|
||||
reset_dut();
|
||||
wr(12'h100, 32'h0000a080, 1'b0);
|
||||
wr(12'h104, 32'h00000020, 1'b0);
|
||||
wr(12'h108, 32'h0000a000, 1'b0);
|
||||
wr(12'h10c, 32'h00000020, 1'b0);
|
||||
write_program(0, 0, 2, 0, 1);
|
||||
write_program(1, 2, 2, 2, 3);
|
||||
wr(12'h208, 32'd8, 1'b0);
|
||||
wr(12'h288, 32'd8, 1'b0);
|
||||
wr(12'h040, 32'h3, 1'b0);
|
||||
race_seen = 1'b0;
|
||||
fork
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
while (!((u_dut.u_ctrl.runtime[0].tick_count == 1) &&
|
||||
(u_dut.u_ctrl.runtime[0].divider_remaining == 2)))
|
||||
@(negedge clk_i);
|
||||
u_apb.psel = 1'b1;
|
||||
u_apb.penable = 1'b0;
|
||||
u_apb.pwrite = 1'b1;
|
||||
u_apb.paddr = 32'h24c;
|
||||
u_apb.pwdata = 32'd1;
|
||||
u_apb.pstrb = 4'hf;
|
||||
@(negedge clk_i);
|
||||
u_apb.penable = 1'b1;
|
||||
while (!u_apb.pready) @(negedge clk_i);
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
u_apb.psel = 1'b0;
|
||||
u_apb.penable = 1'b0;
|
||||
end
|
||||
begin
|
||||
@(posedge clk_i);
|
||||
while (!(u_apb.psel && u_apb.penable && !u_apb.pready && u_apb.pwrite &&
|
||||
(u_apb.paddr[11:0] == 12'h24c) && u_dut.u_core.gen_sm[0].u_sm.s_commit))
|
||||
@(posedge clk_i);
|
||||
race_seen = 1'b1;
|
||||
end
|
||||
join
|
||||
if (!race_seen) $fatal(1, "PIOLITE EVENTS W1C did not coincide with HALT commit");
|
||||
check_reg(12'h018, 32'd1);
|
||||
check_reg(12'h24c, 32'd1);
|
||||
$display("PIOLITE_P2_COVER PL-V08 same-edge-command-commit-and-set-wins-w1c");
|
||||
`endif
|
||||
endtask
|
||||
|
||||
task automatic pause_at_first_tick;
|
||||
`ifndef PIOLITE_SCALAR_APB
|
||||
logic pause_seen;
|
||||
pause_seen = 1'b0;
|
||||
fork
|
||||
begin
|
||||
@(negedge clk_i);
|
||||
while (!((u_dut.u_ctrl.runtime[0].tick_count == 0) &&
|
||||
(u_dut.u_ctrl.runtime[0].divider_remaining == 2)))
|
||||
@(negedge clk_i);
|
||||
u_apb.psel = 1'b1;
|
||||
u_apb.penable = 1'b0;
|
||||
u_apb.pwrite = 1'b1;
|
||||
u_apb.paddr = 32'h044;
|
||||
u_apb.pwdata = 32'd1;
|
||||
u_apb.pstrb = 4'hf;
|
||||
@(negedge clk_i);
|
||||
u_apb.penable = 1'b1;
|
||||
while (!u_apb.pready) @(negedge clk_i);
|
||||
@(posedge clk_i);
|
||||
@(negedge clk_i);
|
||||
u_apb.psel = 1'b0;
|
||||
u_apb.penable = 1'b0;
|
||||
end
|
||||
begin
|
||||
@(posedge clk_i);
|
||||
while (!(u_apb.psel && u_apb.penable && !u_apb.pready && u_apb.pwrite &&
|
||||
(u_apb.paddr[11:0] == 12'h044) &&
|
||||
(u_dut.u_ctrl.runtime[0].divider_remaining == 1)))
|
||||
@(posedge clk_i);
|
||||
pause_seen = 1'b1;
|
||||
end
|
||||
join
|
||||
if (!pause_seen) $fatal(1, "PIOLITE PAUSE did not land on the first execution tick");
|
||||
`endif
|
||||
endtask
|
||||
|
||||
task automatic check_paused_baseline(input logic [3:0] tx_level);
|
||||
check_reg(12'h200, 32'd2);
|
||||
check_reg(12'h254, 32'd0);
|
||||
check_reg(12'h258, 32'd0);
|
||||
check_reg(12'h25c, 32'd0);
|
||||
check_reg(12'h260, 32'd0);
|
||||
check_reg(12'h264, 32'h0002_0001);
|
||||
check_reg(12'h268, 32'd0);
|
||||
check_reg(12'h26c, 32'd0);
|
||||
check_reg(12'h270, 32'd0);
|
||||
check_reg(12'h274, 32'd0);
|
||||
check_reg(12'h278, 32'd0);
|
||||
check_reg(12'h27c, 32'd1);
|
||||
check_reg(12'h24c, 32'd0);
|
||||
check_reg(12'h248, {20'd0, 4'd0, 4'd0, tx_level});
|
||||
check_reg(12'h018, 32'd0);
|
||||
u_pin_bfm.expect_pins(32'h0000_00c0, 32'd1);
|
||||
endtask
|
||||
|
||||
task automatic pause_candidate(input logic [15:0] word, input logic preload_tx);
|
||||
reset_dut();
|
||||
wr(12'h100, {16'd0, word}, 1'b0);
|
||||
wr(12'h104, 32'h0000_0020, 1'b0);
|
||||
write_program(0, 0, 2, 0, 1);
|
||||
wr(12'h204, 32'h0000_001f, 1'b0);
|
||||
wr(12'h208, 32'd8, 1'b0);
|
||||
wr(12'h214, 32'h0000_2000, 1'b0);
|
||||
wr(12'h218, 32'h0000_2000, 1'b0);
|
||||
wr(12'h21c, 32'h0000_0800, 1'b0);
|
||||
wr(12'h220, 32'hffff_ffff, 1'b0);
|
||||
wr(12'h224, 32'hffff_ffff, 1'b0);
|
||||
wr(12'h228, 32'h0000_00c0, 1'b0);
|
||||
wr(12'h22c, 32'h0000_0001, 1'b0);
|
||||
wr(12'h230, 32'h0002_0001, 1'b0);
|
||||
wr(12'h234, 32'h0000_0820, 1'b0);
|
||||
if (preload_tx) wr(12'h240, 32'hd31a5a69, 1'b0);
|
||||
wr(12'h040, 32'd1, 1'b0);
|
||||
pause_at_first_tick();
|
||||
check_paused_baseline(preload_tx ? 4'd1 : 4'd0);
|
||||
endtask
|
||||
|
||||
task automatic core_pause_atomicity;
|
||||
`ifndef PIOLITE_SCALAR_APB
|
||||
pause_candidate(16'h4028, 1'b0);
|
||||
pause_candidate(16'h5004, 1'b1);
|
||||
pause_candidate(16'h31ff, 1'b0);
|
||||
pause_candidate(16'ha080, 1'b0);
|
||||
pause_candidate(16'h0020, 1'b0);
|
||||
$display("PIOLITE_P2_COVER PL-V08 pause-suppresses-all-candidate-side-effects");
|
||||
`endif
|
||||
endtask
|
||||
|
||||
task automatic core_pin_delay;
|
||||
reset_dut();
|
||||
wr(12'h100, 32'h00001101, 1'b0);
|
||||
wr(12'h104, 32'h00000020, 1'b0);
|
||||
write_program(0, 0, 2, 0, 1);
|
||||
wr(12'h208, 32'd2, 1'b0);
|
||||
wr(12'h21c, 32'h00000100, 1'b0);
|
||||
wr(12'h220, 32'd1, 1'b0);
|
||||
wr(12'h224, 32'd1, 1'b0);
|
||||
wr(12'h22c, 32'd1, 1'b0);
|
||||
wr(12'h040, 32'd1, 1'b0);
|
||||
repeat (8) @(negedge clk_i);
|
||||
u_pin_bfm.expect_pins(32'd1, 32'd1);
|
||||
check_reg(12'h200, 32'd3);
|
||||
check_reg(12'h26c, 32'd3);
|
||||
check_reg(12'h278, 32'd2);
|
||||
$display("PIOLITE_P2_CYCLES case=pin_delay pclk_edges=6 sm_ticks=3 transitions=%0d",
|
||||
pin_transitions);
|
||||
endtask
|
||||
|
||||
task automatic core_pause_resume;
|
||||
reset_dut();
|
||||
wr(12'h100, 32'h00008040, 1'b0);
|
||||
wr(12'h104, 32'h00000020, 1'b0);
|
||||
write_program(0, 0, 2, 0, 1);
|
||||
wr(12'h208, 32'd4, 1'b0);
|
||||
wr(12'h220, 32'd1, 1'b0);
|
||||
wr(12'h040, 32'd1, 1'b0);
|
||||
repeat (2) @(negedge clk_i);
|
||||
wr(12'h044, 32'd1, 1'b0);
|
||||
check_reg(12'h200, 32'd2);
|
||||
rd(12'h27c, saved_phase);
|
||||
rd(12'h26c, value);
|
||||
repeat (10) @(negedge clk_i);
|
||||
check_reg(12'h27c, saved_phase);
|
||||
check_reg(12'h26c, value);
|
||||
wr(12'h048, 32'd1, 1'b0);
|
||||
repeat (6) @(negedge clk_i);
|
||||
rd(12'h200, value);
|
||||
if ((value[2:0] != 1) || (value[6:4] != 1)) $fatal(1, "PIOLITE WAIT did not stall");
|
||||
gpio_input_i[0] = 1'b1;
|
||||
repeat (6) @(negedge clk_i);
|
||||
check_reg(12'h200, 32'd3);
|
||||
$display("PIOLITE_P2_COVER PL-V03 dual-tick-divider-delay-and-pause-resume");
|
||||
endtask
|
||||
|
||||
task automatic core_counter_boundaries;
|
||||
for (integer test_index = 0; test_index < 3; test_index++) begin
|
||||
reset_dut();
|
||||
wr(12'h100, 32'h00006010, 1'b0);
|
||||
wr(12'h104, 32'h00006058, 1'b0);
|
||||
wr(12'h108, 32'h00009063, 1'b0);
|
||||
wr(12'h10c, 32'h00000020, 1'b0);
|
||||
write_program(0, 0, 4, 0, 3);
|
||||
case (test_index)
|
||||
0: wr(12'h230, {16'd1, 16'd0}, 1'b0);
|
||||
1: wr(12'h230, {16'd0, 16'd1}, 1'b0);
|
||||
default: wr(12'h230, 32'hffff_ffff, 1'b0);
|
||||
endcase
|
||||
wr(12'h040, 32'd1, 1'b0);
|
||||
repeat (6) @(negedge clk_i);
|
||||
check_reg(12'h200, 32'd3);
|
||||
case (test_index)
|
||||
0: begin
|
||||
check_reg(12'h25c, 32'd0);
|
||||
check_reg(12'h260, 32'd1);
|
||||
check_reg(12'h264, 32'h0001_0000);
|
||||
end
|
||||
1: begin
|
||||
check_reg(12'h25c, 32'd1);
|
||||
check_reg(12'h260, 32'd0);
|
||||
check_reg(12'h264, 32'd0);
|
||||
end
|
||||
default: begin
|
||||
check_reg(12'h25c, 32'h0000_ffff);
|
||||
check_reg(12'h260, 32'h0000_ffff);
|
||||
check_reg(12'h264, 32'hffff_fffe);
|
||||
end
|
||||
endcase
|
||||
check_reg(12'h278, 32'd4);
|
||||
end
|
||||
$display("PIOLITE_P2_COVER PL-V04 counters-move-wrap-and-branches");
|
||||
endtask
|
||||
|
||||
task automatic core_autopush_full;
|
||||
reset_dut();
|
||||
for (integer index = 0; index < 9; index++) wr(12'h100 + 12'(index * 4), 32'h00004028, 1'b0);
|
||||
wr(12'h124, 32'h00000020, 1'b0);
|
||||
write_program(0, 0, 10, 0, 9);
|
||||
wr(12'h204, 32'h0000_000a, 1'b0);
|
||||
wr(12'h230, 32'h0000_005a, 1'b0);
|
||||
wr(12'h234, 32'h0000_0820, 1'b0);
|
||||
wr(12'h040, 32'd1, 1'b0);
|
||||
repeat (12) @(negedge clk_i);
|
||||
check_reg(12'h248, 32'h0000_0800);
|
||||
check_reg(12'h25c, 32'd0);
|
||||
rd(12'h200, value);
|
||||
if ((value[2:0] != 1) || (value[6:4] != 4)) $fatal(1, "PIOLITE autopush did not stall RX");
|
||||
rd(12'h274, value);
|
||||
if (value == 0) $fatal(1, "PIOLITE RX stall counter did not advance");
|
||||
check_reg(12'h278, 32'd8);
|
||||
check_reg(12'h244, 32'h0000_005a);
|
||||
repeat (3) @(negedge clk_i);
|
||||
check_reg(12'h248, 32'h0000_0800);
|
||||
rd(12'h278, value);
|
||||
if ((value < 9) || (value > 10)) $fatal(1, "PIOLITE autopush resume retirement mismatch");
|
||||
check_reg(12'h25c, 32'd0);
|
||||
for (integer index = 0; index < 8; index++) check_reg(12'h244, 32'h0000_005a);
|
||||
repeat (3) @(negedge clk_i);
|
||||
check_reg(12'h200, 32'd3);
|
||||
check_reg(12'h278, 32'd10);
|
||||
$display("PIOLITE_P2_COVER PL-V05 exact-autopush-threshold-and-atomic-stall");
|
||||
$display("PIOLITE_P2_COVER PL-V06 pre-sample-backpressure-and-stall-accounting");
|
||||
endtask
|
||||
|
||||
task automatic apb_repeated_tx;
|
||||
reset_dut();
|
||||
@(negedge clk_i);
|
||||
u_apb.psel = 1'b1;
|
||||
u_apb.penable = 1'b0;
|
||||
u_apb.pwrite = 1'b1;
|
||||
u_apb.paddr = 32'h240;
|
||||
u_apb.pwdata = 32'h89ab_cdef;
|
||||
u_apb.pstrb = 4'hf;
|
||||
@(negedge clk_i);
|
||||
u_apb.penable = 1'b1;
|
||||
while (!u_apb.pready) @(negedge clk_i);
|
||||
repeat (5) @(negedge clk_i);
|
||||
u_apb.psel = 1'b0;
|
||||
u_apb.penable = 1'b0;
|
||||
@(negedge clk_i);
|
||||
check_reg(12'h248, 32'd1);
|
||||
endtask
|
||||
|
||||
function automatic logic [31:0] global_reset_value(input logic [11:0] offset);
|
||||
case (offset)
|
||||
12'h000: global_reset_value = 32'h5049_4f4c;
|
||||
12'h004, 12'h008: global_reset_value = 32'h0001_0000;
|
||||
12'h00c: global_reset_value = 32'h0808_2002;
|
||||
12'h010, 12'h03c: global_reset_value = 32'hffff_ffff;
|
||||
12'h014: global_reset_value = 32'd24000000;
|
||||
12'h024: global_reset_value = 32'h0003_0000;
|
||||
default: global_reset_value = 32'd0;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
function automatic logic [31:0] sm_reset_value(input logic [11:0] offset);
|
||||
case (offset)
|
||||
12'h008: sm_reset_value = 32'd1;
|
||||
12'h034: sm_reset_value = 32'h0000_2020;
|
||||
12'h03c: sm_reset_value = 32'h0000_0100;
|
||||
default: sm_reset_value = 32'd0;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
function automatic logic global_is_ro(input logic [11:0] offset);
|
||||
case (offset)
|
||||
12'h000, 12'h004, 12'h008, 12'h00c, 12'h010, 12'h014, 12'h018, 12'h024,
|
||||
12'h034, 12'h038, 12'h03c:
|
||||
global_is_ro = 1'b1;
|
||||
default: global_is_ro = 1'b0;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
function automatic logic global_is_wo(input logic [11:0] offset);
|
||||
case (offset)
|
||||
12'h01c, 12'h020, 12'h040, 12'h044, 12'h048, 12'h04c, 12'h050, 12'h054, 12'h058:
|
||||
global_is_wo = 1'b1;
|
||||
default: global_is_wo = 1'b0;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
function automatic logic sm_is_ro(input logic [11:0] offset);
|
||||
case (offset)
|
||||
12'h000, 12'h044, 12'h048, 12'h054, 12'h058, 12'h05c, 12'h060, 12'h064,
|
||||
12'h068, 12'h06c, 12'h070, 12'h074, 12'h078, 12'h07c:
|
||||
sm_is_ro = 1'b1;
|
||||
default: sm_is_ro = 1'b0;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
task automatic apb_reset_access_matrix;
|
||||
reset_dut();
|
||||
for (integer index = 0; index < 23; index++) begin
|
||||
if (global_is_wo(12'(index * 4))) access (1'b0, 12'(index * 4), 32'd0, 4'd0, 1'b1, value);
|
||||
else begin
|
||||
check_reg(12'(index * 4), global_reset_value(12'(index * 4)));
|
||||
if (global_is_ro(12'(index * 4))) wr(12'(index * 4), 32'd0, 1'b1);
|
||||
end
|
||||
end
|
||||
for (integer index = 0; index < 32; index++) check_reg(12'(12'h100 + index * 4), 32'd0);
|
||||
for (integer sm = 0; sm < 2; sm++) begin
|
||||
for (integer index = 0; index < 32; index++) begin
|
||||
automatic logic [11:0] offset;
|
||||
offset = 12'((sm == 0 ? 32'h200 : 32'h280) + index * 4);
|
||||
if (12'(index * 4) == 12'h040) access (1'b0, offset, 32'd0, 4'd0, 1'b1, value);
|
||||
else if (12'(index * 4) == 12'h044) begin
|
||||
access (1'b0, offset, 32'd0, 4'd0, 1'b1, value);
|
||||
wr(offset, 32'd0, 1'b1);
|
||||
end else begin
|
||||
check_reg(offset, sm_reset_value(12'(index * 4)));
|
||||
if (sm_is_ro(12'(index * 4))) wr(offset, 32'd0, 1'b1);
|
||||
end
|
||||
end
|
||||
end
|
||||
endtask
|
||||
|
||||
task automatic apb_rw_fields;
|
||||
reset_dut();
|
||||
wr(12'h204, 32'h0000_001f, 1'b0);
|
||||
check_reg(12'h204, 32'h0000_001f);
|
||||
wr(12'h208, 32'h0000_ffff, 1'b0);
|
||||
check_reg(12'h208, 32'h0000_ffff);
|
||||
wr(12'h20c, 32'h0001_0201, 1'b0);
|
||||
check_reg(12'h20c, 32'h0001_0201);
|
||||
wr(12'h210, 32'h0000_0201, 1'b0);
|
||||
check_reg(12'h210, 32'h0000_0201);
|
||||
wr(12'h214, 32'h0000_2000, 1'b0);
|
||||
check_reg(12'h214, 32'h0000_2000);
|
||||
wr(12'h218, 32'h0000_1001, 1'b0);
|
||||
check_reg(12'h218, 32'h0000_1001);
|
||||
wr(12'h21c, 32'h0000_0802, 1'b0);
|
||||
check_reg(12'h21c, 32'h0000_0802);
|
||||
wr(12'h220, 32'h0000_00ff, 1'b0);
|
||||
check_reg(12'h220, 32'h0000_00ff);
|
||||
wr(12'h224, 32'h0000_00f0, 1'b0);
|
||||
check_reg(12'h224, 32'h0000_00f0);
|
||||
wr(12'h228, 32'h0000_0080, 1'b0);
|
||||
check_reg(12'h228, 32'h0000_0080);
|
||||
wr(12'h22c, 32'h0000_0080, 1'b0);
|
||||
check_reg(12'h22c, 32'h0000_0080);
|
||||
wr(12'h230, 32'hffff_0001, 1'b0);
|
||||
check_reg(12'h230, 32'hffff_0001);
|
||||
wr(12'h234, 32'h0000_2001, 1'b0);
|
||||
check_reg(12'h234, 32'h0000_2001);
|
||||
wr(12'h238, 32'h0000_031f, 1'b0);
|
||||
check_reg(12'h238, 32'h0000_031f);
|
||||
wr(12'h23c, 32'h0000_0808, 1'b0);
|
||||
check_reg(12'h23c, 32'h0000_0808);
|
||||
wr(12'h250, 32'h0000_003f, 1'b0);
|
||||
check_reg(12'h250, 32'h0000_003f);
|
||||
wr(12'h028, 32'h0303_0f03, 1'b0);
|
||||
check_reg(12'h028, 32'h0303_0f03);
|
||||
wr(12'h028, 32'd0, 1'b0);
|
||||
wr(12'h02c, 32'h8000_0300, 1'b0);
|
||||
check_reg(12'h02c, 32'h8000_0300);
|
||||
wr(12'h030, 32'h8000_0201, 1'b0);
|
||||
check_reg(12'h030, 32'h8000_0201);
|
||||
wr(12'h02c, 32'd0, 1'b0);
|
||||
wr(12'h030, 32'd0, 1'b0);
|
||||
endtask
|
||||
|
||||
task automatic run_apb;
|
||||
apb_reset_access_matrix();
|
||||
apb_rw_fields();
|
||||
reset_dut();
|
||||
check_reg(12'h000, 32'h50494f4c);
|
||||
check_reg(12'h004, 32'h00010000);
|
||||
check_reg(12'h008, 32'h00010000);
|
||||
check_reg(12'h00c, 32'h08082002);
|
||||
check_reg(12'h014, 32'd24000000);
|
||||
check_reg(12'h208, 32'd1);
|
||||
check_reg(12'h234, 32'h00002020);
|
||||
check_reg(12'h23c, 32'h00000100);
|
||||
check_reg(12'h038, 32'd0);
|
||||
wr(12'h208, 32'd0, 1'b1);
|
||||
check_reg(12'h208, 32'd1);
|
||||
wr(12'h204, 32'h8000_0000, 1'b1);
|
||||
check_reg(12'h204, 32'd0);
|
||||
wr(12'h100, 32'h00000020, 1'b0);
|
||||
check_reg(12'h038, 32'd1);
|
||||
check_reg(12'h100, 32'h00000020);
|
||||
wr(12'h100, 32'h00010020, 1'b1);
|
||||
check_reg(12'h100, 32'h00000020);
|
||||
access (1'b1, 12'h204, 32'd0, 4'h1, 1'b1, value);
|
||||
access (1'b0, 12'h001, 32'd0, 4'd0, 1'b1, value);
|
||||
access (1'b0, 12'h300, 32'd0, 4'd0, 1'b1, value);
|
||||
access (1'b0, 12'h05c, 32'd0, 4'd0, 1'b1, value);
|
||||
access (1'b0, 12'h240, 32'd0, 4'd0, 1'b1, value);
|
||||
access (1'b0, 12'h244, 32'd0, 4'd0, 1'b1, value);
|
||||
access (1'b0, 12'h380, 32'd0, 4'd0, 1'b1, value);
|
||||
access (1'b0, 12'h400, 32'd0, 4'd0, 1'b1, value);
|
||||
access (1'b1, 12'h204, 32'd0, 4'd0, 1'b1, value);
|
||||
for (integer index = 0; index < 6; index++) wr(12'(12'h040 + index * 4), 32'd4, 1'b1);
|
||||
wr(12'h058, 32'd2, 1'b1);
|
||||
wr(12'h01c, 32'h0000_0010, 1'b1);
|
||||
wr(12'h028, 32'h0400_0000, 1'b1);
|
||||
wr(12'h02c, 32'h8000_0002, 1'b1);
|
||||
wr(12'h030, 32'h8000_0002, 1'b1);
|
||||
wr(12'h20c, 32'd0, 1'b1);
|
||||
wr(12'h20c, 32'h0020_0200, 1'b1);
|
||||
wr(12'h210, 32'h0000_0020, 1'b1);
|
||||
wr(12'h214, 32'h0000_2100, 1'b1);
|
||||
wr(12'h218, 32'h0000_0080, 1'b1);
|
||||
wr(12'h21c, 32'h0000_0900, 1'b1);
|
||||
wr(12'h228, 32'd1, 1'b1);
|
||||
wr(12'h22c, 32'd1, 1'b1);
|
||||
wr(12'h234, 32'd0, 1'b1);
|
||||
wr(12'h234, 32'h0000_2000, 1'b1);
|
||||
wr(12'h238, 32'h0000_0020, 1'b1);
|
||||
wr(12'h23c, 32'd0, 1'b1);
|
||||
wr(12'h24c, 32'h0000_0040, 1'b1);
|
||||
wr(12'h250, 32'h0000_0040, 1'b1);
|
||||
for (integer index = 0; index < 8; index++) wr(12'h240, 32'(index), 1'b0);
|
||||
wr(12'h240, 32'hdead_beef, 1'b1);
|
||||
check_reg(12'h248, 32'd8);
|
||||
wr(12'h23c, 32'h0000_0108, 1'b0);
|
||||
wr(12'h028, 32'd4, 1'b1);
|
||||
wr(12'h028, 32'h0001_0000, 1'b0);
|
||||
if (!irq_o) $fatal(1, "PIOLITE TX level watermark did not assert IRQ");
|
||||
wr(12'h028, 32'd0, 1'b0);
|
||||
user_select_i = 32'd0;
|
||||
user_status_i = 32'd0;
|
||||
wr(12'h058, 32'd1, 1'b0);
|
||||
check_reg(12'h038, 32'd0);
|
||||
check_reg(12'h248, 32'd0);
|
||||
apb_repeated_tx();
|
||||
apb_live_write_atomicity();
|
||||
$display("PIOLITE_P2_COVER PL-V07 all-csr-reset-access-fields-and-errors");
|
||||
endtask
|
||||
|
||||
task automatic apb_live_write_atomicity;
|
||||
start_wait_with_output();
|
||||
wr(12'h204, 32'h0000_001f, 1'b1);
|
||||
check_reg(12'h204, 32'd0);
|
||||
wr(12'h208, 32'd2, 1'b1);
|
||||
check_reg(12'h208, 32'd1);
|
||||
wr(12'h100, 32'h0000_0020, 1'b1);
|
||||
check_reg(12'h100, 32'h0000_8040);
|
||||
check_reg(12'h038, 32'd3);
|
||||
wr(12'h050, 32'd1, 1'b0);
|
||||
check_reg(12'h200, 32'd4);
|
||||
wr(12'h204, 32'h0000_001f, 1'b1);
|
||||
check_reg(12'h204, 32'd0);
|
||||
wr(12'h208, 32'd2, 1'b1);
|
||||
check_reg(12'h208, 32'd1);
|
||||
wr(12'h100, 32'h0000_0020, 1'b1);
|
||||
check_reg(12'h100, 32'h0000_8040);
|
||||
check_reg(12'h038, 32'd3);
|
||||
wr(12'h054, 32'd1, 1'b0);
|
||||
check_reg(12'h200, 32'd0);
|
||||
wr(12'h02c, 32'h8000_0300, 1'b0);
|
||||
dma_tx_job_i = 1'b1;
|
||||
wr(12'h204, 32'h0000_001f, 1'b1);
|
||||
check_reg(12'h204, 32'd0);
|
||||
wr(12'h208, 32'd2, 1'b1);
|
||||
check_reg(12'h208, 32'd1);
|
||||
wr(12'h100, 32'h0000_0020, 1'b1);
|
||||
check_reg(12'h100, 32'h0000_8040);
|
||||
check_reg(12'h038, 32'd3);
|
||||
dma_tx_job_i = 1'b0;
|
||||
wr(12'h02c, 32'd0, 1'b0);
|
||||
$display("PIOLITE_P2_COVER PL-V07 live-config-imem-write-atomicity");
|
||||
endtask
|
||||
|
||||
task automatic start_wait_with_output;
|
||||
reset_dut();
|
||||
wr(12'h100, 32'h00008040, 1'b0);
|
||||
wr(12'h104, 32'h00000020, 1'b0);
|
||||
write_program(0, 0, 2, 0, 1);
|
||||
wr(12'h220, 32'd1, 1'b0);
|
||||
wr(12'h224, 32'd1, 1'b0);
|
||||
wr(12'h22c, 32'd1, 1'b0);
|
||||
wr(12'h040, 32'd1, 1'b0);
|
||||
endtask
|
||||
|
||||
task automatic lifecycle_abort_reset;
|
||||
start_wait_with_output();
|
||||
repeat (2) @(negedge clk_i);
|
||||
wr(12'h050, 32'd1, 1'b0);
|
||||
check_reg(12'h200, 32'd4);
|
||||
u_pin_bfm.expect_quiescent();
|
||||
check_reg(12'h24c, 32'd2);
|
||||
wr(12'h040, 32'd1, 1'b1);
|
||||
wr(12'h04c, 32'd1, 1'b1);
|
||||
wr(12'h24c, 32'd2, 1'b0);
|
||||
check_reg(12'h24c, 32'd0);
|
||||
wr(12'h050, 32'd1, 1'b0);
|
||||
check_reg(12'h24c, 32'd2);
|
||||
wr(12'h054, 32'd1, 1'b0);
|
||||
check_reg(12'h200, 32'd0);
|
||||
check_reg(12'h24c, 32'd0);
|
||||
check_reg(12'h220, 32'd1);
|
||||
endtask
|
||||
|
||||
task automatic lifecycle_guard_loss;
|
||||
start_wait_with_output();
|
||||
repeat (2) @(negedge clk_i);
|
||||
user_select_i[0] = 1'b0;
|
||||
#1;
|
||||
u_pin_bfm.expect_quiescent();
|
||||
repeat (2) @(negedge clk_i);
|
||||
check_reg(12'h254, 32'd6);
|
||||
check_reg(12'h200, 32'd4);
|
||||
user_select_i = 32'hffff_ffff;
|
||||
repeat (2) @(negedge clk_i);
|
||||
check_reg(12'h200, 32'd4);
|
||||
wr(12'h054, 32'd1, 1'b0);
|
||||
endtask
|
||||
|
||||
task automatic lifecycle_disabled_resources;
|
||||
reset_dut();
|
||||
wr(12'h240, 32'h1234_5678, 1'b0);
|
||||
wr(12'h050, 32'd1, 1'b0);
|
||||
check_reg(12'h200, 32'd4);
|
||||
wr(12'h240, 32'hdead_beef, 1'b1);
|
||||
access (1'b0, 12'h244, 32'd0, 4'd0, 1'b1, value);
|
||||
wr(12'h054, 32'd1, 1'b0);
|
||||
check_reg(12'h248, 32'd0);
|
||||
endtask
|
||||
|
||||
task automatic lifecycle_invalid_stream;
|
||||
reset_dut();
|
||||
wr(12'h02c, 32'h8000_0300, 1'b0);
|
||||
u_stream_bfm.send_tx(32'h1122_3344, 4'h0, 4'hf, 1'b0);
|
||||
repeat (3) @(negedge clk_i);
|
||||
check_reg(12'h254, 32'd8);
|
||||
check_reg(12'h200, 32'd4);
|
||||
u_stream_bfm.send_tx(32'h5566_7788, 4'hf, 4'hf, 1'b0);
|
||||
check_reg(12'h034, 32'h0000_0010);
|
||||
wr(12'h054, 32'd1, 1'b0);
|
||||
check_reg(12'h02c, 32'd0);
|
||||
endtask
|
||||
|
||||
task automatic lifecycle_rx_valid;
|
||||
reset_dut();
|
||||
wr(12'h100, 32'h00004028, 1'b0);
|
||||
wr(12'h104, 32'h00007080, 1'b0);
|
||||
wr(12'h108, 32'h00000020, 1'b0);
|
||||
write_program(0, 0, 3, 0, 2);
|
||||
wr(12'h204, 32'h0000_0008, 1'b0);
|
||||
wr(12'h230, 32'h0000_00ca, 1'b0);
|
||||
wr(12'h040, 32'd1, 1'b0);
|
||||
repeat (5) @(negedge clk_i);
|
||||
check_reg(12'h200, 32'd3);
|
||||
check_reg(12'h248, 32'h0000_0100);
|
||||
wr(12'h04c, 32'd1, 1'b0);
|
||||
wr(12'h030, 32'h8000_0200, 1'b0);
|
||||
repeat (3) @(negedge clk_i);
|
||||
if (!dma_rx_valid_o || (dma_rx_data_o !== 32'hca)) $fatal(1, "PIOLITE RX held beat missing");
|
||||
repeat (5) @(negedge clk_i);
|
||||
wr(12'h050, 32'd1, 1'b0);
|
||||
if (!dma_rx_valid_o || (dma_rx_data_o !== 32'hca)) $fatal(1, "PIOLITE abort withdrew RX VALID");
|
||||
u_stream_bfm.expect_rx(32'hca, 10);
|
||||
wr(12'h054, 32'd1, 1'b0);
|
||||
check_reg(12'h030, 32'd0);
|
||||
endtask
|
||||
|
||||
task automatic lifecycle_paused_dma_snapshot;
|
||||
reset_dut();
|
||||
wr(12'h100, 32'h00008040, 1'b0);
|
||||
wr(12'h104, 32'h00000020, 1'b0);
|
||||
write_program(0, 0, 2, 0, 1);
|
||||
wr(12'h220, 32'd1, 1'b0);
|
||||
wr(12'h02c, 32'h8000_0300, 1'b0);
|
||||
wr(12'h040, 32'd1, 1'b0);
|
||||
repeat (2) @(negedge clk_i);
|
||||
wr(12'h044, 32'd1, 1'b0);
|
||||
rd(12'h27c, saved_phase);
|
||||
rd(12'h26c, expected);
|
||||
u_stream_bfm.send_tx(32'h0123_4567, 4'hf, 4'hf, 1'b0);
|
||||
u_stream_bfm.send_tx(32'h89ab_cdef, 4'hf, 4'hf, 1'b0);
|
||||
check_reg(12'h248, 32'd2);
|
||||
check_reg(12'h200, 32'd2);
|
||||
check_reg(12'h258, 32'd0);
|
||||
check_reg(12'h25c, 32'd0);
|
||||
check_reg(12'h260, 32'd0);
|
||||
check_reg(12'h264, 32'd0);
|
||||
check_reg(12'h27c, saved_phase);
|
||||
check_reg(12'h26c, expected);
|
||||
wr(12'h050, 32'd1, 1'b0);
|
||||
wr(12'h054, 32'd1, 1'b0);
|
||||
$display("PIOLITE_P2_COVER PL-V08 paused-snapshot-while-dma-progresses");
|
||||
endtask
|
||||
|
||||
task automatic lifecycle_tx_discard;
|
||||
reset_dut();
|
||||
for (integer index = 0; index < 8; index++) wr(12'h240, 32'(index), 1'b0);
|
||||
wr(12'h02c, 32'h8000_0300, 1'b0);
|
||||
u_stream_bfm.send_tx(32'hdead_beef, 4'hf, 4'hf, 1'b0);
|
||||
repeat (2) @(negedge clk_i);
|
||||
rd(12'h034, value);
|
||||
if (!value[2]) $fatal(1, "PIOLITE TX beat was not held at full FIFO");
|
||||
wr(12'h050, 32'd1, 1'b0);
|
||||
repeat (2) @(negedge clk_i);
|
||||
rd(12'h034, value);
|
||||
if (value[2] || !value[4]) $fatal(1, "PIOLITE closing did not discard held TX beat");
|
||||
u_stream_bfm.send_tx(32'hc001_d00d, 4'hf, 4'hf, 1'b0);
|
||||
wr(12'h054, 32'd1, 1'b0);
|
||||
endtask
|
||||
|
||||
task automatic run_netlist;
|
||||
core_dual_halt();
|
||||
reset_dut();
|
||||
check_reg(12'h000, 32'h50494f4c);
|
||||
check_reg(12'h00c, 32'h08082002);
|
||||
wr(12'h100, 32'h00000020, 1'b0);
|
||||
write_program(0, 0, 1, 0, 0);
|
||||
wr(12'h040, 32'd1, 1'b0);
|
||||
repeat (3) @(negedge clk_i);
|
||||
check_reg(12'h200, 32'd3);
|
||||
endtask
|
||||
|
||||
initial begin
|
||||
case (TestKind)
|
||||
0: run_vectors();
|
||||
1: begin
|
||||
core_dual_halt();
|
||||
core_flag_event_race();
|
||||
core_pause_atomicity();
|
||||
core_pin_delay();
|
||||
core_pause_resume();
|
||||
core_counter_boundaries();
|
||||
core_autopush_full();
|
||||
end
|
||||
3: run_apb();
|
||||
4: begin
|
||||
$display("PIOLITE_P2_LIFECYCLE case=abort_reset");
|
||||
lifecycle_abort_reset();
|
||||
$display("PIOLITE_P2_LIFECYCLE case=guard_loss");
|
||||
lifecycle_guard_loss();
|
||||
$display("PIOLITE_P2_LIFECYCLE case=disabled_resources");
|
||||
lifecycle_disabled_resources();
|
||||
$display("PIOLITE_P2_LIFECYCLE case=invalid_stream");
|
||||
lifecycle_invalid_stream();
|
||||
$display("PIOLITE_P2_LIFECYCLE case=rx_valid");
|
||||
lifecycle_rx_valid();
|
||||
lifecycle_paused_dma_snapshot();
|
||||
$display("PIOLITE_P2_LIFECYCLE case=tx_discard");
|
||||
lifecycle_tx_discard();
|
||||
end
|
||||
5: run_netlist();
|
||||
default: $fatal(1, "PIOLITE invalid TestKind");
|
||||
endcase
|
||||
$display("PIOLITE_P2_PASS %s", TestName);
|
||||
$finish;
|
||||
end
|
||||
|
||||
initial begin
|
||||
#20000000;
|
||||
$fatal(1, "SIM_TEST_TIMEOUT PIOLITE fixture");
|
||||
end
|
||||
@@ -0,0 +1,125 @@
|
||||
"""Independent frozen PIO-lite APB1.0/ISA1.0 handwritten mirror checks."""
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
GLOBALS = {
|
||||
"IP_ID": 0x000, "IP_VERSION": 0x004, "ISA_VERSION": 0x008,
|
||||
"CAPABILITY": 0x00C, "ROUTABLE_MASK": 0x010, "CLOCK_HZ": 0x014,
|
||||
"FLAGS": 0x018, "FLAG_SET": 0x01C, "FLAG_CLEAR": 0x020,
|
||||
"IRQ_STATUS": 0x024, "IRQ_ENABLE": 0x028, "TX_BIND": 0x02C,
|
||||
"RX_BIND": 0x030, "TRANSPORT_STATUS": 0x034, "IMEM_VALID": 0x038,
|
||||
"OWNED_MASK": 0x03C, "START": 0x040, "PAUSE": 0x044, "RESUME": 0x048,
|
||||
"DISABLE": 0x04C, "ABORT": 0x050, "SM_RESET": 0x054, "BLOCK_RESET": 0x058,
|
||||
}
|
||||
SM_REGISTERS = {
|
||||
name: index * 4 for index, name in enumerate(
|
||||
"STATUS CONFIG DIV PROGRAM WRAP IN_PINS OUT_PINS SET_PINS CLAIM_MASK OUTPUT_MASK "
|
||||
"INITIAL_VALUE INITIAL_OE INITIAL_XY SHIFT_LIMITS TEST_SELECT WATERMARKS TXDATA "
|
||||
"RXDATA FIFO_LEVELS EVENTS EVENT_ENABLE FAULT PC ISR OSR XY SHIFT_COUNTS "
|
||||
"TICK_COUNT TX_STALL_COUNT RX_STALL_COUNT RETIRED_COUNT EXEC_PHASE".split()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
def _mirrors():
|
||||
sv = (ROOT / "rtl/ip/piolite/piolite_define.svh").read_text()
|
||||
header = (ROOT / "crt/include/retrosoc/hal/piolite_regs.h").read_text()
|
||||
rtl = {}
|
||||
pattern = r"^`define\s+(APB4_PIOLITE__|PIOLITE_)(\w+)\s+(?:(\d+)'h([0-9a-f]+)|(\d+))\s*$"
|
||||
for prefix, name, width, hexadecimal, decimal in re.findall(pattern, sv, re.M):
|
||||
key = ("APB4_" if prefix.startswith("APB4") else "") + name
|
||||
assert key not in rtl
|
||||
rtl[key] = int(hexadecimal, 16) if width else int(decimal)
|
||||
c = {
|
||||
name: int(hexadecimal, 16) if hexadecimal else int(decimal)
|
||||
for name, hexadecimal, decimal in re.findall(
|
||||
r"^#define\s+RS_PIOLITE_ABI_(\w+)\s+"
|
||||
r"(?:UINT32_C\(0x([0-9a-f]+)\)|(\d+)U)\s*$", header, re.M
|
||||
)
|
||||
}
|
||||
return rtl, c, header
|
||||
|
||||
|
||||
def test_exact_svh_c_dictionary_and_frozen_matrix():
|
||||
rtl, c, _ = _mirrors()
|
||||
assert rtl == c
|
||||
for name, offset in GLOBALS.items():
|
||||
assert rtl["APB4_" + name] == offset
|
||||
for name, offset in SM_REGISTERS.items():
|
||||
assert rtl["APB4_SM_" + name] == offset
|
||||
assert rtl["APB4_SM0_BASE"] == 0x200
|
||||
assert rtl["APB4_SM1_BASE"] == 0x280
|
||||
assert rtl["APB4_SM_STRIDE"] == 0x80
|
||||
assert rtl["APB4_IMEM_BASE"] == 0x100
|
||||
assert rtl["APB4_IMEM_END"] == 0x17C
|
||||
assert rtl["APB4_IMEM_WORDS"] == 32
|
||||
assert rtl["APB4_IMEM_WORD_STRIDE"] == 4
|
||||
matrix = list(GLOBALS.values())
|
||||
matrix += [0x100 + index * 4 for index in range(32)]
|
||||
matrix += [base + offset for base in (0x200, 0x280) for offset in SM_REGISTERS.values()]
|
||||
assert len(matrix) == len(set(matrix)) == 119
|
||||
assert sorted(matrix) == sorted(GLOBALS.values()) + sorted(set(matrix) - set(GLOBALS.values()))
|
||||
|
||||
|
||||
def test_independent_resets_fields_and_isa_enums():
|
||||
rtl, _, header = _mirrors()
|
||||
assert rtl["APB4_ID_VALUE"] == 0x50494F4C
|
||||
assert rtl["APB4_VERSION_VALUE"] == rtl["APB4_ISA_VALUE"] == 0x00010000
|
||||
assert rtl["APB4_CAPABILITY_VALUE"] == 0x08082002
|
||||
assert rtl["APB4_ROUTES_VALUE"] == 0xFFFFFFFF
|
||||
assert rtl["APB4_DIV_RESET"] == 1
|
||||
assert rtl["APB4_SHIFT_LIMITS_RESET"] == 0x2020
|
||||
assert rtl["APB4_WATERMARKS_RESET"] == 0x100
|
||||
assert rtl["APB4_IRQ_MASK"] == 0x03030F03
|
||||
fields = {
|
||||
"STATUS_STATE": 0, "STATUS_STALL": 4, "STATUS_LAST_FIFO_FAILED": 8,
|
||||
"PROGRAM_BASE": 0, "PROGRAM_LEN": 8, "PROGRAM_ENTRY": 16,
|
||||
"WRAP_BOTTOM": 0, "WRAP_TOP": 8, "SHIFT_LIMITS_TX_BITS": 0,
|
||||
"SHIFT_LIMITS_RX_BITS": 8, "WATERMARKS_TX_LOW": 0,
|
||||
"WATERMARKS_RX_HIGH": 8, "FAULT_CODE": 0, "FAULT_PC": 8,
|
||||
"SHIFT_COUNTS_ISR": 0, "SHIFT_COUNTS_OSR": 8,
|
||||
"EXEC_PHASE_DIV": 0, "EXEC_PHASE_DELAY": 16,
|
||||
"TX_BIND_SM": 0, "TX_BIND_CHANNEL": 8, "TX_BIND_ENABLE": 31,
|
||||
"RX_BIND_SM": 0, "RX_BIND_CHANNEL": 8, "RX_BIND_ENABLE": 31,
|
||||
"IRQ_STATUS_SM0": 0, "IRQ_STATUS_SM1": 1, "IRQ_STATUS_FLAGS": 8,
|
||||
"IRQ_STATUS_TX_LEVEL": 16, "IRQ_STATUS_RX_LEVEL": 24,
|
||||
}
|
||||
for name, bit in fields.items():
|
||||
assert rtl[name] == bit
|
||||
for index, name in enumerate("AUTOPULL AUTOPUSH TX_RIGHT RX_RIGHT TEST_PIN_ENABLE".split()):
|
||||
assert rtl["CONFIG_" + name] == index
|
||||
for index, name in enumerate("HALT ABORT FAULT TX_STALL RX_STALL FIFO_FAIL".split()):
|
||||
assert rtl["EVENTS_" + name] == rtl["EVENT_ENABLE_" + name] == index
|
||||
expected = {}
|
||||
expected.update({f"OPCODE_{name}": value for value, name in enumerate(
|
||||
"CTRL SET_PIN SET_DIR SET_COUNT IN OUT MOVE FIFO WAIT BRANCH FLAG".split())})
|
||||
expected.update({f"CTRL_{name}": value for value, name in enumerate(
|
||||
"NOP HALT CLEAR_ISR CLEAR_OSR".split())})
|
||||
expected.update({f"IN_{name}": value for value, name in enumerate("PINS X Y ZERO".split())})
|
||||
expected.update({f"OUT_{name}": value for value, name in enumerate(
|
||||
"PINS DIRECTIONS X Y DISCARD".split())})
|
||||
expected.update({f"MOVE_{name}": value for value, name in enumerate(
|
||||
"ISR OSR X Y ZERO ONES".split())})
|
||||
expected.update({"FIFO_PULL": 0, "FIFO_PUSH": 1, "FIFO_NONBLOCKING": 0, "FIFO_BLOCKING": 1})
|
||||
expected.update({"WAIT_PIN": 0, "WAIT_FLAG": 1, "FLAG_CLEAR": 0, "FLAG_SET": 1})
|
||||
expected.update({f"BRANCH_{name}": value for value, name in enumerate(
|
||||
"ALWAYS X_ZERO Y_ZERO DEC_X_NONZERO DEC_Y_NONZERO TEST_PIN_HIGH "
|
||||
"FIFO_FAILED TEST_FLAG_SET".split())})
|
||||
for name, value in expected.items():
|
||||
assert rtl[name] == value
|
||||
c_enums = {
|
||||
name: int(value) for name, value in re.findall(
|
||||
r"\b(RS_PIOLITE_(?:OPCODE|CTRL|IN|OUT|MOVE|FIFO|WAIT|FLAG|BRANCH)_\w+)"
|
||||
r"\s*=\s*(\d+)(?=\s*[,}])",
|
||||
header
|
||||
)
|
||||
}
|
||||
assert c_enums == {"RS_PIOLITE_" + name: value for name, value in expected.items()}
|
||||
|
||||
|
||||
def test_p2_has_no_product_or_hal_integration():
|
||||
assert not (ROOT / "crt/include/retrosoc/hal/piolite.h").exists()
|
||||
for profile in ("ics55-tiny.mk", "ihp130-tiny.mk"):
|
||||
assert "PIOLITE" not in (ROOT / "configs/ci" / profile).read_text().upper()
|
||||
assert all("/piolite/" not in path.read_text() for path in (ROOT / "rtl/tiny/filelist").glob("*.fl"))
|
||||
Reference in new issue
Block a user