Различия

Здесь показаны различия между двумя версиями данной страницы.

Ссылка на это сравнение

Предыдущая версия справа и слева Предыдущая версия
Следующая версия
Предыдущая версия
sim [2019/12/02 17:16]
org
sim [2019/12/03 11:21]
org
Строка 22: Строка 22:
  
 Простой пример:​ запись в регистр PPU. Звучит просто,​ но рассмотрим что происходит при этом: Простой пример:​ запись в регистр PPU. Звучит просто,​ но рассмотрим что происходит при этом:
-процессор (а точнее ядро процессора 6502 встроенного в APU) начинает выполнение инструкции Store. Инструкция декодируется и в итоге значение регистра подается на шину данных APU, которая соединена с шиной данных PPU. При этом на адресную шину выставляется адрес регистра PPU. +  * процессор (а точнее ядро процессора 6502 встроенного в APU) начинает выполнение инструкции Store. Инструкция декодируется и в итоге значение регистра подается на шину данных APU, которая соединена с шиной данных PPU. При этом на адресную шину выставляется адрес регистра PPU. 
-Дополнительная логика на материнской плате NES определяет что выставленный адрес соответствует регистрам PPU ($2000 например). Логика включает PPU CS (chip select) (а точнее делает контакт #DBE = 0), также она подает сигнал R/W (WR=1) и выставляет контакты PPU RS0-RS2 (выбор регистра) +  ​* ​Дополнительная логика на материнской плате NES определяет что выставленный адрес соответствует регистрам PPU ($2000 например). Логика включает PPU CS (chip select) (а точнее делает контакт #DBE = 0), также она подает сигнал R/W (WR=1) и выставляет контакты PPU RS0-RS2 (выбор регистра) 
-В это время логика Register Select внутри PPU запускает процесс обновления затребованного регистра. При этом все внутренние схемы PPU мгновенно реагируют на изменение регистра и соответственно изменяется логика работы всего PPU.+  ​* ​В это время логика Register Select внутри PPU запускает процесс обновления затребованного регистра. При этом все внутренние схемы PPU мгновенно реагируют на изменение регистра и соответственно изменяется логика работы всего PPU.
  
 Какие могут возникнуть тут проблемы?​ Главная проблема - это конечно же propagation delay. Или что было раньше - курица или яйцо. А ведь ситуации могут быть и похлеще,​ например спрайтовая DMA: когда внутренняя схема APU отключает ядро от шины и подвешивает процессор,​ при этом в PPU пересылается поток байт, для обновления спрайтовой памяти. Какие могут возникнуть тут проблемы?​ Главная проблема - это конечно же propagation delay. Или что было раньше - курица или яйцо. А ведь ситуации могут быть и похлеще,​ например спрайтовая DMA: когда внутренняя схема APU отключает ядро от шины и подвешивает процессор,​ при этом в PPU пересылается поток байт, для обновления спрайтовой памяти.
  
 В общем случае проблема выглядит так (упрощенно):​ В общем случае проблема выглядит так (упрощенно):​
-Схема A имеет выходы aaa и входы bbb. +  * Схема A имеет выходы aaa и входы bbb. 
-Схема B имеет выходы bbb и входы aaa. (то есть схемы выдают друг на друга управляющие сигналы) +  ​* ​Схема B имеет выходы bbb и входы aaa. (то есть схемы выдают друг на друга управляющие сигналы) 
-Что выполняется вначале - схема A или B?+  ​* ​Что выполняется вначале - схема A или B?
  
 Ведь при изменении aaa - схема B сразу изменится,​ соответственно сигналы bbb тоже поменяются и повлияют на работу A. Возникает долбаный круговорот. Ведь при изменении aaa - схема B сразу изменится,​ соответственно сигналы bbb тоже поменяются и повлияют на работу A. Возникает долбаный круговорот.
Строка 40: Строка 40:
 Вообще в APU главным является не ядро, а управляющая логика APU, которая решает что главнее в данный момент - внутренние устройства APU (звуковые каналы) или ядро 6502. Вообще в APU главным является не ядро, а управляющая логика APU, которая решает что главнее в данный момент - внутренние устройства APU (звуковые каналы) или ядро 6502.
 Поэтому статический подход к симуляции полутакта CLK заключается в следующем:​ Поэтому статический подход к симуляции полутакта CLK заключается в следующем:​
-симулировать все внутренние схемы APU (делитель частоты,​ frame counter, выбор регистра и выдача его на шину данных,​ звуковые схемы) +  * симулировать все внутренние схемы APU (делитель частоты,​ frame counter, выбор регистра и выдача его на шину данных,​ звуковые схемы) 
-симулировать ядро 6502 (выполнить инструкцию,​ получить или выдать значение на шину данных) +  ​* ​симулировать ядро 6502 (выполнить инструкцию,​ получить или выдать значение на шину данных) 
-симулировать PPU+  ​* ​симулировать PPU
  
 Но опять же остается проблема:​ что делать если мы уже симулировали всю схему (например 6502), но последующая симуляцию другой схемы поменяла входные данные. Например,​ чтение регистра PPU: Но опять же остается проблема:​ что делать если мы уже симулировали всю схему (например 6502), но последующая симуляцию другой схемы поменяла входные данные. Например,​ чтение регистра PPU:
-мы уже выполнили инструкцию Load (при этом получили с шины данных какую-то фигню) +  * мы уже выполнили инструкцию Load (при этом получили с шины данных какую-то фигню) 
-настала очередь симуляции PPU, мы выдаем на шину данных верное значение регистра,​ но ведь симуляция 6502 уже прошла! +  ​* ​настала очередь симуляции PPU, мы выдаем на шину данных верное значение регистра,​ но ведь симуляция 6502 уже прошла! 
-жопа+  ​* ​жопа
  
 ## Реактивное программирование ## Реактивное программирование
Строка 90: Строка 90:
  
 Логика работы следующая:​ Логика работы следующая:​
-Во время CLK = 0 вход регистра отсоединен и входная защелка рефрешится старым значением +  * Во время CLK = 0 вход регистра отсоединен и входная защелка рефрешится старым значением 
-Во время CLK = 1 выход отрезается (транзистором /CLK), чтобы не конфликтовать,​ а новое значение подается на вход **in**, но только в том случае,​ если открыт транзистор ENABLE (разрешить запись) +  ​* ​Во время CLK = 1 выход отрезается (транзистором /CLK), чтобы не конфликтовать,​ а новое значение подается на вход **in**, но только в том случае,​ если открыт транзистор ENABLE (разрешить запись) 
-Выход будет инвертирован относительно входа (так как входная защелка организована в виде инвертора с плавающим затвором) +  ​* ​Выход будет инвертирован относительно входа (так как входная защелка организована в виде инвертора с плавающим затвором) 
-Если CLK = 1, а ENABLE = 0, то на выход подается остаточный заряд с затвора защёлки (то есть старое значение)+  ​* ​Если CLK = 1, а ENABLE = 0, то на выход подается остаточный заряд с затвора защёлки (то есть старое значение)
  
 Псевдокод в этом случае будет такой: Псевдокод в этом случае будет такой:
Строка 122: Строка 122:
  
 При симуляции происходит следующее:​ При симуляции происходит следующее:​
-Выбираются все блоки **at**. Эти блоки являются реактивными по отношению к какому-либо сигналу. В данном случае реактивным сигналом будет являться сигнал CLK. Ядро симулятора постоянно следит за CLK, и как только он изменяет своё значение (с 0 на 1, или наоборот) - запускается соответствующий блок **at**. Блоки которые не имеют реактивной связи нас не интересуют (они как будто "​заморожены"​). +  * Выбираются все блоки **at**. Эти блоки являются реактивными по отношению к какому-либо сигналу. В данном случае реактивным сигналом будет являться сигнал CLK. Ядро симулятора постоянно следит за CLK, и как только он изменяет своё значение (с 0 на 1, или наоборот) - запускается соответствующий блок **at**. Блоки которые не имеют реактивной связи нас не интересуют (они как будто "​заморожены"​). 
-Последовательность симуляции выбранных at-блоков не имеет значения,​ поскольку все входы и выходы также реактивно связаны +  ​* ​Последовательность симуляции выбранных at-блоков не имеет значения,​ поскольку все входы и выходы также реактивно связаны 
-В нашем примере мы имеем непосредственно наш регистр (reg) и какую-то схему, которая выдает новое значение regnew и сигнал enable. +  ​* ​В нашем примере мы имеем непосредственно наш регистр (reg) и какую-то схему, которая выдает новое значение regnew и сигнал enable. 
-Порядок расположения схем выбран не случайно (вначале reg, потом some_circuit),​ чтобы показать особенность реактивного "​выполнения"​ блоков. ​+  ​* ​Порядок расположения схем выбран не случайно (вначале reg, потом some_circuit),​ чтобы показать особенность реактивного "​выполнения"​ блоков. ​
  
 Начинаем исполнение:​ Начинаем исполнение:​
-Первым на очереди стоит блок reg, его входы regnew и enable пока не определены (то есть равны "​x"​) +  * Первым на очереди стоит блок reg, его входы regnew и enable пока не определены (то есть равны "​x"​) 
-В этом случае выход блока #out будет просто инверсией текущего значения регистра not(regvalue). +  ​* ​В этом случае выход блока #out будет просто инверсией текущего значения регистра not(regvalue). 
-Это был первый прогон блока reg +  ​* ​Это был первый прогон блока reg 
-Теперь мы выполняем блок some_circuit и выдаем наружу сигналы regnew и enable. Ядро симулятора видит что эти сигналы реактивно связаны с входами блока reg, и (это ключевой момент) они **изменились**. А раз они изменились надо заново прогнать схему reg, чтобы "​стабилизировать"​ её. +  ​* ​Теперь мы выполняем блок some_circuit и выдаем наружу сигналы regnew и enable. Ядро симулятора видит что эти сигналы реактивно связаны с входами блока reg, и (это ключевой момент) они **изменились**. А раз они изменились надо заново прогнать схему reg, чтобы "​стабилизировать"​ её. 
-Блок reg вызывается во второй раз, но уже на этот раз входы regnew и enable имеют определенные значения. +  ​* ​Блок reg вызывается во второй раз, но уже на этот раз входы regnew и enable имеют определенные значения. 
-Схема стабилизировалась,​ значит все блоки выполнились и мы ждём изменения сигнала CLK, чтобы начать всё заново,​ но логика работы блоков уже поменяется (так как будут загружены блоки **at** для CLK = 1).+  ​* ​Схема стабилизировалась,​ значит все блоки выполнились и мы ждём изменения сигнала CLK, чтобы начать всё заново,​ но логика работы блоков уже поменяется (так как будут загружены блоки **at** для CLK = 1).
  
 А что будет если схема не сможет стабилизироваться?​ Что если regnew или enable будет всегда меняться и нам придётся выполнять блок **reg** снова и снова? Да ничего особенного :-) Такая ситуация называется "race condition"​ и обычно не возникает. В частности в процессоре 6502 для стабилизации всех схем достаточно не более 10 итераций. Для того, чтобы наша схема не начала бесконечный цикл мы просто ставим таймаут на количество итераций и если их количество стало ну скажем - больше 100, то просто выводим ошибку "Race condition!"​ и до свидания :-) А что будет если схема не сможет стабилизироваться?​ Что если regnew или enable будет всегда меняться и нам придётся выполнять блок **reg** снова и снова? Да ничего особенного :-) Такая ситуация называется "race condition"​ и обычно не возникает. В частности в процессоре 6502 для стабилизации всех схем достаточно не более 10 итераций. Для того, чтобы наша схема не начала бесконечный цикл мы просто ставим таймаут на количество итераций и если их количество стало ну скажем - больше 100, то просто выводим ошибку "Race condition!"​ и до свидания :-)
