From 99a6b0c4d3f080a45f1672a489da27053ee07e2f Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Mon, 5 Oct 2026 17:58:18 -0700 Subject: [PATCH 1/7] Integrate latest UCIe commits --- software/ucie.h | 299 ++++++++++++++++++++++++++++++---------- src/Iris.scala | 1 + src/TinyIris.scala | 15 +- src/UcieComplex.scala | 150 +++++++++++++++++++- test/src/IrisSpec.scala | 16 +++ ucie | 2 +- 6 files changed, 398 insertions(+), 85 deletions(-) diff --git a/software/ucie.h b/software/ucie.h index a01c8fe..708b66b 100644 --- a/software/ucie.h +++ b/software/ucie.h @@ -16,48 +16,51 @@ #define UCIE_TX_DATA_MODE 0x10ULL #define UCIE_TX_LFSR_SEED 0x18ULL #define UCIE_TX_LFSR_SEED_WIDTH 0x8ULL -#define UCIE_TX_RST 0xb0ULL -#define UCIE_TX_EXECUTE 0xb8ULL -#define UCIE_TX_WRITE_CHUNK 0xc0ULL -#define UCIE_TX_PACKETS_SENT 0xc8ULL -#define UCIE_TX_MANUAL_REPEAT_PERIOD 0xd0ULL -#define UCIE_TX_PACKETS_TO_SEND 0xd8ULL -#define UCIE_TX_CLK_P 0xe0ULL -#define UCIE_TX_CLK_N 0xe8ULL -#define UCIE_TX_DATA_LANE_GROUP 0xf0ULL -#define UCIE_TX_DATA_OFFSET 0xf8ULL -#define UCIE_TX_DATA_CHUNK_IN0 0x100ULL -#define UCIE_TX_DATA_CHUNK_IN1 0x108ULL -#define UCIE_TX_DATA_CHUNK_OUT0 0x110ULL -#define UCIE_TX_DATA_CHUNK_OUT1 0x118ULL -#define UCIE_TX_TEST_STATE 0x120ULL -#define UCIE_RX_DATA_MODE 0x128ULL -#define UCIE_RX_LFSR_SEED 0x130ULL +#define UCIE_TX_DIVIDER_RST 0xb0ULL +#define UCIE_RX_DIVIDER_RST 0xb8ULL +#define UCIE_TX_DATAPATH_RST 0xc0ULL +#define UCIE_TX_FSM_RST 0xc8ULL +#define UCIE_TX_EXECUTE 0xd0ULL +#define UCIE_TX_WRITE_CHUNK 0xd8ULL +#define UCIE_TX_PACKETS_SENT 0xe0ULL +#define UCIE_TX_MANUAL_REPEAT_PERIOD 0xe8ULL +#define UCIE_TX_PACKETS_TO_SEND 0xf0ULL +#define UCIE_TX_CLK_P 0xf8ULL +#define UCIE_TX_CLK_N 0x100ULL +#define UCIE_TX_DATA_LANE_GROUP 0x108ULL +#define UCIE_TX_DATA_OFFSET 0x110ULL +#define UCIE_TX_DATA_CHUNK_IN0 0x118ULL +#define UCIE_TX_DATA_CHUNK_IN1 0x120ULL +#define UCIE_TX_DATA_CHUNK_OUT0 0x128ULL +#define UCIE_TX_DATA_CHUNK_OUT1 0x130ULL +#define UCIE_TX_TEST_STATE 0x138ULL +#define UCIE_RX_DATA_MODE 0x140ULL +#define UCIE_RX_LFSR_SEED 0x148ULL #define UCIE_RX_LFSR_SEED_WIDTH 0x8ULL -#define UCIE_RX_BIT_ERRORS 0x1c8ULL +#define UCIE_RX_BIT_ERRORS 0x1e0ULL #define UCIE_RX_BIT_ERRORS_WIDTH 0x8ULL -#define UCIE_RX_BIT_ERRORS_EARLY 0x260ULL +#define UCIE_RX_BIT_ERRORS_EARLY 0x278ULL #define UCIE_RX_BIT_ERRORS_EARLY_WIDTH 0x8ULL -#define UCIE_RX_BIT_ERRORS_LATE 0x2f8ULL +#define UCIE_RX_BIT_ERRORS_LATE 0x310ULL #define UCIE_RX_BIT_ERRORS_LATE_WIDTH 0x8ULL -#define UCIE_RX_RST 0x390ULL -#define UCIE_RX_PACKETS_TO_RECEIVE 0x398ULL -#define UCIE_RX_PAUSE_COUNTERS 0x3a0ULL -#define UCIE_RX_PACKETS_RECEIVED 0x3a8ULL -#define UCIE_RX_SIGNATURE 0x3b0ULL -#define UCIE_RX_DATA_LANE 0x3b8ULL -#define UCIE_RX_DATA_OFFSET 0x3c0ULL -#define UCIE_RX_DATA_CHUNK 0x3c8ULL -#define UCIE_CLK_PHASE_SEL 0x3d0ULL -#define UCIE_CLK_FREQ_SEL 0x3d8ULL -#define UCIE_TXCTL 0x3e0ULL +#define UCIE_RX_DATAPATH_RST 0x3a8ULL +#define UCIE_RX_FSM_RST 0x3b0ULL +#define UCIE_RX_PACKETS_TO_RECEIVE 0x3b8ULL +#define UCIE_RX_PAUSE_COUNTERS 0x3c0ULL +#define UCIE_RX_PACKETS_RECEIVED 0x3c8ULL +#define UCIE_RX_SIGNATURE 0x3d0ULL +#define UCIE_RX_IDLE_WORDS_OBSERVED 0x3d8ULL +#define UCIE_RX_DATA_LANE 0x3e0ULL +#define UCIE_RX_DATA_OFFSET 0x3e8ULL +#define UCIE_RX_DATA_CHUNK 0x3f0ULL +#define UCIE_TXCTL 0x3f8ULL #define UCIE_TXCTL_TILE_OFS 0x0ULL #define UCIE_TXCTL_SHUFFLER_OFS 0x8ULL #define UCIE_TXCTL_SHUFFLER_WIDTH 0x8ULL #define UCIE_TXCTL_SAMPLE_NEGEDGE_OFS 0x108ULL #define UCIE_TXCTL_DELAY_OFS 0x110ULL #define UCIE_TXCTL_WIDTH 0x118ULL -#define UCIE_RXCTL 0x1ad8ULL +#define UCIE_RXCTL 0x1af0ULL #define UCIE_RXCTL_ZEN_OFS 0x0ULL #define UCIE_RXCTL_ZCTL_OFS 0x8ULL #define UCIE_RXCTL_VREF_SEL_OFS 0x10ULL @@ -70,46 +73,71 @@ #define UCIE_RXCTL_SAMPLE_NEGEDGE_OFS 0x138ULL #define UCIE_RXCTL_RX_DELAY_OFS 0x140ULL #define UCIE_RXCTL_WIDTH 0x148ULL -#define UCIE_DEBUG_TX_TEST_MODE 0x35c0ULL -#define UCIE_DEBUG_TX_DATA_MODE 0x35c8ULL -#define UCIE_DEBUG_TX_LFSR_SEED 0x35d0ULL -#define UCIE_DEBUG_TX_FSM_RST 0x35d8ULL -#define UCIE_DEBUG_TX_EXECUTE 0x35e0ULL -#define UCIE_DEBUG_TX_MANUAL_REPEAT_PERIOD 0x35e8ULL -#define UCIE_DEBUG_TX_PACKETS_TO_SEND 0x35f0ULL -#define UCIE_DEBUG_DATA 0x35f8ULL +#define UCIE_DEBUG_TX_TEST_MODE 0x35d8ULL +#define UCIE_DEBUG_TX_DATA_MODE 0x35e0ULL +#define UCIE_DEBUG_TX_LFSR_SEED 0x35e8ULL +#define UCIE_DEBUG_TX_FSM_RST 0x35f0ULL +#define UCIE_DEBUG_TX_EXECUTE 0x35f8ULL +#define UCIE_DEBUG_TX_MANUAL_REPEAT_PERIOD 0x3600ULL +#define UCIE_DEBUG_TX_PACKETS_TO_SEND 0x3608ULL +#define UCIE_DEBUG_DATA 0x3610ULL #define UCIE_DEBUG_DATA_WIDTH 0x8ULL -#define UCIE_DEBUG_DRIVERCTL 0x3678ULL +#define UCIE_DEBUG_DRIVERCTL 0x3690ULL #define UCIE_DEBUG_DRIVERCTL_WIDTH 0x8ULL -#define UCIE_DEBUG_TXCTL_TILE 0x3698ULL -#define UCIE_DEBUG_TXCTL_SHUFFLER 0x36a0ULL +#define UCIE_DEBUG_TXCTL_TILE 0x36b0ULL +#define UCIE_DEBUG_TXCTL_SHUFFLER 0x36b8ULL #define UCIE_DEBUG_TXCTL_SHUFFLER_WIDTH 0x8ULL -#define UCIE_DEBUG_TX_TEST_STATE 0x37a0ULL -#define UCIE_DEBUG_TX_PACKETS_SENT 0x37a8ULL -#define UCIE_DEBUG_CLK_MUX_SEL 0x37b0ULL -#define UCIE_DEBUG_RX_LANE 0x37b8ULL -#define UCIE_DEBUG_RX_BIT 0x37c0ULL -#define UCIE_TX_VALID 0x37c8ULL -#define UCIE_RX_LFSR_VALID 0x37d0ULL -#define UCIE_CONTROLLER_SEL 0x37d8ULL -#define UCIE_MAINBAND_MODE 0x37e0ULL -#define UCIE_SIDEBAND_MODE 0x37e8ULL -#define UCIE_CREDIT_FLOW_ENABLE 0x37f0ULL -#define UCIE_TX_VALID_LANE_SEL 0x37f8ULL -#define UCIE_RX_VALID_LANE_SEL 0x3800ULL -#define UCIE_LAST_SEEN_TL_REQ 0x3808ULL -#define UCIE_SB_TX_PACKET 0x3810ULL -#define UCIE_SB_TX_SEND 0x3818ULL -#define UCIE_SB_TX_BUSY 0x3820ULL -#define UCIE_SB_RX_PACKET 0x3828ULL -#define UCIE_SB_RX_VALID 0x3830ULL -#define UCIE_SB_RX_POP 0x3838ULL -#define UCIE_SB_RX_OVERFLOW 0x3840ULL -#define UCIE_SB_RX_RST 0x3848ULL -#define UCIE_SB_TL_RX_OVERFLOW 0x3850ULL -#define UCIE_UCIE_LINK_RESET 0x3858ULL -#define UCIE_UCIE_PWR_GOOD 0x3860ULL -#define UCIE_UCIE_RETRY_TRAINING_AMT 0x3868ULL +#define UCIE_DEBUG_TX_TEST_STATE 0x37b8ULL +#define UCIE_DEBUG_TX_PACKETS_SENT 0x37c0ULL +#define UCIE_DEBUG_CLK_MUX_SEL 0x37c8ULL +#define UCIE_DEBUG_RX_LANE 0x37d0ULL +#define UCIE_DEBUG_RX_BIT 0x37d8ULL +#define UCIE_TX_VALID 0x37e0ULL +#define UCIE_RX_LFSR_VALID 0x37e8ULL +#define UCIE_CONTROLLER_SEL 0x37f0ULL +#define UCIE_MAINBAND_MODE 0x37f8ULL +#define UCIE_SIDEBAND_MODE 0x3800ULL +#define UCIE_CREDIT_FLOW_ENABLE 0x3808ULL +#define UCIE_TX_VALID_LANE_SEL 0x3810ULL +#define UCIE_RX_VALID_LANE_SEL 0x3818ULL +#define UCIE_LAST_SEEN_TL_REQ 0x3820ULL +#define UCIE_SB_TX_PACKET 0x3828ULL +#define UCIE_SB_TX_SEND 0x3830ULL +#define UCIE_SB_TX_BUSY 0x3838ULL +#define UCIE_SB_RX_PACKET 0x3840ULL +#define UCIE_SB_RX_VALID 0x3848ULL +#define UCIE_SB_RX_POP 0x3850ULL +#define UCIE_SB_RX_OVERFLOW 0x3858ULL +#define UCIE_SB_RX_RST 0x3860ULL +#define UCIE_SB_TL_RX_OVERFLOW 0x3868ULL +#define UCIE_UCIE_LINK_RESET 0x3870ULL +#define UCIE_UCIE_PWR_GOOD 0x3878ULL +#define UCIE_UCIE_RETRY_TRAINING_AMT 0x3880ULL +#define UCIE_MAIN_CLK_SEL 0x10000ULL +#define UCIE_PLL8_EN 0x10008ULL +#define UCIE_PLL12_EN 0x10010ULL +#define UCIE_PLL16_EN 0x10018ULL +#define UCIE_TX_CLK_DIV 0x10020ULL +#define UCIE_TX_CLK_PHASE 0x10028ULL +#define UCIE_DIG_CLK_DIV 0x10030ULL +#define UCIE_DIG_CLK_BYPASS_EN 0x10038ULL +#define UCIE_CLK_PHASE_SEL 0x10040ULL +#define UCIE_CLK_GATE_EN 0x10048ULL +#define UCIE_RX_CLK_GATE_EN 0x10050ULL +#define UCIE_FREQ_SEL_AUTO_EN 0x10058ULL +#define UCIE_FREQ_SEL_OBSERVED 0x10060ULL +#define UCIE_SB_CLK_DIV 0x10068ULL +#define UCIE_SB_CLK_BYPASS_EN 0x10070ULL +#define UCIE_RX_CLK_FROM_TX_Q 0x10078ULL +#define UCIE_CLK_CFG_APPLY 0x10080ULL +#define UCIE_CLK_CFG_BUSY 0x10088ULL +#define UCIE_CLK_CFG_UNGATE_SRC 0x10090ULL +#define UCIE_CLK_CFG_UNGATE_DELAY 0x10098ULL +#define UCIE_CLK_CFG_UNGATE_REQ 0x100a0ULL +#define UCIE_PLL_LOCK_OBSERVED 0x100a8ULL +#define UCIE_UCIE_RST 0x100b0ULL +#define UCIE_RATE_CFG 0x10100ULL +#define UCIE_RATE_CFG_WIDTH 0x8ULL // === Constants === #define UCIE_TX_TEST_STATE_IDLE 0x0ULL @@ -131,11 +159,35 @@ #define UCIE_DEFAULT_TRACK 0x55555555ULL #define UCIE_ENABLE_DRIVER_CTL 0x3ffeULL #define UCIE_ENABLE_TX_CTL 0x1fff000000000ULL +#define UCIE_TX_CTL_DELAY_LSB 0x0ULL +#define UCIE_TRAIN_LFSR_SEED 0x123456789abcdefULL +#define UCIE_TRAIN_DATA_LANES 0x10ULL +#define UCIE_TRAIN_VALID_LANE 0x10ULL +#define UCIE_TRAIN_SCORE_LANES 0x11ULL +#define UCIE_RATE_CFG_MAIN_CLK_SEL_LSB 0x8ULL +#define UCIE_RATE_CFG_TX_CLK_DIV_LSB 0x6ULL +#define UCIE_RATE_CFG_DIG_CLK_DIV_LSB 0x3ULL +#define UCIE_TRAIN_EYE_FINE_POINTS 0x4ULL +#define UCIE_TRAIN_EYE_FINE_STEP 0x10ULL +#define UCIE_TRAIN_EYE_DIVS 0x3ULL +#define UCIE_TRAIN_GLOBAL_CODES 0x10ULL +#define UCIE_TRAIN_GLOBAL_STEP 0x4ULL +#define UCIE_TRAIN_LOCAL_CODES 0x10ULL +#define UCIE_TRAIN_LOCAL_STEP 0x2ULL +#define UCIE_TRAIN_VREF_CODES 0x10ULL +#define UCIE_TRAIN_VREF_STEP 0x8ULL +#define UCIE_TRAIN_PACKETS 0x10ULL +#define UCIE_TRAIN_SLIP_RATIO 0x4ULL +#define UCIE_CLK_UNGATE_SRC_PLL_LOCK 0x0ULL +#define UCIE_CLK_UNGATE_SRC_MMIO 0x1ULL +#define UCIE_CLK_UNGATE_SRC_DELAY 0x2ULL // === Helper functions === static inline void reset_fsms(uintptr_t base) { - reg_write64(base + UCIE_TX_RST, 0x1ULL); - reg_write64(base + UCIE_RX_RST, 0x1ULL); + reg_write64(base + UCIE_TX_DATAPATH_RST, 0x1ULL); + reg_write64(base + UCIE_RX_DATAPATH_RST, 0x1ULL); + reg_write64(base + UCIE_TX_FSM_RST, 0x1ULL); + reg_write64(base + UCIE_RX_FSM_RST, 0x1ULL); reg_write64(base + UCIE_DEBUG_TX_FSM_RST, 0x1ULL); assert(reg_read64(base + UCIE_TX_TEST_STATE) == (UCIE_TX_TEST_STATE_IDLE)); assert(reg_read64(base + UCIE_TX_PACKETS_SENT) == (0x0ULL)); @@ -150,7 +202,77 @@ static inline void write_rxctl(uintptr_t base, uint64_t lane, uint64_t ofs, uint reg_write64(base + UCIE_RXCTL + lane * UCIE_RXCTL_WIDTH + ofs, v); } +static inline void set_clk_gate(uintptr_t base, uint64_t en) { + reg_write64(base + UCIE_CLK_GATE_EN, en); +} +static inline void set_rate_cfg(uintptr_t base, uint64_t rate, uint64_t main_sel, uint64_t tx_div, uint64_t dig_div, uint64_t pll_en) { + reg_write64(base + UCIE_RATE_CFG + rate * UCIE_RATE_CFG_WIDTH, ((main_sel << UCIE_RATE_CFG_MAIN_CLK_SEL_LSB) | (tx_div << UCIE_RATE_CFG_TX_CLK_DIV_LSB) | (dig_div << UCIE_RATE_CFG_DIG_CLK_DIV_LSB) | pll_en)); +} +static inline void set_freq_sel_auto(uintptr_t base, uint64_t en) { + reg_write64(base + UCIE_FREQ_SEL_AUTO_EN, en); +} +static inline void apply_clk_cfg(uintptr_t base) { + reg_write64(base + UCIE_CLK_CFG_APPLY, 0x1ULL); + for (int w = 0; w < 256; w++) { + uint64_t busy; + busy = reg_read64(base + UCIE_CLK_CFG_BUSY); + if (busy == 0) { + break; + } + } +} +static inline void set_ungate_src(uintptr_t base, uint64_t src, uint64_t delay) { + reg_write64(base + UCIE_CLK_CFG_UNGATE_SRC, src); + reg_write64(base + UCIE_CLK_CFG_UNGATE_DELAY, delay); +} +static inline void release_clk_gate(uintptr_t base) { + reg_write64(base + UCIE_CLK_CFG_UNGATE_REQ, 0x1ULL); +} +static inline void reset_ucie(uintptr_t base) { + reg_write64(base + UCIE_UCIE_RST, 0x1ULL); + reg_write64(base + UCIE_UCIE_RST, 0x0ULL); +} +static inline void reset_dividers(uintptr_t base) { + reg_write64(base + UCIE_TX_DIVIDER_RST, 0x1ULL); + reg_write64(base + UCIE_RX_DIVIDER_RST, 0x1ULL); + reg_write64(base + UCIE_TX_DIVIDER_RST, 0x0ULL); + reg_write64(base + UCIE_RX_DIVIDER_RST, 0x0ULL); +} +static inline void set_main_clk(uintptr_t base, uint64_t src, uint64_t div) { + reg_write64(base + UCIE_MAIN_CLK_SEL, src); + reg_write64(base + UCIE_TX_CLK_DIV, div); + reg_write64(base + UCIE_CLK_GATE_EN, 0x1ULL); + apply_clk_cfg(base); +} +static inline void set_tx_phase(uintptr_t base, uint64_t half_cycles) { + reg_write64(base + UCIE_CLK_GATE_EN, 0x0ULL); + reg_write64(base + UCIE_TX_CLK_PHASE, half_cycles); + reg_write64(base + UCIE_CLK_GATE_EN, 0x1ULL); +} +static inline void use_internal_clk(uintptr_t base, uint64_t dig_div) { + reg_write64(base + UCIE_PLL8_EN, 0x1ULL); + reg_write64(base + UCIE_PLL12_EN, 0x1ULL); + reg_write64(base + UCIE_PLL16_EN, 0x1ULL); + reg_write64(base + UCIE_DIG_CLK_DIV, dig_div); + reg_write64(base + UCIE_DIG_CLK_BYPASS_EN, 0x0ULL); + apply_clk_cfg(base); +} +static inline void set_rx_clk_gate(uintptr_t base, uint64_t en) { + reg_write64(base + UCIE_RX_CLK_GATE_EN, en); +} +static inline void set_global_delay(uintptr_t base, uint64_t taps) { + reg_write64(base + UCIE_CLK_PHASE_SEL, ((0x1ULL << taps) - 0x1ULL)); +} +static inline void set_tx_delay(uintptr_t base, uint64_t lane, uint64_t taps) { + write_txctl(base, lane, UCIE_TXCTL_TILE_OFS, (UCIE_ENABLE_TX_CTL | (((0x1ULL << taps) - 0x1ULL) << UCIE_TX_CTL_DELAY_LSB))); +} + +static inline void set_rx_vref(uintptr_t base, uint64_t lane, uint64_t code) { + write_rxctl(base, lane, UCIE_RXCTL_VREF_SEL_OFS, code); +} + static inline void setup_ucie(uintptr_t base) { + reset_ucie(base); for (int lane = 0; lane < 21; lane++) { write_txctl(base, lane, UCIE_TXCTL_TILE_OFS, UCIE_ENABLE_TX_CTL); write_rxctl(base, lane, UCIE_RXCTL_ZEN_OFS, 0x1ULL); @@ -164,10 +286,45 @@ static inline void setup_ucie(uintptr_t base) { reg_write64(base + UCIE_DEBUG_DRIVERCTL + 8 * i, UCIE_ENABLE_DRIVER_CTL); } reg_write64(base + UCIE_DEBUG_TXCTL_TILE, UCIE_ENABLE_TX_CTL); + set_clk_gate(base, 0x0ULL); + reset_dividers(base); + set_clk_gate(base, 0x1ULL); reset_fsms(base); reg_write64(base + UCIE_CONTROLLER_SEL, UCIE_CONTROLLER_SEL_PHYTEST); reg_write64(base + UCIE_MAINBAND_MODE, UCIE_BAND_MODE_MANUAL); reg_write64(base + UCIE_SIDEBAND_MODE, UCIE_BAND_MODE_MANUAL); } +static inline void seed_lfsrs(uintptr_t base) { + for (int i = 0; i < 19; i++) { + reg_write64(base + UCIE_TX_LFSR_SEED + i * UCIE_TX_LFSR_SEED_WIDTH, UCIE_TRAIN_LFSR_SEED); + reg_write64(base + UCIE_RX_LFSR_SEED + i * UCIE_RX_LFSR_SEED_WIDTH, UCIE_TRAIN_LFSR_SEED); + } +} + +static inline void run_lfsr(uintptr_t base, uint64_t packets) { + reset_fsms(base); + for (int s = 0; s < 64; s++) { + uint64_t settle_w; + settle_w = reg_read64(base + UCIE_RX_IDLE_WORDS_OBSERVED); + if (settle_w >= 4) { + break; + } + } + reg_write64(base + UCIE_TEST_TARGET, UCIE_TEST_TARGET_MAINBAND); + reg_write64(base + UCIE_TX_TEST_MODE, UCIE_TX_TEST_MODE_LFSR); + reg_write64(base + UCIE_TX_DATA_MODE, UCIE_DATA_MODE_FINITE); + reg_write64(base + UCIE_RX_DATA_MODE, UCIE_DATA_MODE_FINITE); + reg_write64(base + UCIE_RX_PACKETS_TO_RECEIVE, packets); + reg_write64(base + UCIE_TX_PACKETS_TO_SEND, packets); + reg_write64(base + UCIE_TX_EXECUTE, 0x1ULL); + for (int w = 0; w < 16; w++) { + uint64_t r; + r = reg_read64(base + UCIE_RX_PACKETS_RECEIVED); + if (r >= packets) { + break; + } + } +} + #endif diff --git a/src/Iris.scala b/src/Iris.scala index 444b6bf..5c34fa9 100644 --- a/src/Iris.scala +++ b/src/Iris.scala @@ -44,6 +44,7 @@ class JTAGChipIO(hasReset: Boolean) extends Bundle { class IrisSystem(implicit p: Parameters) extends edu.berkeley.cs.chippy.ChippySystem with testchipip.soc.CanHaveChipletRouting + with CanHaveUcieRegisters // UCIe register blocks, attached around the router with testchipip.soc.CanHaveSubsystemInjectors // Enables the subsystem injector API with testchipip.soc.CanHaveSwitchableOffchipBus // Enables optional off-chip-bus with interface-switch with testchipip.serdes.CanHavePeripheryTLSerial diff --git a/src/TinyIris.scala b/src/TinyIris.scala index 94a0de6..d4e1989 100644 --- a/src/TinyIris.scala +++ b/src/TinyIris.scala @@ -102,8 +102,9 @@ class TinyIrisTop(implicit p: Parameters) extends LazyModule with BindingScope { // c2c_serial_tl1 <> system.d2d_port_ios.get(1) // Connect D2D UCIe - val c2c_ucie0 = IO(new edu.berkeley.cs.uciedigital.tilelink.UcieBumpsIO(p(ChipletRoutingKey).get.ports(0).asInstanceOf[edu.berkeley.cs.uciedigital.tilelink.UcieTLParams].numLanes)) - val c2c_ucie1 = IO(new edu.berkeley.cs.uciedigital.tilelink.UcieBumpsIO(p(ChipletRoutingKey).get.ports(1).asInstanceOf[edu.berkeley.cs.uciedigital.tilelink.UcieTLParams].numLanes)) + val uciePorts = UciePort.all(p) + val c2c_ucie0 = IO(new edu.berkeley.cs.uciedigital.tilelink.UcieBumpsIO(uciePorts(0).numLanes)) + val c2c_ucie1 = IO(new edu.berkeley.cs.uciedigital.tilelink.UcieBumpsIO(uciePorts(1).numLanes)) c2c_ucie0 <> system.d2d_port_ios.get(0) c2c_ucie1 <> system.d2d_port_ios.get(1) } @@ -238,18 +239,20 @@ class TinyIrisConfig(sim: Boolean = false) new testchipip.soc.WithChipletRouting(testchipip.soc.ChipletRoutingParams( routerParams = testchipip.soc.OffchipRouterParams(tableEntries = 4), ports = Seq( - edu.berkeley.cs.uciedigital.tilelink.UcieTLParams( + // IrisSystem attaches the UCIe register blocks itself, which needs + // the links UcieComplexPort builds. + UcieComplexPort(edu.berkeley.cs.uciedigital.tilelink.UcieTLParams( address = 0x200000, managerWhere = SBUS, numLanes = 16, includeDefaultModels = true - ), - edu.berkeley.cs.uciedigital.tilelink.UcieTLParams( + )), + UcieComplexPort(edu.berkeley.cs.uciedigital.tilelink.UcieTLParams( address = 0x208000, managerWhere = SBUS, numLanes = 16, includeDefaultModels = true - ) + )) ))) ++ // 1 serial tilelink port diff --git a/src/UcieComplex.scala b/src/UcieComplex.scala index 820782f..e937778 100644 --- a/src/UcieComplex.scala +++ b/src/UcieComplex.scala @@ -2,9 +2,12 @@ package edu.berkeley.cs.iris import org.chipsalliance.cde.config.Parameters import freechips.rocketchip.diplomacy._ -import freechips.rocketchip.subsystem.TLBusWrapperLocation -import edu.berkeley.cs.uciedigital.tilelink.UcieTLParams +import freechips.rocketchip.prci._ +import freechips.rocketchip.subsystem.{BaseSubsystem, TLBusWrapperLocation} +import freechips.rocketchip.tilelink._ +import edu.berkeley.cs.uciedigital.tilelink.{UcieBumpsIO, UcieTL, UcieTLParams} import testchipip.soc.{ + CanHaveChipletRouting, ChipletLinkParams, ChipletLinkWrapper, ChipletLinkWrapperInstantiationLike, @@ -27,6 +30,85 @@ class UcieComplex(implicit p: Parameters) override lazy val desiredName = "UcieComplex" } +/** A UCIe link as a chiplet-router port, minus its register blocks. + * + * The router attaches one control node per port to its control bus. A UCIe + * link has two register blocks: the main one, on the clock its PHY makes, and + * the clock register block, which sets that clock up and so runs on the + * chip's. Rather than send one through the router and the other around it, + * this hands the router neither, and [[CanHaveUcieRegisters]] attaches both + * straight to the bus. + * + * Every port this link puts on [[UcieComplex]] is synchronous to the one + * digital clock the router gives it, the register ports included. The main + * register block crosses into the PHY's clock in here, rather than at the + * bus, so the crossing stays inside the block physical design hardens and + * its boundary has no clock the block's constraints don't already know. + * + * Otherwise this is the UCIe repo's `UcieChipletLink`, module name included, + * so the hierarchy physical design sees does not move. + */ +class UcieComplexLink( + val params: UcieTLParams, + sysParams: OffchipSubsystemParams +)(implicit p: Parameters) + extends ChipletLinkWrapper { + override lazy val desiredName = s"UcieChipletLink${params.moduleSuffix}" + + val ucie = LazyModule( + new UcieTL( + params, + sysParams.managerRegion, + sysParams.managerBeatBytes, + sysParams.managerBlockBytes + ) + ) + + // The link's digital clock. Named for the UcieTL node it used to feed + // directly, so that the port it makes on UcieComplex is still + // `auto_d2d_port_ucie_digital_clock_in`, which the block's SDC names. + val ucieDigitalClockNode = ClockSinkNode(Seq(ClockSinkParameters())) + // It is also the chip clock the clock register block runs on: always + // running, which is all that block asks of it, and the same clock the bus + // reaches it on, so its register port needs no crossing. + private val ucieClocks = ClockSourceNode(Seq.fill(2)(ClockSourceParameters())) + ucie.digitalClockNode := ucieClocks + ucie.chipDigitalClockNode := ucieClocks + + val client_node = ucie.clientNode + val manager_node = ucie.managerNode + val control_manager_node: Option[TLRegisterNode] = None + val clock_node = Some(ucieDigitalClockNode) + val top_IO = BundleBridgeSource(() => new UcieBumpsIO(params.numLanes)) + + /** The main register block's port. The block runs on the clock the PHY + * makes, which is unrelated to the digital clock, so the port crosses into + * it: the source half here, the sink inside the block. One entry, like the + * debug module's register crossing, since register accesses need no more. + * The name node keeps the port it makes on UcieComplex called + * `auto_d2d_port_ucie_regs_in`, as it was before the crossing. + */ + val regNode: TLInwardNode = + ucie.regs.crossIn(ucie.regNode).apply(AsynchronousCrossing(depth = 1)) :=* + TLNameNode("ucie_regs") + + /** The clock register block's port, already on the digital clock. */ + val clkRegNode: TLInwardNode = ucie.clkRegNode + + override lazy val module = new LazyRawModuleImp(this) { + val digitalClock = ucieDigitalClockNode.in.head._1 + childClock := digitalClock.clock + childReset := digitalClock.reset + override def provideImplicitClockToLazyChildren = true + + ucieClocks.out.foreach { case (out, _) => + out.clock := digitalClock.clock + out.reset := digitalClock.reset + } + ucie.module.io <> top_IO.out(0)._1 + } +} + /** A chiplet-router port whose UCIe link goes inside the shared [[UcieComplex]]. * * The router builds each port by calling `instantiate` on the port parameters, @@ -44,8 +126,9 @@ case class UcieComplexPort(ucie: UcieTLParams) extends ChipletLinkParams with ChipletLinkWrapperInstantiationLike { def managerBusWhere: TLBusWrapperLocation = ucie.managerBusWhere - def controlManagerBusWhere: Option[TLBusWrapperLocation] = - ucie.controlManagerBusWhere + // The link gives the router no control node to attach; see + // [[CanHaveUcieRegisters]] for where its registers go instead. + def controlManagerBusWhere: Option[TLBusWrapperLocation] = None def instantiate(params: OffchipSubsystemParams, id: Int)(implicit p: Parameters @@ -63,16 +146,15 @@ case class UcieComplexPort(ucie: UcieTLParams) d2d_ports.suggestName("d2d_ports") d2d_ports } - complex { ucie.instantiate(params, id) } + complex { LazyModule(new UcieComplexLink(ucie, params)) } } } object UciePort { - /** The UCIe parameters of a chiplet-router port, wrapped or not. */ + /** The UCIe parameters of a chiplet-router port. */ def unapply(link: ChipletLinkParams): Option[UcieTLParams] = link match { case wrapped: UcieComplexPort => Some(wrapped.ucie) - case direct: UcieTLParams => Some(direct) case _ => None } @@ -81,3 +163,57 @@ object UciePort { .map(_.ports.collect { case UciePort(u) => u }) .getOrElse(Nil) } + +/** Attaches the register blocks of every UCIe link directly to the chiplet + * router's control bus. + * + * [[UcieComplexLink]] gives the router no control node, so this is the only + * place they are attached. Both of its register ports are on the link's + * digital clock, which the router already connects the link's data ports to + * the buses on without a crossing, and the link does its own crossing into + * the PHY's clock. + * + * The router keeps its ports to itself, so the links are found by walking the + * subsystem's children. Extending [[CanHaveChipletRouting]] makes sure they + * exist by the time this runs. + */ +trait CanHaveUcieRegisters extends CanHaveChipletRouting { + this: BaseSubsystem => + p(ChipletRoutingKey).foreach { params => + // The UCIe repo's own link hands its main register block to the router and + // has no way to hand over the clock register block, which would be left + // dangling. + require( + !params.ports.exists(_.isInstanceOf[UcieTLParams]), + "UCIe chiplet-router ports must be wrapped in UcieComplexPort, so that " + + "CanHaveUcieRegisters can attach their register blocks" + ) + + def links(lm: LazyModule): Seq[UcieComplexLink] = + lm.getChildren.reverse.flatMap { + case link: UcieComplexLink => Seq(link) + case child => links(child) + } + val ucieLinks = links(this) + require( + ucieLinks.size == UciePort.all(p).size, + s"found ${ucieLinks.size} UCIe links under the subsystem, but the " + + s"chiplet router has ${UciePort.all(p).size} UCIe ports" + ) + + val cbus = locateTLBusWrapper(params.controlBusWhere) + ucieLinks.foreach { link => + Seq( + "control" -> link.regNode, + "clk_control" -> link.clkRegNode + ).foreach { case (suffix, node) => + // The register nodes are as wide as the link's manager bus, which is + // wider than the control bus. + cbus.coupleTo(s"${link.name}_$suffix") { + node := TLWidthWidget(cbus.beatBytes) := TLBuffer() := + TLFragmenter(cbus) := _ + } + } + } + } +} diff --git a/test/src/IrisSpec.scala b/test/src/IrisSpec.scala index da209de..cb7ab6d 100644 --- a/test/src/IrisSpec.scala +++ b/test/src/IrisSpec.scala @@ -163,6 +163,20 @@ class TestHarness(nChips: Int, binaryPaths: Seq[Path], plusArgs: Seq[Seq[String] ucieDigitalBypassClockSource.io.gate := false.B ucieDigitalBypassClock := ucieDigitalBypassClockSource.io.clk + val ucieSidebandBypassFreqMHz = 800 + val ucieSidebandBypassClock = Wire(Clock()) + val ucieSidebandBypassClockSource = Module(new ClockSourceAtFreqMHz(ucieSidebandBypassFreqMHz)) + ucieSidebandBypassClockSource.io.power := true.B + ucieSidebandBypassClockSource.io.gate := false.B + ucieSidebandBypassClock := ucieSidebandBypassClockSource.io.clk + + // The clocking tile's PLL reference, as in the UCIe repo's own harness. + val ucieRefFreqMHz = 100 + val ucieRefClock = Wire(Clock()) + val ucieRefClockSource = Module(new ClockSourceAtFreqMHz(ucieRefFreqMHz)) + ucieRefClockSource.io.power := true.B + ucieRefClockSource.io.gate := false.B + ucieRefClock := ucieRefClockSource.io.clk implicit def view[A <: Data, B <: Data] : DataView[testchipip.tsi.TSIIO, TSIIO] = @@ -228,6 +242,8 @@ class TestHarness(nChips: Int, binaryPaths: Seq[Path], plusArgs: Seq[Seq[String] Seq(chiptop.c2c_ucie0, chiptop.c2c_ucie1).foreach { ucie => ucie.phy.bypassClk := ucieBypassClock ucie.phy.digitalBypassClk := ucieDigitalBypassClock + ucie.phy.sidebandBypassClk := ucieSidebandBypassClock + ucie.phy.refClk := ucieRefClock } Seq(chiptop.c2c_ucie0, chiptop.c2c_ucie1) diff --git a/ucie b/ucie index caf0daf..3fbc560 160000 --- a/ucie +++ b/ucie @@ -1 +1 @@ -Subproject commit caf0daf1135f5b87225fa4903fcc3dfb87863de5 +Subproject commit 3fbc560b3c6060a69b1052603d8c2105e40edcae From eea47b668d2c3898dad4b346a1688bac9692896a Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Tue, 6 Oct 2026 17:38:32 -0700 Subject: [PATCH 2/7] connect up latest ucie version --- software/ucie.h | 81 ++++++++++++++++++++++++- src/TinyIris.scala | 15 ++--- src/UcieComplex.scala | 131 ++++++---------------------------------- test/src/IrisSpec.scala | 47 ++++---------- ucie | 2 +- 5 files changed, 115 insertions(+), 161 deletions(-) diff --git a/software/ucie.h b/software/ucie.h index 708b66b..bd29c98 100644 --- a/software/ucie.h +++ b/software/ucie.h @@ -110,9 +110,84 @@ #define UCIE_SB_RX_OVERFLOW 0x3858ULL #define UCIE_SB_RX_RST 0x3860ULL #define UCIE_SB_TL_RX_OVERFLOW 0x3868ULL -#define UCIE_UCIE_LINK_RESET 0x3870ULL -#define UCIE_UCIE_PWR_GOOD 0x3878ULL -#define UCIE_UCIE_RETRY_TRAINING_AMT 0x3880ULL +#define UCIE_PCIE_EXT_CAP_ID 0x4000ULL +#define UCIE_DVSEC_VENDOR_ID 0x4004ULL +#define UCIE_DVSEC_ID 0x4008ULL +#define UCIE_RAW_FORMAT 0x400cULL +#define UCIE_RAW_FORMAT_ENABLE 0x4010ULL +#define UCIE_RAW_FORMAT_ENABLED 0x4014ULL +#define UCIE_LINK_STATUS_CHANGED_INT_EN 0x4018ULL +#define UCIE_LOCATOR0_BIR 0x401cULL +#define UCIE_LOCATOR0_BLOCK_OFFSET_HI 0x4020ULL +#define UCIE_LOCATOR1_BIR 0x4024ULL +#define UCIE_LOCATOR1_BLOCK_OFFSET_HI 0x4028ULL +#define UCIE_MAILBOX_OPCODE 0x402cULL +#define UCIE_MAILBOX_ADDR_HI 0x4030ULL +#define UCIE_MAILBOX_DATA_LO 0x4034ULL +#define UCIE_MAILBOX_DATA_HI 0x4038ULL +#define UCIE_MAILBOX_TRIGGER 0x403cULL +#define UCIE_MAILBOX_STATUS 0x4040ULL +#define UCIE_VENDOR_ID 0x5000ULL +#define UCIE_VENDOR_REG_BLOCK_VERSION 0x5004ULL +#define UCIE_VENDOR_REG_BLOCK_LENGTH 0x5008ULL +#define UCIE_VENDOR_REG_BLOCK_RESERVED 0x500cULL +#define UCIE_UNCORR_ERR_STATUS 0x5010ULL +#define UCIE_UNCORR_ERR_MASK 0x5014ULL +#define UCIE_UNCORR_ERR_SEVERITY 0x5018ULL +#define UCIE_CORR_ERR_STATUS 0x501cULL +#define UCIE_CORR_ERR_MASK 0x5020ULL +#define UCIE_HEADER_LOG1_LO 0x5024ULL +#define UCIE_HEADER_LOG1_HI 0x5028ULL +#define UCIE_HL2_TIMEOUT_ENC 0x502cULL +#define UCIE_REMOTE_REG_ACCESS_THRESHOLD 0x5030ULL +#define UCIE_PARITY_LOG0_LO 0x5034ULL +#define UCIE_PARITY_LOG0_HI 0x5038ULL +#define UCIE_PARITY_LOG1_LO 0x503cULL +#define UCIE_PARITY_LOG1_HI 0x5040ULL +#define UCIE_PARITY_LOG2_LO 0x5044ULL +#define UCIE_PARITY_LOG2_HI 0x5048ULL +#define UCIE_PARITY_LOG3_LO 0x504cULL +#define UCIE_PARITY_LOG3_HI 0x5050ULL +#define UCIE_ADV_ADAPTER_CAP_LO 0x5054ULL +#define UCIE_ADV_ADAPTER_CAP_HI 0x5058ULL +#define UCIE_FIN_ADAPTER_CAP_LO 0x505cULL +#define UCIE_FIN_ADAPTER_CAP_HI 0x5060ULL +#define UCIE_ADV_CXL_CAP_LO 0x5064ULL +#define UCIE_ADV_CXL_CAP_HI 0x5068ULL +#define UCIE_FIN_CXL_CAP_LO 0x506cULL +#define UCIE_FIN_CXL_CAP_HI 0x5070ULL +#define UCIE_ADV_MULTIPROT_CAP_LO 0x5078ULL +#define UCIE_ADV_MULTIPROT_CAP_HI 0x507cULL +#define UCIE_FIN_MULTIPROT_CAP_LO 0x5080ULL +#define UCIE_FIN_MULTIPROT_CAP_HI 0x5084ULL +#define UCIE_ADV_CXL_CAP_STACK1_LO 0x5088ULL +#define UCIE_ADV_CXL_CAP_STACK1_HI 0x508cULL +#define UCIE_FIN_CXL_CAP_STACK1_LO 0x5090ULL +#define UCIE_FIN_CXL_CAP_STACK1_HI 0x5094ULL +#define UCIE_PHY_CAP_RSVD 0x6000ULL +#define UCIE_PHY_CTRL_RSVD 0x6004ULL +#define UCIE_PHY_STATUS_RSVD 0x6008ULL +#define UCIE_INIT_CONTROL 0x600cULL +#define UCIE_RLT_CTRL 0x6100ULL +#define UCIE_MODULE3_LANE_REPAIR_ID_RSVD_HI 0x6104ULL +#define UCIE_RUNTIME_LINK_TEST_BUSY 0x6108ULL +#define UCIE_NULL_EXT_CAP 0x6200ULL +#define UCIE_TS1_DATA_PATTERN_M0 0x6010ULL +#define UCIE_TS2_IDLE_COUNT_M0 0x6020ULL +#define UCIE_TS3_LANE_MASK_LO_M0 0x6030ULL +#define UCIE_TS3_LANE_MASK_HI_M0 0x6034ULL +#define UCIE_TS4_REPAIR_LANE_MASK_M0 0x6050ULL +#define UCIE_CURRENT_LANE_MAP_LO_M0 0x6060ULL +#define UCIE_CURRENT_LANE_MAP_HI_M0 0x6064ULL +#define UCIE_ERR_LOG0_STATE_NM0 0x6080ULL +#define UCIE_ERR_LOG1_STATE_NM3_M0 0x6090ULL +#define UCIE_VENDOR_REG_BLOCK_RSVD 0x700cULL +#define UCIE_VENDOR_DEBUG_UNLOCK 0x7010ULL +#define UCIE_VENDOR_FORCE_LINK_ACTIVE 0x7014ULL +#define UCIE_VENDOR_TS_OVERRIDE_EN 0x7018ULL +#define UCIE_UCIE_LINK_RESET 0x3c00ULL +#define UCIE_UCIE_PWR_GOOD 0x3c08ULL +#define UCIE_UCIE_RETRY_TRAINING_AMT 0x3c10ULL #define UCIE_MAIN_CLK_SEL 0x10000ULL #define UCIE_PLL8_EN 0x10008ULL #define UCIE_PLL12_EN 0x10010ULL diff --git a/src/TinyIris.scala b/src/TinyIris.scala index d4e1989..94a0de6 100644 --- a/src/TinyIris.scala +++ b/src/TinyIris.scala @@ -102,9 +102,8 @@ class TinyIrisTop(implicit p: Parameters) extends LazyModule with BindingScope { // c2c_serial_tl1 <> system.d2d_port_ios.get(1) // Connect D2D UCIe - val uciePorts = UciePort.all(p) - val c2c_ucie0 = IO(new edu.berkeley.cs.uciedigital.tilelink.UcieBumpsIO(uciePorts(0).numLanes)) - val c2c_ucie1 = IO(new edu.berkeley.cs.uciedigital.tilelink.UcieBumpsIO(uciePorts(1).numLanes)) + val c2c_ucie0 = IO(new edu.berkeley.cs.uciedigital.tilelink.UcieBumpsIO(p(ChipletRoutingKey).get.ports(0).asInstanceOf[edu.berkeley.cs.uciedigital.tilelink.UcieTLParams].numLanes)) + val c2c_ucie1 = IO(new edu.berkeley.cs.uciedigital.tilelink.UcieBumpsIO(p(ChipletRoutingKey).get.ports(1).asInstanceOf[edu.berkeley.cs.uciedigital.tilelink.UcieTLParams].numLanes)) c2c_ucie0 <> system.d2d_port_ios.get(0) c2c_ucie1 <> system.d2d_port_ios.get(1) } @@ -239,20 +238,18 @@ class TinyIrisConfig(sim: Boolean = false) new testchipip.soc.WithChipletRouting(testchipip.soc.ChipletRoutingParams( routerParams = testchipip.soc.OffchipRouterParams(tableEntries = 4), ports = Seq( - // IrisSystem attaches the UCIe register blocks itself, which needs - // the links UcieComplexPort builds. - UcieComplexPort(edu.berkeley.cs.uciedigital.tilelink.UcieTLParams( + edu.berkeley.cs.uciedigital.tilelink.UcieTLParams( address = 0x200000, managerWhere = SBUS, numLanes = 16, includeDefaultModels = true - )), - UcieComplexPort(edu.berkeley.cs.uciedigital.tilelink.UcieTLParams( + ), + edu.berkeley.cs.uciedigital.tilelink.UcieTLParams( address = 0x208000, managerWhere = SBUS, numLanes = 16, includeDefaultModels = true - )) + ) ))) ++ // 1 serial tilelink port diff --git a/src/UcieComplex.scala b/src/UcieComplex.scala index e937778..985ef43 100644 --- a/src/UcieComplex.scala +++ b/src/UcieComplex.scala @@ -2,10 +2,9 @@ package edu.berkeley.cs.iris import org.chipsalliance.cde.config.Parameters import freechips.rocketchip.diplomacy._ -import freechips.rocketchip.prci._ import freechips.rocketchip.subsystem.{BaseSubsystem, TLBusWrapperLocation} -import freechips.rocketchip.tilelink._ -import edu.berkeley.cs.uciedigital.tilelink.{UcieBumpsIO, UcieTL, UcieTLParams} +import freechips.rocketchip.tilelink.{TLBuffer, TLFragmenter, TLWidthWidget} +import edu.berkeley.cs.uciedigital.tilelink.{UcieChipletLink, UcieTLParams} import testchipip.soc.{ CanHaveChipletRouting, ChipletLinkParams, @@ -30,85 +29,6 @@ class UcieComplex(implicit p: Parameters) override lazy val desiredName = "UcieComplex" } -/** A UCIe link as a chiplet-router port, minus its register blocks. - * - * The router attaches one control node per port to its control bus. A UCIe - * link has two register blocks: the main one, on the clock its PHY makes, and - * the clock register block, which sets that clock up and so runs on the - * chip's. Rather than send one through the router and the other around it, - * this hands the router neither, and [[CanHaveUcieRegisters]] attaches both - * straight to the bus. - * - * Every port this link puts on [[UcieComplex]] is synchronous to the one - * digital clock the router gives it, the register ports included. The main - * register block crosses into the PHY's clock in here, rather than at the - * bus, so the crossing stays inside the block physical design hardens and - * its boundary has no clock the block's constraints don't already know. - * - * Otherwise this is the UCIe repo's `UcieChipletLink`, module name included, - * so the hierarchy physical design sees does not move. - */ -class UcieComplexLink( - val params: UcieTLParams, - sysParams: OffchipSubsystemParams -)(implicit p: Parameters) - extends ChipletLinkWrapper { - override lazy val desiredName = s"UcieChipletLink${params.moduleSuffix}" - - val ucie = LazyModule( - new UcieTL( - params, - sysParams.managerRegion, - sysParams.managerBeatBytes, - sysParams.managerBlockBytes - ) - ) - - // The link's digital clock. Named for the UcieTL node it used to feed - // directly, so that the port it makes on UcieComplex is still - // `auto_d2d_port_ucie_digital_clock_in`, which the block's SDC names. - val ucieDigitalClockNode = ClockSinkNode(Seq(ClockSinkParameters())) - // It is also the chip clock the clock register block runs on: always - // running, which is all that block asks of it, and the same clock the bus - // reaches it on, so its register port needs no crossing. - private val ucieClocks = ClockSourceNode(Seq.fill(2)(ClockSourceParameters())) - ucie.digitalClockNode := ucieClocks - ucie.chipDigitalClockNode := ucieClocks - - val client_node = ucie.clientNode - val manager_node = ucie.managerNode - val control_manager_node: Option[TLRegisterNode] = None - val clock_node = Some(ucieDigitalClockNode) - val top_IO = BundleBridgeSource(() => new UcieBumpsIO(params.numLanes)) - - /** The main register block's port. The block runs on the clock the PHY - * makes, which is unrelated to the digital clock, so the port crosses into - * it: the source half here, the sink inside the block. One entry, like the - * debug module's register crossing, since register accesses need no more. - * The name node keeps the port it makes on UcieComplex called - * `auto_d2d_port_ucie_regs_in`, as it was before the crossing. - */ - val regNode: TLInwardNode = - ucie.regs.crossIn(ucie.regNode).apply(AsynchronousCrossing(depth = 1)) :=* - TLNameNode("ucie_regs") - - /** The clock register block's port, already on the digital clock. */ - val clkRegNode: TLInwardNode = ucie.clkRegNode - - override lazy val module = new LazyRawModuleImp(this) { - val digitalClock = ucieDigitalClockNode.in.head._1 - childClock := digitalClock.clock - childReset := digitalClock.reset - override def provideImplicitClockToLazyChildren = true - - ucieClocks.out.foreach { case (out, _) => - out.clock := digitalClock.clock - out.reset := digitalClock.reset - } - ucie.module.io <> top_IO.out(0)._1 - } -} - /** A chiplet-router port whose UCIe link goes inside the shared [[UcieComplex]]. * * The router builds each port by calling `instantiate` on the port parameters, @@ -126,9 +46,8 @@ case class UcieComplexPort(ucie: UcieTLParams) extends ChipletLinkParams with ChipletLinkWrapperInstantiationLike { def managerBusWhere: TLBusWrapperLocation = ucie.managerBusWhere - // The link gives the router no control node to attach; see - // [[CanHaveUcieRegisters]] for where its registers go instead. - def controlManagerBusWhere: Option[TLBusWrapperLocation] = None + def controlManagerBusWhere: Option[TLBusWrapperLocation] = + ucie.controlManagerBusWhere def instantiate(params: OffchipSubsystemParams, id: Int)(implicit p: Parameters @@ -146,15 +65,16 @@ case class UcieComplexPort(ucie: UcieTLParams) d2d_ports.suggestName("d2d_ports") d2d_ports } - complex { LazyModule(new UcieComplexLink(ucie, params)) } + complex { ucie.instantiate(params, id) } } } object UciePort { - /** The UCIe parameters of a chiplet-router port. */ + /** The UCIe parameters of a chiplet-router port, wrapped or not. */ def unapply(link: ChipletLinkParams): Option[UcieTLParams] = link match { case wrapped: UcieComplexPort => Some(wrapped.ucie) + case direct: UcieTLParams => Some(direct) case _ => None } @@ -164,14 +84,13 @@ object UciePort { .getOrElse(Nil) } -/** Attaches the register blocks of every UCIe link directly to the chiplet +/** Attaches the register port of every UCIe link directly to the chiplet * router's control bus. * - * [[UcieComplexLink]] gives the router no control node, so this is the only - * place they are attached. Both of its register ports are on the link's - * digital clock, which the router already connects the link's data ports to - * the buses on without a crossing, and the link does its own crossing into - * the PHY's clock. + * `UcieChipletLink` gives the router no control node, so this is the only + * place it is attached. The port is synchronous to the link's clock, which + * the router already connects the link's data ports to the buses on without a + * crossing; the crossings into the PHY's clocks are inside `UcieTL`. * * The router keeps its ports to itself, so the links are found by walking the * subsystem's children. Extending [[CanHaveChipletRouting]] makes sure they @@ -180,18 +99,9 @@ object UciePort { trait CanHaveUcieRegisters extends CanHaveChipletRouting { this: BaseSubsystem => p(ChipletRoutingKey).foreach { params => - // The UCIe repo's own link hands its main register block to the router and - // has no way to hand over the clock register block, which would be left - // dangling. - require( - !params.ports.exists(_.isInstanceOf[UcieTLParams]), - "UCIe chiplet-router ports must be wrapped in UcieComplexPort, so that " + - "CanHaveUcieRegisters can attach their register blocks" - ) - - def links(lm: LazyModule): Seq[UcieComplexLink] = + def links(lm: LazyModule): Seq[UcieChipletLink] = lm.getChildren.reverse.flatMap { - case link: UcieComplexLink => Seq(link) + case link: UcieChipletLink => Seq(link) case child => links(child) } val ucieLinks = links(this) @@ -203,16 +113,9 @@ trait CanHaveUcieRegisters extends CanHaveChipletRouting { val cbus = locateTLBusWrapper(params.controlBusWhere) ucieLinks.foreach { link => - Seq( - "control" -> link.regNode, - "clk_control" -> link.clkRegNode - ).foreach { case (suffix, node) => - // The register nodes are as wide as the link's manager bus, which is - // wider than the control bus. - cbus.coupleTo(s"${link.name}_$suffix") { - node := TLWidthWidget(cbus.beatBytes) := TLBuffer() := - TLFragmenter(cbus) := _ - } + cbus.coupleTo(s"${link.name}_control") { + link.regNode := TLWidthWidget(cbus.beatBytes) := TLBuffer() := + TLFragmenter(cbus) := _ } } } diff --git a/test/src/IrisSpec.scala b/test/src/IrisSpec.scala index cb7ab6d..01ca0c0 100644 --- a/test/src/IrisSpec.scala +++ b/test/src/IrisSpec.scala @@ -141,42 +141,21 @@ class TestHarness(nChips: Int, binaryPaths: Seq[Path], plusArgs: Seq[Seq[String] val reset = Input(Bool()) }) - val digitalFreqMHz = 500 - - val digitalClock = Wire(Clock()) - val source = Module(new ClockSourceAtFreqMHz(digitalFreqMHz)) - source.io.power := true.B - source.io.gate := false.B - digitalClock := source.io.clk - - val ucieBypassFreqMHz = 8000 - val ucieBypassClock = Wire(Clock()) - val ucieBypassClockSource = Module(new ClockSourceAtFreqMHz(ucieBypassFreqMHz)) - ucieBypassClockSource.io.power := true.B - ucieBypassClockSource.io.gate := false.B - ucieBypassClock := ucieBypassClockSource.io.clk - - val ucieDigitalBypassFreqMHz = 800 - val ucieDigitalBypassClock = Wire(Clock()) - val ucieDigitalBypassClockSource = Module(new ClockSourceAtFreqMHz(ucieDigitalBypassFreqMHz)) - ucieDigitalBypassClockSource.io.power := true.B - ucieDigitalBypassClockSource.io.gate := false.B - ucieDigitalBypassClock := ucieDigitalBypassClockSource.io.clk - - val ucieSidebandBypassFreqMHz = 800 - val ucieSidebandBypassClock = Wire(Clock()) - val ucieSidebandBypassClockSource = Module(new ClockSourceAtFreqMHz(ucieSidebandBypassFreqMHz)) - ucieSidebandBypassClockSource.io.power := true.B - ucieSidebandBypassClockSource.io.gate := false.B - ucieSidebandBypassClock := ucieSidebandBypassClockSource.io.clk + // A free-running clock from a simulation clock source. + def freeRunningClock(freqMHz: Double): Clock = { + val source = Module(new ClockSourceAtFreqMHz(freqMHz)) + source.io.power := true.B + source.io.gate := false.B + source.io.clk + } + val digitalFreqMHz = 500 + val digitalClock = freeRunningClock(digitalFreqMHz) + val ucieBypassClock = freeRunningClock(8000) + val ucieDigitalBypassClock = freeRunningClock(800) + val ucieSidebandBypassClock = freeRunningClock(800) // The clocking tile's PLL reference, as in the UCIe repo's own harness. - val ucieRefFreqMHz = 100 - val ucieRefClock = Wire(Clock()) - val ucieRefClockSource = Module(new ClockSourceAtFreqMHz(ucieRefFreqMHz)) - ucieRefClockSource.io.power := true.B - ucieRefClockSource.io.gate := false.B - ucieRefClock := ucieRefClockSource.io.clk + val ucieRefClock = freeRunningClock(100) implicit def view[A <: Data, B <: Data] : DataView[testchipip.tsi.TSIIO, TSIIO] = diff --git a/ucie b/ucie index 3fbc560..97d5031 160000 --- a/ucie +++ b/ucie @@ -1 +1 @@ -Subproject commit 3fbc560b3c6060a69b1052603d8c2105e40edcae +Subproject commit 97d5031fd73174964d9b82718f00b8afb6b703a9 From 489bd4588bea3c6704c5ddd36946896dff453f58 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Tue, 6 Oct 2026 18:17:48 -0700 Subject: [PATCH 3/7] remove unecessary mixins --- src/Iris.scala | 15 ++++++++++++++- src/TinyIris.scala | 14 ++++++++++++++ src/UcieComplex.scala | 40 ++++++++++++---------------------------- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/Iris.scala b/src/Iris.scala index 5c34fa9..aa34a97 100644 --- a/src/Iris.scala +++ b/src/Iris.scala @@ -29,6 +29,7 @@ import freechips.rocketchip.subsystem._ import freechips.rocketchip.prci._ import freechips.rocketchip.devices.debug._ import freechips.rocketchip.devices.tilelink.BootROMLocated +import freechips.rocketchip.tilelink.{TLBuffer, TLFragmenter, TLWidthWidget} import freechips.rocketchip.util._ import sifive.blocks.inclusivecache.{InclusiveCachePortParameters} @@ -44,7 +45,6 @@ class JTAGChipIO(hasReset: Boolean) extends Bundle { class IrisSystem(implicit p: Parameters) extends edu.berkeley.cs.chippy.ChippySystem with testchipip.soc.CanHaveChipletRouting - with CanHaveUcieRegisters // UCIe register blocks, attached around the router with testchipip.soc.CanHaveSubsystemInjectors // Enables the subsystem injector API with testchipip.soc.CanHaveSwitchableOffchipBus // Enables optional off-chip-bus with interface-switch with testchipip.serdes.CanHavePeripheryTLSerial @@ -64,6 +64,19 @@ class IrisTop(implicit p: Parameters) extends LazyModule with BindingScope { .fixedClockNode def debugClockBundle = debugClockSinkNode.in.head._1 + // Each UCIe link's register port, on the chiplet router's control bus. The + // router cannot attach these itself: each is a crossbar in front of UcieTL's + // register blocks, not the bare register node it takes. + p(ChipletRoutingKey).foreach { routing => + val cbus = system.locateTLBusWrapper(routing.controlBusWhere) + UcieChipletLinks(system).foreach { link => + cbus.coupleTo(s"${link.name}_control") { + link.regNode := TLWidthWidget(cbus.beatBytes) := TLBuffer() := + TLFragmenter(cbus) := _ + } + } + } + override lazy val module = new IrisTopImpl class IrisTopImpl extends LazyRawModuleImp(this) with DontTouch { val io = IO(new Bundle { diff --git a/src/TinyIris.scala b/src/TinyIris.scala index 94a0de6..2e437c1 100644 --- a/src/TinyIris.scala +++ b/src/TinyIris.scala @@ -29,6 +29,7 @@ import freechips.rocketchip.subsystem._ import freechips.rocketchip.prci._ import freechips.rocketchip.devices.debug._ import freechips.rocketchip.devices.tilelink.BootROMLocated +import freechips.rocketchip.tilelink.{TLBuffer, TLFragmenter, TLWidthWidget} import freechips.rocketchip.util._ import sifive.blocks.inclusivecache.{InclusiveCachePortParameters} @@ -44,6 +45,19 @@ class TinyIrisTop(implicit p: Parameters) extends LazyModule with BindingScope { .fixedClockNode def debugClockBundle = debugClockSinkNode.in.head._1 + // Each UCIe link's register port, on the chiplet router's control bus. The + // router cannot attach these itself: each is a crossbar in front of UcieTL's + // register blocks, not the bare register node it takes. + p(ChipletRoutingKey).foreach { routing => + val cbus = system.locateTLBusWrapper(routing.controlBusWhere) + UcieChipletLinks(system).foreach { link => + cbus.coupleTo(s"${link.name}_control") { + link.regNode := TLWidthWidget(cbus.beatBytes) := TLBuffer() := + TLFragmenter(cbus) := _ + } + } + } + override lazy val module = new IrisTopImpl class IrisTopImpl extends LazyRawModuleImp(this) with DontTouch { val io = IO(new Bundle { diff --git a/src/UcieComplex.scala b/src/UcieComplex.scala index 985ef43..7c59299 100644 --- a/src/UcieComplex.scala +++ b/src/UcieComplex.scala @@ -2,11 +2,9 @@ package edu.berkeley.cs.iris import org.chipsalliance.cde.config.Parameters import freechips.rocketchip.diplomacy._ -import freechips.rocketchip.subsystem.{BaseSubsystem, TLBusWrapperLocation} -import freechips.rocketchip.tilelink.{TLBuffer, TLFragmenter, TLWidthWidget} +import freechips.rocketchip.subsystem.TLBusWrapperLocation import edu.berkeley.cs.uciedigital.tilelink.{UcieChipletLink, UcieTLParams} import testchipip.soc.{ - CanHaveChipletRouting, ChipletLinkParams, ChipletLinkWrapper, ChipletLinkWrapperInstantiationLike, @@ -84,39 +82,25 @@ object UciePort { .getOrElse(Nil) } -/** Attaches the register port of every UCIe link directly to the chiplet - * router's control bus. +/** The UCIe links the chiplet router built under `system`, in port order. * - * `UcieChipletLink` gives the router no control node, so this is the only - * place it is attached. The port is synchronous to the link's clock, which - * the router already connects the link's data ports to the buses on without a - * crossing; the crossings into the PHY's clocks are inside `UcieTL`. - * - * The router keeps its ports to itself, so the links are found by walking the - * subsystem's children. Extending [[CanHaveChipletRouting]] makes sure they - * exist by the time this runs. + * `UcieChipletLink` gives the router no control node, so whoever builds the + * system attaches each link's `regNode` to a control bus. The router keeps its + * ports to itself, so they are found by walking the module tree. */ -trait CanHaveUcieRegisters extends CanHaveChipletRouting { - this: BaseSubsystem => - p(ChipletRoutingKey).foreach { params => +object UcieChipletLinks { + def apply(system: LazyModule)(implicit p: Parameters): Seq[UcieChipletLink] = { def links(lm: LazyModule): Seq[UcieChipletLink] = lm.getChildren.reverse.flatMap { case link: UcieChipletLink => Seq(link) case child => links(child) } - val ucieLinks = links(this) + val found = links(system) require( - ucieLinks.size == UciePort.all(p).size, - s"found ${ucieLinks.size} UCIe links under the subsystem, but the " + - s"chiplet router has ${UciePort.all(p).size} UCIe ports" + found.size == UciePort.all(p).size, + s"found ${found.size} UCIe links under the system, but the chiplet " + + s"router has ${UciePort.all(p).size} UCIe ports" ) - - val cbus = locateTLBusWrapper(params.controlBusWhere) - ucieLinks.foreach { link => - cbus.coupleTo(s"${link.name}_control") { - link.regNode := TLWidthWidget(cbus.beatBytes) := TLBuffer() := - TLFragmenter(cbus) := _ - } - } + found } } From e9bd006d2cd94d601448e27b7971addb357e6083 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Thu, 8 Oct 2026 09:29:31 -0700 Subject: [PATCH 4/7] Directly retrieve register nodes from router --- src/Iris.scala | 8 +++++--- src/TinyIris.scala | 8 +++++--- src/UcieComplex.scala | 25 +------------------------ testchipip | 2 +- 4 files changed, 12 insertions(+), 31 deletions(-) diff --git a/src/Iris.scala b/src/Iris.scala index aa34a97..b5ae29f 100644 --- a/src/Iris.scala +++ b/src/Iris.scala @@ -30,6 +30,7 @@ import freechips.rocketchip.prci._ import freechips.rocketchip.devices.debug._ import freechips.rocketchip.devices.tilelink.BootROMLocated import freechips.rocketchip.tilelink.{TLBuffer, TLFragmenter, TLWidthWidget} +import edu.berkeley.cs.uciedigital.tilelink.UcieChipletLink import freechips.rocketchip.util._ import sifive.blocks.inclusivecache.{InclusiveCachePortParameters} @@ -67,9 +68,10 @@ class IrisTop(implicit p: Parameters) extends LazyModule with BindingScope { // Each UCIe link's register port, on the chiplet router's control bus. The // router cannot attach these itself: each is a crossbar in front of UcieTL's // register blocks, not the bare register node it takes. - p(ChipletRoutingKey).foreach { routing => - val cbus = system.locateTLBusWrapper(routing.controlBusWhere) - UcieChipletLinks(system).foreach { link => + system.d2d_ports.foreach { ports => + val cbus = system.locateTLBusWrapper(p(ChipletRoutingKey).get.controlBusWhere) + val links = ports.collect { case link: UcieChipletLink => link } + links.foreach { link => cbus.coupleTo(s"${link.name}_control") { link.regNode := TLWidthWidget(cbus.beatBytes) := TLBuffer() := TLFragmenter(cbus) := _ diff --git a/src/TinyIris.scala b/src/TinyIris.scala index 2e437c1..6243c35 100644 --- a/src/TinyIris.scala +++ b/src/TinyIris.scala @@ -30,6 +30,7 @@ import freechips.rocketchip.prci._ import freechips.rocketchip.devices.debug._ import freechips.rocketchip.devices.tilelink.BootROMLocated import freechips.rocketchip.tilelink.{TLBuffer, TLFragmenter, TLWidthWidget} +import edu.berkeley.cs.uciedigital.tilelink.UcieChipletLink import freechips.rocketchip.util._ import sifive.blocks.inclusivecache.{InclusiveCachePortParameters} @@ -48,9 +49,10 @@ class TinyIrisTop(implicit p: Parameters) extends LazyModule with BindingScope { // Each UCIe link's register port, on the chiplet router's control bus. The // router cannot attach these itself: each is a crossbar in front of UcieTL's // register blocks, not the bare register node it takes. - p(ChipletRoutingKey).foreach { routing => - val cbus = system.locateTLBusWrapper(routing.controlBusWhere) - UcieChipletLinks(system).foreach { link => + system.d2d_ports.foreach { ports => + val cbus = system.locateTLBusWrapper(p(ChipletRoutingKey).get.controlBusWhere) + val links = ports.collect { case link: UcieChipletLink => link } + links.foreach { link => cbus.coupleTo(s"${link.name}_control") { link.regNode := TLWidthWidget(cbus.beatBytes) := TLBuffer() := TLFragmenter(cbus) := _ diff --git a/src/UcieComplex.scala b/src/UcieComplex.scala index 7c59299..820782f 100644 --- a/src/UcieComplex.scala +++ b/src/UcieComplex.scala @@ -3,7 +3,7 @@ package edu.berkeley.cs.iris import org.chipsalliance.cde.config.Parameters import freechips.rocketchip.diplomacy._ import freechips.rocketchip.subsystem.TLBusWrapperLocation -import edu.berkeley.cs.uciedigital.tilelink.{UcieChipletLink, UcieTLParams} +import edu.berkeley.cs.uciedigital.tilelink.UcieTLParams import testchipip.soc.{ ChipletLinkParams, ChipletLinkWrapper, @@ -81,26 +81,3 @@ object UciePort { .map(_.ports.collect { case UciePort(u) => u }) .getOrElse(Nil) } - -/** The UCIe links the chiplet router built under `system`, in port order. - * - * `UcieChipletLink` gives the router no control node, so whoever builds the - * system attaches each link's `regNode` to a control bus. The router keeps its - * ports to itself, so they are found by walking the module tree. - */ -object UcieChipletLinks { - def apply(system: LazyModule)(implicit p: Parameters): Seq[UcieChipletLink] = { - def links(lm: LazyModule): Seq[UcieChipletLink] = - lm.getChildren.reverse.flatMap { - case link: UcieChipletLink => Seq(link) - case child => links(child) - } - val found = links(system) - require( - found.size == UciePort.all(p).size, - s"found ${found.size} UCIe links under the system, but the chiplet " + - s"router has ${UciePort.all(p).size} UCIe ports" - ) - found - } -} diff --git a/testchipip b/testchipip index fc8013c..2a109c2 160000 --- a/testchipip +++ b/testchipip @@ -1 +1 @@ -Subproject commit fc8013c30e098f46402f1c98543b9d4b7b9b36d2 +Subproject commit 2a109c2f3fa7f28336a6daea106049f9e4045df5 From 208d87f064234a64a7e4853cc6db69e030f0a5bc Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Tue, 13 Oct 2026 14:32:33 -0700 Subject: [PATCH 5/7] fix tests --- software/ring-hello.c | 2 ++ software/router.c | 2 ++ software/ucie-loopback.c | 2 ++ software/ucie-vec-memcpy.c | 2 ++ software/ucie.h | 63 ++++++++++++++++++++++++++++++-------- test/src/Utils.scala | 18 +++++++++-- ucie | 2 +- 7 files changed, 75 insertions(+), 16 deletions(-) diff --git a/software/ring-hello.c b/software/ring-hello.c index 2095f7c..41a0ea6 100644 --- a/software/ring-hello.c +++ b/software/ring-hello.c @@ -20,8 +20,10 @@ int main(void) { setup_ucie(UCIE0_REG_BASE); reg_write64(UCIE0_REG_BASE + UCIE_MAINBAND_MODE, UCIE_BAND_MODE_TL); + reset_datapath(UCIE0_REG_BASE); setup_ucie(UCIE1_REG_BASE); reg_write64(UCIE1_REG_BASE + UCIE_MAINBAND_MODE, UCIE_BAND_MODE_TL); + reset_datapath(UCIE1_REG_BASE); int next = (chip_id % 4) + 1; int prev = ((chip_id + 2) % 4) + 1; diff --git a/software/router.c b/software/router.c index e4d19b4..1933b04 100644 --- a/software/router.c +++ b/software/router.c @@ -50,6 +50,7 @@ int main(void) { if (chip_id == 1) { setup_ucie(UCIE0_REG_BASE); reg_write64(UCIE0_REG_BASE + UCIE_MAINBAND_MODE, UCIE_BAND_MODE_TL); + reset_datapath(UCIE0_REG_BASE); printf("Set up UCIe 0 for chip 1\n"); program_router(0, 2, 0); // Chip 1 UCIe0 -> Chip 2 UCIe1 rw_mem(OFFCHIP_OFFSET * 2); @@ -57,6 +58,7 @@ int main(void) { } else { setup_ucie(UCIE1_REG_BASE); reg_write64(UCIE1_REG_BASE + UCIE_MAINBAND_MODE, UCIE_BAND_MODE_TL); + reset_datapath(UCIE1_REG_BASE); printf("Set up UCIe 1 for chip 2\n"); program_router(0, 1, 1); // Chip 2 UCIe1 -> Chip 1 UCIe0 rw_mem(OFFCHIP_OFFSET * 1); diff --git a/software/ucie-loopback.c b/software/ucie-loopback.c index be5ad6f..22fd3e0 100644 --- a/software/ucie-loopback.c +++ b/software/ucie-loopback.c @@ -54,9 +54,11 @@ int main(void) { printf("Setting up UCIe0\n"); setup_ucie(UCIE0_REG_BASE); reg_write64(UCIE0_REG_BASE + UCIE_MAINBAND_MODE, UCIE_BAND_MODE_TL); + reset_datapath(UCIE0_REG_BASE); printf("Setting up UCIe1\n"); setup_ucie(UCIE1_REG_BASE); reg_write64(UCIE1_REG_BASE + UCIE_MAINBAND_MODE, UCIE_BAND_MODE_TL); + reset_datapath(UCIE1_REG_BASE); printf("Testing UCIe at port 0\n"); program_router(0, 1, 0); diff --git a/software/ucie-vec-memcpy.c b/software/ucie-vec-memcpy.c index 7500c9e..c008fd3 100644 --- a/software/ucie-vec-memcpy.c +++ b/software/ucie-vec-memcpy.c @@ -57,6 +57,7 @@ void __main(void) // ============================================================ setup_ucie(UCIE0_REG_BASE); reg_write64(UCIE0_REG_BASE + UCIE_MAINBAND_MODE, UCIE_BAND_MODE_TL); + reset_datapath(UCIE0_REG_BASE); program_router(0, 2, 0); uint64_t peer_chip_id = 2; @@ -90,6 +91,7 @@ void __main(void) // ============================================================ setup_ucie(UCIE1_REG_BASE); reg_write64(UCIE1_REG_BASE + UCIE_MAINBAND_MODE, UCIE_BAND_MODE_TL); + reset_datapath(UCIE1_REG_BASE); program_router(0, 1, 1); *done_flag = 0; diff --git a/software/ucie.h b/software/ucie.h index bd29c98..286df8c 100644 --- a/software/ucie.h +++ b/software/ucie.h @@ -191,11 +191,11 @@ #define UCIE_MAIN_CLK_SEL 0x10000ULL #define UCIE_PLL8_EN 0x10008ULL #define UCIE_PLL12_EN 0x10010ULL -#define UCIE_PLL16_EN 0x10018ULL +#define UCIE_X2_EN 0x10018ULL #define UCIE_TX_CLK_DIV 0x10020ULL #define UCIE_TX_CLK_PHASE 0x10028ULL -#define UCIE_DIG_CLK_DIV 0x10030ULL -#define UCIE_DIG_CLK_BYPASS_EN 0x10038ULL +#define UCIE_PHY_TEST_CLK_DIV 0x10030ULL +#define UCIE_PHY_TEST_CLK_BYPASS_EN 0x10038ULL #define UCIE_CLK_PHASE_SEL 0x10040ULL #define UCIE_CLK_GATE_EN 0x10048ULL #define UCIE_RX_CLK_GATE_EN 0x10050ULL @@ -233,15 +233,19 @@ #define UCIE_DEFAULT_VALID 0xf0f0f0fULL #define UCIE_DEFAULT_TRACK 0x55555555ULL #define UCIE_ENABLE_DRIVER_CTL 0x3ffeULL -#define UCIE_ENABLE_TX_CTL 0x1fff000000000ULL +#define UCIE_ENABLE_TX_CTL 0x3fffff000000000ULL #define UCIE_TX_CTL_DELAY_LSB 0x0ULL +#define UCIE_TX_CTL_ENP_LSB 0x31ULL +#define UCIE_TX_CTL_ENN_LSB 0x28ULL +#define UCIE_TX_CTL_ENP_EQ_LSB 0x24ULL +#define UCIE_TX_CTL_ENN_EQ_LSB 0x20ULL #define UCIE_TRAIN_LFSR_SEED 0x123456789abcdefULL #define UCIE_TRAIN_DATA_LANES 0x10ULL #define UCIE_TRAIN_VALID_LANE 0x10ULL #define UCIE_TRAIN_SCORE_LANES 0x11ULL #define UCIE_RATE_CFG_MAIN_CLK_SEL_LSB 0x8ULL #define UCIE_RATE_CFG_TX_CLK_DIV_LSB 0x6ULL -#define UCIE_RATE_CFG_DIG_CLK_DIV_LSB 0x3ULL +#define UCIE_RATE_CFG_PHY_TEST_CLK_DIV_LSB 0x3ULL #define UCIE_TRAIN_EYE_FINE_POINTS 0x4ULL #define UCIE_TRAIN_EYE_FINE_STEP 0x10ULL #define UCIE_TRAIN_EYE_DIVS 0x3ULL @@ -258,9 +262,12 @@ #define UCIE_CLK_UNGATE_SRC_DELAY 0x2ULL // === Helper functions === -static inline void reset_fsms(uintptr_t base) { +static inline void reset_datapath(uintptr_t base) { reg_write64(base + UCIE_TX_DATAPATH_RST, 0x1ULL); reg_write64(base + UCIE_RX_DATAPATH_RST, 0x1ULL); +} +static inline void reset_fsms(uintptr_t base) { + reset_datapath(base); reg_write64(base + UCIE_TX_FSM_RST, 0x1ULL); reg_write64(base + UCIE_RX_FSM_RST, 0x1ULL); reg_write64(base + UCIE_DEBUG_TX_FSM_RST, 0x1ULL); @@ -280,8 +287,8 @@ static inline void write_rxctl(uintptr_t base, uint64_t lane, uint64_t ofs, uint static inline void set_clk_gate(uintptr_t base, uint64_t en) { reg_write64(base + UCIE_CLK_GATE_EN, en); } -static inline void set_rate_cfg(uintptr_t base, uint64_t rate, uint64_t main_sel, uint64_t tx_div, uint64_t dig_div, uint64_t pll_en) { - reg_write64(base + UCIE_RATE_CFG + rate * UCIE_RATE_CFG_WIDTH, ((main_sel << UCIE_RATE_CFG_MAIN_CLK_SEL_LSB) | (tx_div << UCIE_RATE_CFG_TX_CLK_DIV_LSB) | (dig_div << UCIE_RATE_CFG_DIG_CLK_DIV_LSB) | pll_en)); +static inline void set_rate_cfg(uintptr_t base, uint64_t rate, uint64_t main_sel, uint64_t tx_div, uint64_t phytest_div, uint64_t pll_en) { + reg_write64(base + UCIE_RATE_CFG + rate * UCIE_RATE_CFG_WIDTH, ((main_sel << UCIE_RATE_CFG_MAIN_CLK_SEL_LSB) | (tx_div << UCIE_RATE_CFG_TX_CLK_DIV_LSB) | (phytest_div << UCIE_RATE_CFG_PHY_TEST_CLK_DIV_LSB) | pll_en)); } static inline void set_freq_sel_auto(uintptr_t base, uint64_t en) { reg_write64(base + UCIE_FREQ_SEL_AUTO_EN, en); @@ -324,12 +331,12 @@ static inline void set_tx_phase(uintptr_t base, uint64_t half_cycles) { reg_write64(base + UCIE_TX_CLK_PHASE, half_cycles); reg_write64(base + UCIE_CLK_GATE_EN, 0x1ULL); } -static inline void use_internal_clk(uintptr_t base, uint64_t dig_div) { +static inline void use_internal_clk(uintptr_t base, uint64_t phytest_div) { reg_write64(base + UCIE_PLL8_EN, 0x1ULL); reg_write64(base + UCIE_PLL12_EN, 0x1ULL); - reg_write64(base + UCIE_PLL16_EN, 0x1ULL); - reg_write64(base + UCIE_DIG_CLK_DIV, dig_div); - reg_write64(base + UCIE_DIG_CLK_BYPASS_EN, 0x0ULL); + reg_write64(base + UCIE_X2_EN, 0x1ULL); + reg_write64(base + UCIE_PHY_TEST_CLK_DIV, phytest_div); + reg_write64(base + UCIE_PHY_TEST_CLK_BYPASS_EN, 0x0ULL); apply_clk_cfg(base); } static inline void set_rx_clk_gate(uintptr_t base, uint64_t en) { @@ -364,10 +371,40 @@ static inline void setup_ucie(uintptr_t base) { set_clk_gate(base, 0x0ULL); reset_dividers(base); set_clk_gate(base, 0x1ULL); - reset_fsms(base); reg_write64(base + UCIE_CONTROLLER_SEL, UCIE_CONTROLLER_SEL_PHYTEST); reg_write64(base + UCIE_MAINBAND_MODE, UCIE_BAND_MODE_MANUAL); reg_write64(base + UCIE_SIDEBAND_MODE, UCIE_BAND_MODE_MANUAL); + reset_fsms(base); +} + +static inline void setup_ucie_digital(uintptr_t base) { + set_clk_gate(base, 0x0ULL); + set_global_delay(base, 0x1dULL); + reset_dividers(base); + set_clk_gate(base, 0x1ULL); + for (int lane = 0; lane < 20; lane++) { + write_txctl(base, lane, UCIE_TXCTL_TILE_OFS, UCIE_ENABLE_TX_CTL); + } + for (int lane = 0; lane < 18; lane++) { + write_rxctl(base, lane, UCIE_RXCTL_ZEN_OFS, 0x1ULL); + write_rxctl(base, lane, UCIE_RXCTL_ZCTL_OFS, 0x0ULL); + } + reg_write64(base + UCIE_CONTROLLER_SEL, UCIE_CONTROLLER_SEL_UCIE); + reset_datapath(base); + { + uint64_t linkControl; + linkControl = reg_read64(base + UCIE_RAW_FORMAT_ENABLE); + reg_write64(base + UCIE_RAW_FORMAT_ENABLE, linkControl | 0x401ULL); + } + reg_write64(base + UCIE_UNCORR_ERR_MASK, 0x0ULL); + reg_write64(base + UCIE_CORR_ERR_MASK, 0x0ULL); + while (1) { + uint64_t linkStatus; + linkStatus = reg_read64(base + UCIE_RAW_FORMAT_ENABLED); + if ((linkStatus >> 0xfULL) & 0x1ULL) { + break; + } + } } static inline void seed_lfsrs(uintptr_t base) { diff --git a/test/src/Utils.scala b/test/src/Utils.scala index f58f577..c14f89f 100644 --- a/test/src/Utils.scala +++ b/test/src/Utils.scala @@ -52,7 +52,7 @@ verilator \\ -CFLAGS "$$CXXFLAGS -O3 -std=c++17 -DVERILATOR -I$$RISCV/include" \\ -LDFLAGS "$$LDFLAGS -L$$RISCV/lib -Wl,-rpath,$$RISCV/lib -lriscv -lfesvr" \\ -F ${sourceFilesList.toString} -script -f -c "./simulation >(spike-dasm > simulation.out)" simulation.log +script -e -f -c "./simulation >(spike-dasm > simulation.out)" simulation.log """ ) path.toIO.setExecutable(true) @@ -89,7 +89,7 @@ vcs \\ +define+layer$$Verification$$Cover$$Temporal \\ +define+VCS +define+FSDB +define+RANDOMIZE_MEM_INIT +define+RANDOMIZE_REG_INIT +define+RANDOMIZE_GARBAGE_ASSIGN +define+RANDOMIZE_INVALID_ASSIGN$debugCompileFlags \\ -o simulation -Mdir=vcs-sources -script -f -c "./simulation +permissive +vcs+thread+16 +dramsim +dramsim_ini_dir=${dramsim_ini.toString}${loadmem.map(p => s" +loadmem=${p.toString}").getOrElse("")}$debugRuntimeFlag +permissive-off placeholder-binary >(spike-dasm > simulation.out)" simulation.log +script -e -f -c "./simulation +permissive +vcs+thread+16 +dramsim +dramsim_ini_dir=${dramsim_ini.toString}${loadmem.map(p => s" +loadmem=${p.toString}").getOrElse("")}$debugRuntimeFlag +permissive-off placeholder-binary >(spike-dasm > simulation.out)" simulation.log """ ) path.toIO.setExecutable(true) @@ -162,5 +162,19 @@ script -f -c "./simulation +permissive +vcs+thread+16 +dramsim +dramsim_ini_dir= "/bin/bash", simScript ).call(stdout = os.Inherit, stderr = os.Inherit, cwd = simDir) + + // The simulator's exit status does not say whether the test passed: VCS + // ends with status 0 after a `$fatal`, whether from SimTop's timeout or + // from a failed assertion. SimTop prints its success line only when the + // chips report success, so the log decides it. + val simLog = os.read(simDir / "simulation.log") + val failure = + Seq("Assertion failed", "Fatal:", "Test timed out!").find(simLog.contains) + assert( + failure.isEmpty && simLog.contains("Test completed successfully."), + failure.fold("simulation ended without reporting success")(f => + s"simulation log reports \"$f\"" + ) + s"; see ${simDir / "simulation.log"}" + ) } } diff --git a/ucie b/ucie index 97d5031..4db6449 160000 --- a/ucie +++ b/ucie @@ -1 +1 @@ -Subproject commit 97d5031fd73174964d9b82718f00b8afb6b703a9 +Subproject commit 4db6449716976a79765828a6dc27547c6a0c6366 From 63f7fad60738584abd0f5b5f01b590eb65fc0802 Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Tue, 13 Oct 2026 14:35:22 -0700 Subject: [PATCH 6/7] bump testchipip --- testchipip | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testchipip b/testchipip index 2a109c2..86c9a82 160000 --- a/testchipip +++ b/testchipip @@ -1 +1 @@ -Subproject commit 2a109c2f3fa7f28336a6daea106049f9e4045df5 +Subproject commit 86c9a824fd42b854526018f9111087b0f3d1d31c From 6efb58db7497d07188856646b028472aa1d90bdd Mon Sep 17 00:00:00 2001 From: Rohan Kumar Date: Tue, 13 Oct 2026 15:40:26 -0700 Subject: [PATCH 7/7] update tests to assume 1 shuttle tile --- software/tacit-always.c | 3 ++- software/ucie-vec-memcpy.c | 8 ++++---- src/Iris.scala | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/software/tacit-always.c b/software/tacit-always.c index 0037349..3b597cf 100644 --- a/software/tacit-always.c +++ b/software/tacit-always.c @@ -36,7 +36,8 @@ static void branch_workload(void) int main(void) { - for (unsigned tile = 0; tile < 3; tile++) { + // One encoder per tile: the Shuttle tile and Rocket. + for (unsigned tile = 0; tile < 2; tile++) { tacit_select_always(tile); } branch_workload(); diff --git a/software/ucie-vec-memcpy.c b/software/ucie-vec-memcpy.c index c008fd3..afef76d 100644 --- a/software/ucie-vec-memcpy.c +++ b/software/ucie-vec-memcpy.c @@ -1,7 +1,7 @@ // two-chip UCIe RVV memcpy test // -// hart 0/1 are the Shuttle/OPU cores -// hart 2 is the Rocket tile with Saturn DMA +// hart 0 is the Shuttle/OPU core +// hart 1 is the Rocket tile with Saturn DMA // BENCH_HART_ID picks which hart runs this test #include @@ -24,10 +24,10 @@ #define COPY_BYTES 256 #endif -// Which hart runs the test. Hart 2 is the Saturn-DMA-only rocket tile added +// Which hart runs the test. Hart 1 is the Saturn-DMA-only rocket tile added // by IrisConfig's "Saturn DMA" line. Tunable: -DBENCH_HART_ID=. #ifndef BENCH_HART_ID -#define BENCH_HART_ID 2 +#define BENCH_HART_ID 1 #endif // Aligned to the 32-byte beat so the natural-alignment path is exercised. diff --git a/src/Iris.scala b/src/Iris.scala index 8d7385c..85abb95 100644 --- a/src/Iris.scala +++ b/src/Iris.scala @@ -146,7 +146,7 @@ class IrisTop(implicit p: Parameters) extends LazyModule with BindingScope { // IrisConfig and IrisDryRunConfig differ only in // * the SBUS NoC node mapping, and // * which optional blocks are present: the Rocket Saturn DMA vector unit, the -// two Shuttle tiles with their Saturn OPU, and the UCIe chiplet router. +// Shuttle tile with its Saturn OPU, and the UCIe chiplet router. // Everything else is identical and lives in the fragments below. // // These are several fragments rather than one block because CDE composition is @@ -156,8 +156,8 @@ class IrisTop(implicit p: Parameters) extends LazyModule with BindingScope { // sit to the LEFT of WithIrisRocketCore. // * The Shuttle fragments must sit to the RIGHT of WithIrisRocketCore. Tile // ids are handed out from `up(NumTiles)`, so this ordering is what makes the -// Shuttle tiles harts 0-1 and Rocket hart 2 -- which the NoC mapping keys -// off by name ("Core 0 ICache", "Core 2 DCache"). +// Shuttle tile hart 0 and Rocket hart 1 -- which the NoC mapping keys +// off by name ("Core 0 ICache", "Core 1 DCache"). // ============================================================================ /** SBUS NoC parameters. Only the node mapping differs between the two chips;