Czysta architektura Harvarda ogólnie pozwala komputerowi o danym poziomie złożoności działać szybciej niż architektura Von Neumana, pod warunkiem, że nie trzeba współużytkować zasobów między kodem a pamięcią danych. Jeśli ograniczenia wyprowadzeń lub inne czynniki zmuszają użycie jednej magistrali do uzyskania dostępu do obu przestrzeni pamięci, zalety takie mogą zostać w dużej mierze zniesione.
„Czysta” architektura Harvarda będzie ograniczona do działającego kodu, który jest wprowadzany do pamięci przez inny mechanizm niż procesor, który uruchomi kod. Ogranicza to użyteczność takich architektur dla urządzeń, których przeznaczenie nie jest ustawione przez fabrykę (lub kogoś ze specjalistycznym sprzętem do programowania). Aby rozwiązać ten problem, można zastosować dwa podejścia:
Niektóre systemy mają oddzielne obszary kodu i pamięci, ale zapewniają specjalny sprzęt, który można poprosić o krótkotrwałe przejęcie magistrali kodowej, wykonanie pewnych operacji i zwrot kontroli do CPU po zakończeniu takiej operacji. Niektóre takie systemy wymagają dość skomplikowanego protokołu do przeprowadzenia takich operacji, niektóre mają specjalne instrukcje, aby wykonać takie zadanie, a niektóre nawet szukają określonych adresów „pamięci danych” i wyzwalają przejęcie / zwolnienie, gdy próbuje się uzyskać do nich dostęp . Kluczowym aspektem takich systemów jest to, że istnieją wyraźnie zdefiniowane obszary pamięci dla „kodu” i „danych”; nawet jeśli procesor może odczytywać i zapisywać przestrzeń „kodu”, nadal jest rozpoznawany jako semantycznie różny od przestrzeni danych ”.
Alternatywnym podejściem stosowanym w niektórych systemach wyższej klasy jest posiadanie kontrolera z dwiema magistralami pamięci, jedną dla kodu i jedną dla danych, oba połączone z jednostką arbitrażu pamięci. Ta jednostka z kolei jest podłączona do różnych podsystemów pamięci za pomocą osobnej magistrali pamięci dla każdego. Dostęp do kodu do jednego podsystemu pamięci może być przetwarzany jednocześnie z dostępem do danych do innego; tylko jeśli kod i dane spróbują uzyskać dostęp do tego samego podsystemu jednocześnie, jedno z nich będzie musiało poczekać.
W systemach, które stosują to podejście, niekrytyczne pod względem wydajności części programu mogą po prostu zignorować granice między podsystemami pamięci. Jeśli kod i dane znajdują się w tym samym podsystemie pamięci, rzeczy nie będą działały tak szybko, jak gdyby były w oddzielnych podsystemach, ale dla wielu części typowego programu, które nie będą miały znaczenia. W typowym systemie będzie mała część kodu, w której wydajność naprawdę ma znaczenie, i będzie działać tylko na niewielkiej części danych przechowywanych przez system. Gdyby ktoś miał system z 16 KB pamięci RAM, który był podzielony na dwie partycje 8 KB, można by użyć instrukcji linkera, aby upewnić się, że kod krytyczny dla wydajności znajduje się w pobliżu początku całej przestrzeni pamięci, a dane krytyczne dla wydajności były blisko koniec. Jeśli ogólny rozmiar kodu wzrośnie do np. 9 KB, kod z ostatniego 1K działałby wolniej niż kod umieszczony gdzie indziej, ale kod ten nie byłby krytyczny pod względem wydajności. Podobnie, jeśli kod miałby np. Tylko 6 KB, ale dane wzrosłyby do 9 KB, dostęp do najniższego 1 000 danych byłby powolny, ale gdyby dane o kluczowej wydajności były zlokalizowane gdzie indziej, nie stanowiłoby to problemu.
Zauważ, że chociaż wydajność byłaby optymalna, gdyby kod był mniejszy niż 8 KB, a dane poniżej 8 KB, wspomniana konstrukcja systemu pamięci nie narzucałaby żadnej ścisłej podziału między kodem a przestrzenią danych. Jeśli program potrzebuje tylko 1 KB danych, kod może wzrosnąć do 15 KB. Jeśli potrzebuje tylko 2K kodu, dane mogą wzrosnąć do 14K. Znacznie bardziej wszechstronny niż posiadanie obszaru 8K tylko dla kodu i obszaru 8K tylko dla danych.