|
|
Experimental Physics and
| ||||||||||||||||
|
|
Hi Michael and Andrew, Please find the summary below: On MVME2100 (MPC8240 + Tundra Universe II CA91C142), ANY software write to the Universe II's outbound (PCI-to-VME) window registers (LSI CTL, BS, BD, or TO) causes all subsequent VME coupled reads to fail with S_TA=1 (PCI Signaled Target Abort) and VERR=0 (no VME cycle generated). Only the PPCBug firmware's factory power-on configuration produces working VME cycles. The RTEMS BSP's normal boot sequence — vmeUniverseReset() followed by BSP_VMEOutboundPortCfg() — triggers this failure. In more details: The Universe II claims PCI transactions (asserts DEVSEL#) but signals Target Abort without ever generating a VME bus cycle. The chip appears to have hidden internal state in its outbound window state machine that is not captured in the register values and cannot be restored by software. The workaround: We override BSP_vme_config() (weak symbol) and wrap vmeUniverseReset() and BSP_vme2local_adrs() via the --wrap linker option: - __wrap_vmeUniverseReset(): no-op, preserves PPCBug config - BSP_vme_config(): calls BSP_VMEInit() (sets theOps), configures DBAT1 for PPCBug's PCI range (0xA0000000), installs IRQ manager. Does NOT call BSP_VMEOutboundPortCfg(). - __wrap_BSP_vme2local_adrs(): hardcoded VME-to-local address translation for PPCBug's window layout (the Universe II driver's xlateAddr cannot reverse PPCBug's translation offsets due to 32-bit wrapping, and the AM code doesn't match because PPCBug uses non-privileged mode) Makefile: USR_LDFLAGS_RTEMS += -Wl,--wrap=vmeUniverseReset USR_LDFLAGS_RTEMS += -Wl,--wrap=BSP_vme2local_adrs Board details: MVME2101, MPC8240 @200MHz, 32MB SDRAM, Bus Clock 67MHz, PPCBug ENV at factory defaults, Universe II at PCI BAR0=0xFCDFF000, RTEMS 6.0.0, EPICS Base 7.0.10. We don't know if this is specific to our board/revision or affects all MVME2100 systems. If anyone has successfully used vmeUniverseReset() + BSP_VMEOutboundPortCfg() on MVME2100 with RTEMS 6, we'd be interested to hear about it. ------------------------------------------------------------------------------------------------------------------ for our OMS based project we have added the following files in the omsApp/src directory: file: Makefile : oms_DBD += motorRecord.dbd oms_DBD += devOms.dbd : oms_DBD += omsSetupRegister.dbd oms_DBD += epicsvme.dbd oms_DBD += epicspci.dbd oms_SRCS += omsSetupRegister.cpp ifeq ($(T_A),RTEMS-mvme2100) USR_LDFLAGS += -Wl,--wrap=vmeUniverseReset USR_LDFLAGS += -Wl,--wrap=BSP_vme2local_adrs endif -------------------------------------------------------------------- file: omsSetupRegister.dbd registrar(omsSetupRegistrar) variable(drvOMSdebug) ----------------------------------------------------------------- file: omsSetupRegister.cpp /* * omsSetupRegister.cpp * * 1) IOCsh registration for omsSetup() (missing from upstream drvOms.cc). * 2) MVME2100 VME bridge fix for RTEMS 6: * - Override BSP_vme_config() (weak symbol) to prevent BSP from * reprogramming the Universe II outbound windows. ANY write to * the outbound LSI registers (CTL, BS, BD, or TO) destroys hidden * internal state and causes S_TA=1 (Signaled Target Abort) on all * subsequent VME coupled reads. Only PPCBug's factory power-on * configuration produces working VME cycles. * - Wrap vmeUniverseReset() to a no-op (it disables all windows). * - Wrap BSP_vme2local_adrs() to provide address translation for * PPCBug's window layout (the Universe II driver's xlateAddr * cannot reverse PPCBug's translation offsets due to 32-bit wrap). * - Set DBAT1 to cover PPCBug's PCI address range (0xA0000000). * 3) Diagnostic IOCsh commands: vmeProbeTest, vmeScanA16 */ #include <stdio.h> #include <string.h> #include <iocsh.h> #include <epicsExport.h> #include <devLib.h> #include <epicsMMIO.h> /* RTN_STATUS is defined in motor.h (typedef enum RTN_VALUES) */ #include "motor.h" /* Forward-declare omsSetup from drvOms.cc */ extern RTN_STATUS omsSetup(int num_cards, unsigned addrs, unsigned vector, int int_level, int scan_rate); /* ================================================================== */ /* MVME2100 VME bridge fix (RTEMS 6 / Universe II) */ /* ================================================================== */ #if defined(__rtems__) && defined(__PPC__) extern "C" { #include <bsp.h> /* defines mot_ppc_mvme2100 via bspopts.h — required before VMEConfig.h for correct BSP_VME_BAT_IDX */ #include <libcpu/bat.h> #include <libcpu/io.h> #include <bsp/VMEConfig.h> #include <bsp/VME.h> } extern "C" { /* * __wrap_vmeUniverseReset — linked via -Wl,--wrap=vmeUniverseReset * * Prevents the BSP from resetting the Universe II during BSP_VMEInit(). * vmeUniverseReset() disables all outbound/inbound windows, clears * interrupt routing, and sets CRT/U2SPEC — destroying PPCBug's config. */ void __wrap_vmeUniverseReset(void) { printf("*** vmeUniverseReset: WRAPPED — preserving PPCBug defaults ***\n"); } /* * Override BSP_vme_config (weak symbol in librtemsbsp.a). * * The default BSP_vme_config calls vmeUniverseReset (our no-op wrapper), * then BSP_VMEOutboundPortCfg to reprogram windows at 0x9xxx PCI addresses. * Those reprogrammed windows cause S_TA=1 on MVME2100. * * Our override calls BSP_VMEInit (to set up theOps function table), sets * DBAT1 for PPCBug's PCI range, and installs the IRQ manager — but does * NOT reprogram any outbound windows. * * PPCBug factory-default outbound windows (MVME2100 manual Ch.5): * LSI1: PCI 0x81000000-0x9FFFFFFF → VME A32 0x01-0x1F * LSI2: PCI 0xA0000000-0xA1FFFFFF → VME A24 0xF0-0xF1 * LSI3: PCI 0xAFFF0000-0xAFFFFFFF → VME A16 0xFFFF */ void BSP_vme_config(void) { /* Fix stdout buffering: Newlib on RTEMS defaults to fully buffered, * which causes output from iocsh commands (vmeread, dbl, etc.) to * get stuck in the buffer and never appear on the serial console. * Switch to line-buffered so output appears after each newline. */ setvbuf(stdout, NULL, _IOLBF, 0); printf("=== Custom BSP_vme_config for MVME2100 ===\n"); /* BSP_VMEInit finds Universe II on PCI bus (via pci_find_device), * stores register base in vmeUniverse0BaseAddr, enables PCI BM+MS, * sets theOps = uniOpsRec. * Internally calls vmeUniverseReset — our no-op wrapper. */ if (BSP_VMEInit()) { printf(" BSP_VMEInit FAILED!\n"); return; } printf(" BSP_VMEInit OK\n"); /* DBAT1: cover PPCBug A24 (0xA0) + A16 (0xAFFF) PCI range */ setdbat(BSP_VME_BAT_IDX, 0xA0000000, 0xA0000000, 0x10000000, IO_PAGE); printf(" DBAT%d: 0xA0000000/256MB -I-G\n", BSP_VME_BAT_IDX); /* DO NOT call BSP_VMEOutboundPortCfg — PPCBug defaults preserved. * ANY write to Universe II outbound LSI registers causes S_TA=1. */ BSP_VMEOutboundPortsShow(0); BSP_VMEInboundPortsShow(0); /* Install VME interrupt manager */ printf(" Installing VME IRQ manager...\n"); if (BSP_VMEIrqMgrInstall() < 0) printf(" WARNING: BSP_VMEIrqMgrInstall error\n"); else printf(" VME IRQ manager installed\n"); printf("=== Custom BSP_vme_config complete ===\n"); } /* * __wrap_BSP_vme2local_adrs — linked via -Wl,--wrap=BSP_vme2local_adrs * * The Universe II driver's vmeUniverseXlateAddr cannot reverse PPCBug's * translation offsets (32-bit wrapping) and doesn't match the AM code * (PPCBug uses SUPER=0, EPICS requests supervisory AM). We provide * hardcoded translations for PPCBug's known window layout. */ extern int __real_BSP_vme2local_adrs( unsigned long am, unsigned long vmeaddr, unsigned long *plocaladdr); int __wrap_BSP_vme2local_adrs( unsigned long am, unsigned long vmeaddr, unsigned long *plocaladdr) { switch (am) { case 0x2D: /* A16 supervisory */ case 0x29: /* A16 non-privileged */ *plocaladdr = 0xAFFF0000UL + (vmeaddr & 0xFFFF); return 0; case 0x3D: /* A24 supervisory */ case 0x39: /* A24 non-privileged */ if (vmeaddr < 0x02000000UL) { *plocaladdr = 0xA0000000UL + vmeaddr; return 0; } break; case 0x0D: /* A32 supervisory */ case 0x09: /* A32 non-privileged */ if (vmeaddr >= 0x01000000UL && vmeaddr < 0x20000000UL) { *plocaladdr = 0x81000000UL + (vmeaddr - 0x01000000UL); return 0; } break; } return __real_BSP_vme2local_adrs(am, vmeaddr, plocaladdr); } } /* extern "C" */ /* ================================================================== */ /* Diagnostic helpers (read-only Universe II register access) */ /* ================================================================== */ static volatile unsigned int *universeRegs = NULL; static inline unsigned int univ_bswap32(unsigned int v) { return ((v & 0xFF) << 24) | ((v & 0xFF00) << 8) | ((v >> 8) & 0xFF00) | ((v >> 24) & 0xFF); } static inline unsigned int univRead(int offset) { return univ_bswap32(universeRegs[offset / 4]); } static int findUniverseRegs(void) { volatile unsigned int *candidate = (volatile unsigned int *)0xFCDFF000UL; unsigned int id = bswap32(candidate[0]); if ((id & 0x0000FFFF) == 0x10E3 || (id & 0xFFFF0000) == 0x10E30000) { universeRegs = candidate; return 0; } universeRegs = candidate; return 0; } /* ================================================================== */ /* VME diagnostic at startup (registrar, runs before iocInit) */ /* ================================================================== */ static void installFixedVmeProbe(void) { printf("VME diag: registrar running — PPCBug defaults should be active\n"); if (findUniverseRegs() != 0) return; /* Read-only diagnostics */ { unsigned int pci_csr = univRead(0x004); printf("VME diag: PCI_CSR=0x%08X (BM=%d MS=%d S_TA=%d)\n", pci_csr, (pci_csr>>2)&1, (pci_csr>>1)&1, (pci_csr>>27)&1); } /* Show PPCBug's outbound windows */ { int port; printf("VME diag: Outbound windows:\n"); for (port = 0; port < 4; port++) { int off = 0x100 + port * 0x14; unsigned int ctl = univRead(off); if (!((ctl >> 31) & 1)) continue; printf(" LSI%d: CTL=0x%08X BS=0x%08X BD=0x%08X TO=0x%08X\n", port, ctl, univRead(off+4), univRead(off+8), univRead(off+0xC)); } } /* Quick VME test */ { volatile void *pLocal = NULL; unsigned char probeVal = 0; long status; status = devBusToLocalAddr(atVMEA16, 0xFC00, &pLocal); if (status == 0) { status = devReadProbe(1, pLocal, &probeVal); printf("VME diag: devReadProbe A16 0xFC00 @ %p = %s (0x%02X)\n", pLocal, status == 0 ? "OK" : "FAIL", probeVal); } else { printf("VME diag: devBusToLocalAddr A16 0xFC00 FAILED\n"); } } } #endif /* __rtems__ && __PPC__ */ /* ================================================================== */ /* omsSetup IOCsh registration */ /* ================================================================== */ static const iocshArg omsArg0 = {"num_cards", iocshArgInt}; static const iocshArg omsArg1 = {"addrs", iocshArgInt}; static const iocshArg omsArg2 = {"vector", iocshArgInt}; static const iocshArg omsArg3 = {"int_level", iocshArgInt}; static const iocshArg omsArg4 = {"scan_rate", iocshArgInt}; static const iocshArg * const omsArgs[5] = { &omsArg0, &omsArg1, &omsArg2, &omsArg3, &omsArg4 }; static const iocshFuncDef omsSetupFuncDef = {"omsSetup", 5, omsArgs}; static void omsSetupCallFunc(const iocshArgBuf *args) { omsSetup(args[0].ival, (unsigned)args[1].ival, (unsigned)args[2].ival, args[3].ival, args[4].ival); } /* ================================================================== */ /* vmeProbeTest - diagnostic IOCsh command */ /* ================================================================== */ static void vmeProbeTestFunc(int vmeAddr, int wordSize) { volatile void *pPhysical = NULL; long status; unsigned char val8 = 0; unsigned short val16 = 0; unsigned int val32 = 0; int ws; printf("vmeProbeTest: VME A16 address 0x%04X, wordSize=%d\n", (unsigned)vmeAddr, wordSize); status = devBusToLocalAddr(atVMEA16, (size_t)vmeAddr, &pPhysical); if (status != 0) { printf(" devBusToLocalAddr FAILED, status=%ld\n", status); return; } printf(" local addr = %p\n", pPhysical); for (ws = 1; ws <= 4; ws <<= 1) { void *valPtr; switch (ws) { case 1: valPtr = &val8; break; case 2: valPtr = &val16; break; default: valPtr = &val32; break; } status = devReadProbe(ws, pPhysical, valPtr); if (status == 0) { switch (ws) { case 1: printf(" %d-byte: 0x%02X OK\n", ws, val8); break; case 2: printf(" %d-byte: 0x%04X OK\n", ws, val16); break; default: printf(" %d-byte: 0x%08X OK\n", ws, val32); break; } } else { printf(" %d-byte: FAIL\n", ws); } } } static const iocshArg probeArg0 = {"vmeAddr", iocshArgInt}; static const iocshArg probeArg1 = {"wordSize", iocshArgInt}; static const iocshArg * const probeArgs[2] = { &probeArg0, &probeArg1 }; static const iocshFuncDef vmeProbeTestDef = {"vmeProbeTest", 2, probeArgs}; static void vmeProbeTestCallFunc(const iocshArgBuf *args) { int ws = args[1].ival; if (ws < 1 || ws > 4) ws = 1; vmeProbeTestFunc(args[0].ival, ws); } /* ================================================================== */ /* vmeScanA16 - scan entire A16 space */ /* ================================================================== */ static void vmeScanA16Func(void) { volatile void *pPhysical = NULL; long status; unsigned char val8 = 0; int addr, found = 0; printf("vmeScanA16: Scanning A16 0x0000..0xFF00 (step 0x100)...\n"); for (addr = 0; addr < 0x10000; addr += 0x100) { status = devBusToLocalAddr(atVMEA16, (size_t)addr, &pPhysical); if (status != 0) continue; status = devReadProbe(1, pPhysical, &val8); if (status == 0) { printf(" A16 0x%04X -> %p -> 0x%02X FOUND\n", addr, pPhysical, val8); found++; } } printf(" %d device(s) found.\n", found); } static const iocshFuncDef vmeScanA16Def = {"vmeScanA16", 0, NULL}; static void vmeScanA16CallFunc(const iocshArgBuf *args) { vmeScanA16Func(); } /* ================================================================== */ /* Registrar */ /* ================================================================== */ static void omsSetupRegistrar(void) { #if defined(__rtems__) && defined(__PPC__) installFixedVmeProbe(); #endif iocshRegister(&omsSetupFuncDef, &omsSetupCallFunc); iocshRegister(&vmeProbeTestDef, &vmeProbeTestCallFunc); iocshRegister(&vmeScanA16Def, &vmeScanA16CallFunc); } extern "C" { epicsExportRegistrar(omsSetupRegistrar); } ------------------------------------------------------------------------------------------------------------------ Best Regards Mirek On Mon, Apr 13, 2026 at 2:03 PM Michael Davidsaver <mdavidsaver at gmail.com> wrote:
| ||||||||||||||||
| ANJ, 16 Apr 2026 |
·
Home
·
News
·
About
·
Talk
·
Base
·
Modules
·
Extensions
·
· Distributions · Download · Documents · Links · Licensing · |