
Re: ARM мутекси, st32F4 __DMB() - как се ползва
По-горе се опитах да го обясня...
За да сваляш резервация, трябва да следиш всички нормални store-ве от всички ядра. При едно ядро и ако е просто е възможно. Но при малко по-сложна система увисваш като прани гащи, защото писанията са по-скоро стриймове, демек ядрата си изпълняват store инструкции, но не чакат да се запише всичко. Така "писанията" се нареждат в опашки, кешове и на практика във всеки един момент може да имаш десетки висящи писания пръснати на 100 места.
Кое точно писане от тия 100 искаш да ти развали резервацията? Да не говорим, че освен ако софтуерът не търкаля някакъв "read only" алгоритъм е почти изключено да няма висящи писания някъде. Демек ако всяко писане чисти резервации, може резервациите ти никога да не са успешни.
Следователно никак не е добра идея всяко писане да се следи. Следва варианта да следиш само определени писания - в същия регион или на същия адрес. Само че за да го направиш това, трябва да сравняваш адреси/региони. Но за да може да сравняваш трябва да си в един клок регион. Сравнението на данни от различни клок региони не е тривиално нещо. На практика едно от най-трудните неща за всеки, който проектира хардуер. Говоря, че едно единствено сравнение е проблем, а десетки или стотици сравнения си граничи с безумие. Но е достатъчно само едно сравнение да направиш, за да разбереш проблема. Не е невъзможно, даже и аз съм правил подобни глупости, но цената на това е безсмислено висока.
Убягва ти архитектурата...
Значи ядрото ти е конвейр с да кажем 5-10 стъпки. Навън също имаш нещо като конвейр, само дето е за данни. За разлика от конвейра вътре в чиповете, навън може ти се налага да ходиш по писти и по други причини не винаги може да си позволиш толкова висок клок, колкото вътре. Отделно тъй като може да имаш шини и да се налага арбитрация, или пък имаш контролер на паметта, който опреснява и т.н. почти задължително имаш кеш. А даже и да имаш пряк път за некеширани операция той пак не е толкова "пряк". Нужни са доста клокове за могат данните от ядрото да стигнат до паметта или обратно.
По степен на сложност най-лесен е пътя към паметта. С кеш/буфер спасяваш положението. Следващия по сложност е пътя от паметта, демек четения. Тук се правят много трикове - като почнеш от спекулативни четения, така че данните да са "под ръка", кешовете също помагат донякъде. Отделно и вътрешния конвейр е така проектиран, че адресите да са ясни колкото се може по-рано, пък данните от четенето да са необходими колкото се може по-късно. Общо взето рядко едно ядро успява (някои никога) да чете без да блокира конвейр.
Следващият вариант е read-modify-write. Това е вече отживелица, защото дори и тези архитектури които успяват да прекарат и четения и писания за един клок го правят като прекарват само едната операция.
Демек могат средно да имат едно четене или един запис на клок, но адресите трябва да са различни. Просто това е конвейр и когато четенето "излиза" следващата инструкция която ползва данните трябва тепърва да "влезе". Затова четене-смятане-писане ти е над 5-10 клока или там колкото ти е конвейра, без да броим външните забавяния.
Ситуацията особено се усложнява когато ядрото не е само и директно вързано към централна памет. Както е в примера с STM32H7 където имаш 7 различни памети, имаш още 10-20 мастера, имаш различни шини на различни честоти. В такива ситуации ако тръгнеш да ползва read-modify-write просто убиваш ядрото.
А пък вариантът който ти искаш е write-modify-read e още по-тежък, защото писането трябва да стигне до контролера и то по нормалния ред и не може да се кешира. За четенето пък не може да имаш спекулативен елемент. Евентуално може да се направи по-пряк път, но така или иначе това не решава проблема.
С други думи чистия екслузив е единственото решение, което е просто и универсално. Всяко друго решение или ще е на солена цена или ще е решение само в конкретна и контролирана ситуация.