monotonic_buffer_resource sorgt für DMA Interrupt Error



  • Hallo Leutz, war lange nicht mehr im Forum aktiv, aber es gab bisher auch nichts zu beanstanden.
    Zuerst mal wünsche ich allen ein gesunden neues Jahr.🙂

    Mein Jahres wechsel war ja so Semi gut, ich habe an meinem Hobby Playstation 2 Spiel weiter gemacht
    und da hat mich den ganzen Dezember über eine Race condition + Heisenbug geärgert😡 🤬 .
    Was für ein Ultra Nerv das war.

    Jetzt geht der Spaß weiter, aber anders. Jetzt stürzt das Programm ab wenn ich mit
    std::pmr::monotonic_buffer_resource eine Memory-Pool baue.
    Ich haben mir eine Pool Helper klasse gebastelt:

    #ifndef POOL_HELPER_H
    #define POOL_HELPER_H
    
    #include <memory>
    #include <memory_resource>
    #include <utility>
    
    // Bekannter Deleter
    struct PMRDeleter
    {
        template <typename T>
        void operator()(T* ptr) const
        {
            if (ptr) ptr->~T();
        }
    };
    
    template <typename T>
    using pool_ptr = std::unique_ptr<T, PMRDeleter>;
    
    // Die statische Speicher-Klasse
    class MemoryManager
    {
    private:
        static constexpr std::size_t GAME_OBJ_SIZE = 512 * 1024;//512 kb
        static constexpr std::size_t MAP_SIZE = 512 * 1024;//128kB
        static inline char buffer[MAP_SIZE];
        static inline std::pmr::monotonic_buffer_resource upstream{
            buffer, MAP_SIZE, std::pmr::null_memory_resource()
        };
    
    
    public:
        // Definition der Puffergrößen
        //static constexpr std::size_t RENDER_OBJECT_SIZE = 2 * 1024 * 1024;//2Mb
        /*
            static std::pmr::monotonic_buffer_resource& getrenderObjectPool() {
                alignas(64) static char buffer[RENDER_OBJECT_SIZE];
                static std::pmr::monotonic_buffer_resource res(buffer, RENDER_OBJECT_SIZE, std::pmr::null_memory_resource());
                return res;
            }
        */
        // Zugriff auf die Ressourcen
        static std::pmr::monotonic_buffer_resource& getGameObjectPool()
        {
            static char buffer[GAME_OBJ_SIZE];
            static std::pmr::monotonic_buffer_resource res(buffer, GAME_OBJ_SIZE, std::pmr::null_memory_resource());
            return res;
        }
    
        static std::pmr::unsynchronized_pool_resource& getMapPool()
        {
            static std::pmr::unsynchronized_pool_resource pool(&upstream);
            return pool;
        }
    
    
        // Die Factory-Funktion direkt in der Klasse
        template<typename T, typename... Args>
        static pool_ptr<T> createObject(std::pmr::monotonic_buffer_resource& currentPool, Args&&... args)
        {
            void* mem = currentPool.allocate(sizeof(T), alignof(T));
            T* obj = ::new (mem) T(std::forward<Args>(args)...);
            return pool_ptr<T>(obj);
        }
    
        // Optional: Den kompletten Speicher zurücksetzen (z.B. game over)
        static void resetAll()
        {
            getGameObjectPool().release();
            //getrenderObjectPool().release();
        }
    
        static uint32_t getPoolSize(){
            return GAME_OBJ_SIZE+MAP_SIZE;
        }
    };
    
    #endif
    
    

    Soweit so gut. Nur diese Strukturen werden im Pool abgelegt:

    struct alignas(16) effect_t
    {
        texrect_t rect;
        VECTOR border;
        uint16_t tex_x;
        uint16_t tex_y;
        bool show = false;
        bool brokenEffect = false;
        bool srcBufferIndex = 0;
    };
    
    struct alignas(16) renderObject_t
    {
        VECTOR texelPosition;   // 16
        VECTOR objectPosition;  // 16
        VECTOR border;          // 16
    
        float zIndex;           // 4
        float spriteWidth;      // 4
        float spriteHeight;     // 4
    
        uint16_t gID;           // 2
        uint8_t flip = 0;                // 1
        uint8_t srcBufferIndex = 0;      // 1
        bool show = true;              // 1
    
        struct alignas(16)
        {
            VECTOR pos;         // 16
            bool show = true;          // 1
            // Padding
        } shadow;
    };
    

    Jetzt gibt es eine Besonderheit. Nicht die Strukturen werden via DMA transportiert, sonder nur der VECTOR.
    Das funktioniert via Chain DMA Transfer.

    Ich bastel mir also eine src DMA Kette an Adressen zusammen von dem VECTOR, der in dem struct ist und letzten Endes im std::pmr::monotonic_buffer_resource.

    Jetzt das Kuriose, der erste Kampf läuft noch, dann wird der Game over screen angezeigt und man kehrt ins Menü zurück, dabei wird der monotonic_buffer_resource zurück gesetzt.
    Der zweite Kampf beginnt und erst nach einer Zeit x stützt die Play'si ab.
    Der Absturzbericht ist immer gleich:

     # DMAC: Bus error interrupt.
    
    *** Unexpected reply - type=BREAKR result=PROGEND
    
    dsedb S> dr
     at=00000002  v0-1=00000000,00000000  a0-3=00000001,00000000,80030000,80012618
     t0-7=8002ac54,b000e020,b000c800,00800000, 10000001,000068a5,30007fff,00011730
     s0-7=10000000,8001d604,009f5dd0,00000000, 0000000f,00000000,00000000,00000000
     t8=00000000 t9=00000000   k0=80016ed8 k1=00000000   gp=0042e870 sp=8001d3f0
     fp=009cad30 ra=80001db0   lo=00000024 hi=00000000   sa=00000000 PC=80000dc0
     badvaddr=40000f87 badpaddr=42084200
     $cause   = 0x50008424 [ BD2 CE1 EXC2=Reset IP7 IP2 EXC="Breakpoint" ]
     $status  = 0x70030c02 [ Cu210 EDI EIE IM3 IM2 KSU=Kernel EXL ]
      0x80000db8: 0x00000000  nop
      0x80000dbc: 0x00000000  nop
    ->0x80000dc0: 0x03ffffcd  break   0xfffff
      0x80000dc4: 0x00000000  nop
      0x80000dc8: 0x00000000  nop
      0x80000dcc: 0x00000000  nop
      0x80000dd0: 0x00000000  nop
    dsedb S>  
    

    die Return Adresse zeigt ins nirgendwo und ist deshalb total unnütz
    Nun ist ja Aligment immer so ein Thema bei DMA Transferen.
    Ich haben mir drauf hin einen eigenen arena allocator gebastelt und extra auf die Ausrichtung geachtet
    Mit der erkenntis. Der Absturz passiert da genau so.

    Zum Schluss habe ich auch noch alles auf Roh Zeiger umgebaut, hat auch nichts bewirkt.

    Flush Cache habe ich nach dem Zurücksetzen des Speichers probiert, das ist es auch nicht gewesen.

    Es muss also speziell damit zu tun haben, das man die DMA Src Adressen einsperrt.

    Ein ähnliches Problem besteht auch bei der Verwendung von einer unordered_map mit einer monotonic_buffer_resource
    wenn ich die Map lösche "clear()" und dann den Puffer zurück setze, knallt es direkt mit einem TLB Miss, sobald ich wider auf die Map zugreife.
    daher habe ich auf std::pmr::unsynchronized_pool_resource für unordered_map gesetzt und die Render Objecte landen im normalen heap mit std::unique_ptr.

    Ich habe heute Morgen 4 Stunden gezockt und es lief.
    Trotzdem die Heap Fragmentierung ist ein Problem und lässt mir keine ruhe.

    Das ist zwar ein sehr Spezielles Thema, eventuell weiß trotzdem jemand was das Problem sein könnte.

    VG. Dr. D.



  • @D_Key

    Nicht die Strukturen werden via DMA transportiert, sonder nur der VECTOR.

    Wie synchronisiert du den Zugriff? Nicht dass du auf den VECTOR zugreifst, während der DMA schreibt.


    BTW: MIr ist mal selbst ein unsigned int i; i++ um die Ohren geflogen.



  • @Quiche-Lorraine sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Wie synchronisiert du den Zugriff? Nicht dass du auf den VECTOR zugreifst, während der DMA schreibt.

    Das macht die Packet2 Bibliothek bzw die DMA Bibliothek, wenn man die entsprechende flag gesetzt hat.
    es gibt einmal FlushCache:
    https://github.com/ps2dev/ps2sdk/blob/master/ee/dma/src/dma.c#L164
    und
    https://github.com/ps2dev/ps2sdk/blob/master/ee/dma/src/dma.c#L193
    kurz vor dem Transfer.

    mit packet2_utils_vu_add_unpack_data schribe ich den VECTOR in das DMA Paket
    https://github.com/ps2dev/ps2sdk/blob/master/ee/packet2/include/packet2_utils.h#L67

    Ich habe nur noch nicht begriffen was der Parameter bei FlushCache zu sagen hat und was der unterschied zwischen
    iSyncDCache und SyncDCache ist.

    Ich habe mal geschaut was $cause = 0x50008424 und $status ist,
    Das sind zwei Register im Kernel die den Absturz status speichern,
    aber irwie ist das nichts aussagend.

    Ich habe heute mit nm mal geschaut, was da Speichertechnisch passiert.
    Also static inline char buffer[MAP_SIZE]; funktioniert überhaupt nicht, totz std 17. Die ganze Klasse verbraucht 5MB.
    Ich hatte das als header Only Datei, aus faulheit.
    habe jetzt ein cpp Datei dazu gebastelt jetzt passt das.

    Ob das jetzt das Problem war weiß ich noch nicht.

    Für Heute ist Dienstschluss.

    @Quiche-Lorraine sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    BTW: MIr ist mal selbst ein unsigned int i; i++ um die Ohren geflogen.

    Ja, da hätte ich ein volatile davor gesetzt, nicht das dir da der Optimizer quer kommt🤓



  • @D_Key sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Ich hatte das als header Only Datei, aus faulheit.
    habe jetzt ein cpp Datei dazu gebastelt jetzt passt das.
    Ob das jetzt das Problem war weiß ich noch nicht.

    Ein glasklares Nein. Embedded Entwicklung ist durch die Hardware-Nähe tückisch. Und ich habe schon selbst Spaß mit einem Watchdog gehabt.

    Das ist eigentlich so eine Phase wo ein genaues Studium der Doku rätsam wäre.

    Ja, da hätte ich ein volatile davor gesetzt, nicht das dir da der Optimizer quer kommt🤓

    Nö, es war eine 8-Bit CPU welche eine 32 Bit Variable mittels Interrupt inkrementieren wollte. Also musste ich die Inkrementierung atomar machen.



  • Mein Tipp. Mache mal einen Belastungstest. Jage mal ordentlich Daten über den DMA und schaue ob die Daten stimmen.



  • @D_Key sagte in monotonic_buffer_resource sorgt für DMA Interrupt Error:

    Playstation 2

    Ich bin da sowas von nicht drin und kann nichts wertvolles beitragen, aber dennoch: Playstation 2? Alle Achtung! Nach dem, was ich gehört habe, gibt es wohl nur wenige Computersysteme, die dermaßen kompliziert korrekt und nicht zuletzt effizient zu programmieren sind. Viel Erfolg bei der Fehlersuche!

    Mir reicht gerade völlig mein eigenes Hobbyprojekt einer x86 DOS Runtime und GCC-basierter Toolchain für die Retroprogrammierung - aber das ist vermutlich ein Kindergeburtstag gegen die PS2 😉


Anmelden zum Antworten