Строка 148: Строка 148:
  
 Такая реализация обеспечит нам серьезный задел на будущее:​ Такая реализация обеспечит нам серьезный задел на будущее:​
-Симулировать можно любые системы и процессоры,​ просто перегнав их в Verilog +  * Симулировать можно любые системы и процессоры,​ просто перегнав их в Verilog 
-Система встраиваемых функций позволяет реализовать самые любые псевдо-устройства +  ​* ​Система встраиваемых функций позволяет реализовать самые любые псевдо-устройства 
-Реализацию на Verilog можно будет воплотить в железном варианте +  ​* ​Реализацию на Verilog можно будет воплотить в железном варианте 
-HardWareMan будет тоже очень рад, потому что он получит готовую реализацию PPU на Verilog :-)+  ​* ​HardWareMan будет тоже очень рад, потому что он получит готовую реализацию PPU на Verilog :-)
  
 Задача ясна и понятна:​ Задача ясна и понятна:​
-Написать лексический анализатор Verlog-синтаксиса +  * Написать лексический анализатор Verlog-синтаксиса 
-Написать парсер синтаксиса в синтаксическое дерево +  ​* ​Написать парсер синтаксиса в синтаксическое дерево 
-Оптимизировать и преобразовать синтаксическое дерево в netlist (надо изучить какие бывают популярные форматы для netlist-ов) +  ​* ​Оптимизировать и преобразовать синтаксическое дерево в netlist (надо изучить какие бывают популярные форматы для netlist-ов) 
-Написать ядро симулятора netlist, которое статически рекомпилирует его в X86-код и выполняет (тут надо продумать эффективную реактивную модель) +  ​* ​Написать ядро симулятора netlist, которое статически рекомпилирует его в X86-код и выполняет (тут надо продумать эффективную реактивную модель) 
-Написать псевдо-устройства для NES (джойпад и телек)+  ​* ​Написать псевдо-устройства для NES (джойпад и телек)
  
 http://​irs.nntu.ru/​globals/​files/​bukvarev/​verilog.pdf http://​irs.nntu.ru/​globals/​files/​bukvarev/​verilog.pdf
  
-Описание внутреннего устройства виртуальной машины - [[sim:​breaksvm|BreaksVM]] 
  • Показать страницу