Просмотр полной версии : Цифровая археология: 1801 и все-все-все
Страницы :
1
2
[
3]
4
5
6
7
8
9
10
11
Весь день мучала мысль - неужели 1801ВМ1 анализирует возникающие прерывания и исключения дожидаясь окончания чтения кода инструкции. Никак не мог поверить в это. Сейчас уточнил - таки да, сначала стартует чтение инструкции, потом опрос матрицы приоритетного шифратора прерываний, потом ожидается окончание чтения, а потом, если внезапно! есть прерывание, то код свежепрочитанной инструкции широким барским жестом выкидывается и начинается работа микропрограммы обработки прерывания. С чем такое расточительство связано - непонятно, ведь обработка прерываний от свежесчитанной и еще невыполненной инструкции зависеть не может. Да еще сохраняемый в стеке PC надо откатить назад на 2, чтобы было верное значение адреса возврата. В-общем, богато люди жили, богато.
С чем такое расточительство связано - непонятно, ведь обработка прерываний от свежесчитанной и еще невыполненной инструкции зависеть не может.
Может быть это как-то связано с обработкой прерываний по ЕМТ, TRAP, BPT или ложной инструкции?
Может быть это как-то связано с обработкой прерываний по ЕМТ, TRAP, BPT или ложной инструкции?
Для EMT, TRAP, BPT, IOT своя отдельная микропрограмма, вероятно она не запускает чтение следующей инструкции потому что известно что она в данный момент выполняться не будет. Речь идет об асинхронных прерываниях (IRQ1-3, VIRQ) и исключениях (тайм-аут ответа, нечетный адрес (нереализована)). В-общем, реалтайм в виде времени реакции на прерывание принесли в жертву скорости потокового исполнения. Прерывания в те времена были явлениями нечастыми - 50/100 Гц системного таймера, или ~960 от системного терминала, поэтому со временем реакции никто не парился.
реалтайм в виде времени реакции на прерывание принесли в жертву скорости потокового исполненияЕсли бы все команды ВМ1 выполнялись за одно обращение к шине - это была бы жертва. Но у ВМ1 есть команды, которые откладывают обработку прерывания на 7 циклов шины ( например - ADD @TAB1(R0),@TAB2(R1) ), поэтому плюс-минус один цикл - не так уж и принципиально.
Если бы все команды ВМ1 выполнялись за одно обращение к шине - это была бы жертва. Но у ВМ1 есть команды, которые откладывают обработку прерывания на 7 циклов шины ( например - ADD @TAB1(R0),@TAB2(R1) ), поэтому плюс-минус один цикл - не так уж и принципиально.
Угу, это ужас-ужас как долго, но терять целый цикл в случае возникновения прерывания все равно жалко.
Нарисовал диаграммку первичного декодирования инструкции - состояние 7E - это самая начальная фаза выполнения всех команд, на входе имеет уже опрошенный контроллер прерываний и полученную от него 3-х битную маску PLI/P1X, а в некоторых случаях и загруженный регистр кода инструкции. Эта маска хитрая - она может получаться как от контроллера прерываний, так и в результате декодирования команды. В итоге программные исключения типа IOT/BPT/TRAP/EMT искусственно ставят эту маску и перезапускают декодирование, получается типа прерывание.
Исключение по недопустимой команде или таймауту шины безусловно приводит к сбросу микроавтомата и принудительному переходу на состояние 7E с активным запросом на исключением с последующей его обработкой.
На флаги TNZVC при первичном декодировании никто не смотрит, не влияют на работу в этой фазе.
Интересно что начальное декодирование достаточно сложное, но оно пропускает некоторые MUL на следующий адрес и только там уже умирает по исключению недопустимой инструкции.
Диаграмма в приложении, полную для всех состояний будет сложно и долго нарисовать, поэтому ограничусь самыми интересными кусками - вход в прерывание, возврат RTI/RTT, MARK.
терять целый цикл в случае возникновения прерывания все равно жалкоТест VM1C8 (http://zx-pk.ru/attachment.php?attachmentid=25730) проверяет, сколько тактов занимает приём вектора прерывания.
На тестировавшейся ДВК-1 результат был такой:
.ru my1:vm1c8
1801VM1 Timings Test #8a
Scale: 1024
Retry: 100
Return : 35:100
RtI : 46:100
IOT + RtI : 126:100
Handler cycles : 1024
Handler clocks : 405:100
IOT + Handler : 483:10 484:90
Interrupts : 1024
Interrupt + Handler : 501:100
Если вычислить разницу между [IOT + Handler] и [Interrupt + Handler], а потом добавить время выборки кода команды IOT ( 12 тактов ), то получается, что адрес вектора прерывания принимался за 30 тактов, а сохранение РС и PSW с последующей загрузкой новых значений из вектора - занимало у IOT и Interrupt по 68 тактов.
По сравнению с такими расходами, один цикл шины длиной 12 тактов ( при задержке RPLY = 6 тактов ) - не очень шокирует.
Если вычислить разницу между [IOT + Handler] и [Interrupt + Handler], а потом добавить время выборки кода команды IOT ( 12 тактов ), то получается, что адрес вектора прерывания принимался за 30 тактов
30 тактов это очень большая разница, я думаю что 12 тактов как раз уже не надо добавлять, потому что они и тратятся на выборку выбрасываемой при прерывании инструкции.
Ну понятно что погоды это выбрасывание не делает, но оно везде все такое же медленное в первом поколении этих процессоров - что LSI-11, что ВМ1. Посмотрел бенчмарки, так уже F11, который незначительно сложнее и тактовая у него почти такая же, уже дал прирост в 4 раза по скорости.
По сравнению с такими расходами, один цикл шины длиной 12 тактов ( при задержке RPLY = 6 тактов ) - не очень шокирует.
Да не шокирует, просто неоптимальность есть определенная и ее также хватает и в других местах. Видимо при разработке в те годы было много других проблем, требовавших решения, поэтому было не до оптимизации. Сделали что смогли на имеющихся средствах разработки.
Помоделировал аппаратные прерывания, при этом матрица шифратора выдает PLI=100b, и, внезапно (sic!), в состоянии 68 перед сохранением в стеке значение PC откатывается назад на 2. То есть, часть исключений сохраняет адрес следующей инструкции, а часть - адрес текущей, откатывая его назад на слово.
ВМ2 в такой же коробочке с бантиком хачу)
ВМ2 у меня есть, и солнечногорский и ангстремовский, я даже его открыл уже. Но пока не фотографировал и руки реверсить его не скоро дойдут. Но могу сфотографировать и отдать желающим на векторизацию :)
http://s012.radikal.ru/i320/1503/20/9ad9e5b2baa7t.jpg (http://s012.radikal.ru/i320/1503/20/9ad9e5b2baa7.jpg)
ВМ2 у меня есть, и солнечногорский и ангстремовский, я даже его открыл уже.
ого! вот это качество снимка! это 4 мкм?
ого! вот это качество снимка! это 4 мкм?
Ниже снимок микрометра на этом же объективе.
http://s020.radikal.ru/i712/1407/cd/695dde782258t.jpg (http://radikal.ru/fp/dc006885914b41e79126c49aa43a65eb)
Цена самого мелкого деления 10 микрон, ширина всего поля снимка примерно 100 делений, всего 1мм. Также ширина всего снимка 3488 пикселей, ширина дорожки на снимке 14 пикселей, получается что ширина дорожки примерно 14/3488 * 1мм = ~0.004 мм. Итого - 4 микрона, бинго! :)
Вопрос знатокам истории. Фото фрагмента 1801ВМ2 двумя постами выше сделано с процессора с маркировкой Ангстрема (серпасто-молоткастый) 91.05. На фотографии хорошо видно сигнатуру ТР4. С другой стороны, на форуме радиокартинки (http://radiopicture.listbb.ru/viewtopic.php?f=3&t=28&st=0&sk=t&sd=a&start=60) такой же кристалл с сигнатурой ТР4 находится в корпусе с маркировкой СЭМЗ, дата 93.05. Уже понятно, что были разные наборы фотошаблонов, поскольку существуют фотографии кристаллов 1801ВМ2 с незначительно отличающейся топологией. Но, получается, что оба завода работали с разными наборами фотошаблонов в разное время, то есть, "старая" и "свежая" топологии могли выпускаться на обоих заводах?
Хотелось бы, конечно, быть уверенным, что кристаллы у меня ангстремовские. В-общем-то, у меня этих А91.05 четыре штуки из одной партии, поэтому фотографировать придется именно их, остальные мои ВМ2 - разнобой, а нужно минимум два одинаковых процессора для снимков металла (остается целым как эталон для непоняток) и диффузии (этот шлифуется).
То есть, часть исключений сохраняет адрес следующей инструкции, а часть - адрес текущей, откатывая его назад на слово.
Из БКшного опыта известно, что IRQ1 не стремится сохранить адрес следующей инструкции.
Блокировку клавиши СТОП на БК10 делали так:
V4: SUB (PC),(SP)
RTI
Т.е. возврат из прерывания по вектору 4 при нажатии на кнопу СТОП приходил на начало той же инструкции, во время исполнения которой и произошло прерывание.
Для двухсловных и трёхсловных команд адрес возврата из IRQ1 приходился на второе слово поэтому и вычитали 2 из адреса возврата, но для трёхсловных в очень редких случаях адрес возврата приходился и на третье слово. Так что если достаточно упорно и быстро долбить по клавише СТОП - то БК10 рано или поздно зависала при такой блокировке клавиши СТОП.
А какие ещё исключения сохраняли адрес текущей инструкции? Обращение по несуществующему адресу?
Из БКшного опыта известно, что IRQ1 не стремится сохранить адрес следующей инструкции.
Видимо контроллер прерываний по IRQ1 подсовывает блоку микропрограммного управления PLI=011, и для БМУ прерывание IRQ1 выглядит как будто он выбрал код HALT, увеличил PC++ и выполнил HALT. С его точки зрения законно не откатывать PC и сохранить адрес следующей инструкции после HALT. Табличка PLI (там всего 3 бита) у меня еще не закончена, будет уточняться, пока только ясность:
000 - ожидание снятия ACLO после сброса
001 - нет прерываний
011 - HALT (и возможно IRQ1)
100 - векторное прерывание (и возможно IRQ2/IRQ3)
101 - программное исключение IOT/TRP/EMT/BPT
111 - выполнить начальную последовательность старта
Блокировку клавиши СТОП на БК10 делали так:
V4: SUB (PC),(SP)
RTI
Сначала впал в ступор, потом вспомнил что опкод RTI равен двум, недавно смотрел :)
но для трёхсловных в очень редких случаях адрес возврата приходился и на третье слово.
А это может быть очень интересный косяк микропрограммы, очень странно - прерывание обработано на середине команды. Двухадресных команд немного, поэтому можно проанализировать. А может и какой чисто аппаратный глюк процессора, все-таки у нас только логическая модель. Да и HALT/IRQ1 в БК "неродные", потому что падают по тайм-ауту записи в 177676.
А какие ещё исключения сохраняли адрес текущей инструкции? Обращение по несуществующему адресу?
Это надо прогнать все ситуации и задокументировать, буду писать раздел "Прерывания и исключения" - сделаю.
А это может быть очень интересный косяк микропрограммы
Это скорее всего не косяк. В различных описаниях 1801ВМ1 писалось, что IRQ1 - безусловное не маскируемое прерывание (что как выяснилось, не совсем правда, таки маскируемое). Если оно происходит, то процессор бросает всё и начинает исполнять его. Т.е. по этой логике IRQ1 может прерывать исполнение инструкции как после чтения опкода, так и после чтения аргумента источника в трёхсловной команде, смотря на какой фазе произойдёт. А соображения здравого смысла (по крайней мере моего) говорят, что в пульт лучше переходить не с середины команды, а после её выполнения.
Просто как раз этот механизм мне не совсем понятен и я эмулирую его крайне упрощенно, в силу своего понимания происходящего.
Нажали СТОП -> приходит IRQ1 -> попытка вывалиться в пульт -> зависание по тайм-ауту записи в 177676 -> прерывание по вектору 4. И вот адрес выхода из прерывания по вектору 4 статистически в большинстве случаев указывает на слово, следующее за опкодом. Для однословных команд - это адрес следующей команды, для многословных - адрес аргумента источника. Вот как раз такую штуку моя текущая модель процессора делать не умеет.
писалось, что IRQ1 - безусловное не маскируемое прерывание (что как выяснилось, не совсем правда, таки маскируемое). Если оно происходит, то процессор бросает всё и начинает исполнять его
Теперь мы знаем о внутренностях процессора немножко больше :)
Структурно IRQ1 приходит снаружи ТОЛЬКО на матрицу шифратора прерываний. А выход шифратора осуществляется в:
- трехбитный регистр PLI (он же P1X)
- четырехбитный регистр выбора вектора из таблицы констант
- четырехбитный номер подтверждения прерывания (чтобы сбросить входные защелки и детекторы фронта запросов)
На поток исполнения влияет только PLI. Это такой 3-х битный регистр, выход его подается прямо на основную матрицу ПЛМ и влияет на адрес и исполнение следдующей микрокоманды. Этот же PLI может записываться как с выхода шифратора приоритетов прерываний, так и с части выходов ПЛМ по специальному стробу (тогда я его называю P1X, физически это тот же самый регистр, надо будет переобозвать в PLI). То что это PLI может записываться также из основной матрицы матрицы позволяет эмулировать прерывание по командам HALT/IOT итд. Эти команды перегружают PLI своим значением (там ранее находилось записанное ранее шифратором 001 - нет прерываний) и повторяют цикл 7Е.
Т.е. по этой логике IRQ1 может прерывать исполнение инструкции как после чтения опкода
Вот это понятно и наблюдается, IRQ1 тождественнен выбору опкода HALТ.
, так и после чтения аргумента источника в трёхсловной команде, смотря на какой фазе произойдёт
Вот это непонятно. Напишу утилитку для анализа микроадресов и выясню кто еще из них может реагировать на PLI. Потому что переход на микропрограмму HALT (микроадрес 62) происходит ТОЛЬКО из основной точки старта 7Е - начало разбора инструкции в IR, PC указывает на следующее за опкодом слово, PLI актуальное. Другое дело что на 7Е много кто еще неизученный ходит, а вдруг кто на 7Е ошибочно переходит после выборки поля источника при наличии IRQ1?
....
- адрес аргумента источника. Вот как раз такую штуку моя текущая модель процессора делать не умеет.
Нет желания написать аутентичную модель на основе эмуляции микрокода? Уже сейчас можно взять модельку, грузить в нее свои тесты и разбираться в ModelSim-е. Также в помощь выложу исходник на С вычисляющий основную матрицу.
Желание есть, моральных сил взяться за дело недостаточно. К тому же китайские иероглифы мне кажутся более понятными, чем модель в верилоге. Вроде бы и конструкции структур, и синтаксис выглядят знакомо и узнаваемо, но не складывается в голове образ, как оно всё работает, а без этого, каким бы ни было желание, всё равно ничего не напишется. Может на самом деле всё гораздо проще, чем мне кажется, но у меня сейчас полностью отсутствует представление, какая программная модель должна получиться в результате, а это тоже серьёзный довод против.
На освоение верилога и ModelSim'а по моим сейчашним прикидками уйдёт столько времени, что Patron успеет написать аутентичные модели всего семейства 1801ВМх
Желание есть, моральных сил взяться за дело недостаточно. К тому же китайские иероглифы мне кажутся более понятными, чем модель в верилоге.
А если я перепишу Верилог на Си, сил хватит? :) Переписывание не должно занять много времени, если не заниматься оптимизацией - паковать биты и прочее. Можно даже поискать автоматический транслятор Верилог->Cи, тут Patron упоминал про такой - преобразует Верилог в объект C++.
какая программная модель должна получиться в результате
Основа, грубо говоря, один объект с методами - вызываются при изменении входных сигналов процессора. Потом основной метод - обработка двух событий - возрастающего и ниспадающего фронта клока. Можно оставить только эти два метода, а остальные сигналы подавать как аргументы этих двух методов - все равно состояние объекта меняется только по клокам. Ну и красиво показывать внутреннее состояние - для отладки.
Сам я писать эмулятор не буду - без готовых наработок займет много времени, а тут у нас на форуме есть продвинутые эмуляторщики PDP - как минимум Вы, Patron, Titus (извините если кого забыл).
А если я перепишу Верилог на Си, сил хватит?
Это смотря как переписать.
Можно даже поискать автоматический транслятор Верилог->Cи, тут Patron упоминал про такой
Попробовал я поконвертировать этим Verilator'ом. Модель VM1 из архива vm1_rev12j из директории Async он переварить не смог, пишет такую вещь:
%Error-MODDUP: vm1_plm.v:1492: Duplicate declaration of module: vm1_pli
%Error-MODDUP: vm1_qbus.v:856: ... Location of original declaration
Впрочем и на модели из более ранних архивов так же ругается, только номера строк другие.
Хотя вроде бы всё не так, в строке 1492 vm1_plm.v - объявление модуля, а в строке 856 vm1_qbus.v - его использование, и никакого объявления. Да ещё и Multiple top level modules ему не нравятся. В общем, всё равно, не понимая, что там к чему в верилог файле, сконвертировать как надо вряд ли получится.
Зато второй подопытный - vp_037.v сконвертировался с кучей варнингов об неоптимизабельных сигналах. На выходе получились те же яйца, только в профиль - ничего не понятно, но теперь на с++, к тому же сильно разбухшая каша из кода, в которой от строгости и лаконичности верилога не осталось и следа.
Переписывание не должно занять много времени, если не заниматься оптимизацией
А и не надо никаких оптимизаций, их всегда можно сделать потом. Я более чем уверен, что у Вас переписание верилога на Си, не важно ++ или просто С, получится эффективнее, чем у автоматического транслятора. К тому же, как мне кажется, сопоставление одного и того же, описанного на Си и Верилоге, поможет в будущем лучше понимать Верилог.
А я, если честно, ожидал окончания реверсинга и написания документации, потому что, имея словесное описание разобраться в алгоритмах на верилоге гораздо проще, поэтому до этого времени просто наблюдал за темой.
Попробовал я поконвертировать этим Verilator'ом
Спасибо за попытку, в общем ясно что быстро и гладко не получится.
А я, если честно, ожидал окончания реверсинга и написания документации,
Угу, я ни за что новое не берусь, даже микросхемы накопившиеся не декаплю и не фотаю, все небольшое свободное время - на окончание реверса ВМ1. Но эмулятор был бы полезен для анализа микрокода. В-общем, замкнутый круг - эмулятор нужен для анализа, анализ нужен для документации, а документация нужна для эмулятора. Оки, буду обходится модельсимом, в принципе, все что надо он обеспечивает, просто не так удобно.
ram_scan
29.03.2015, 17:59
А какие ещё исключения сохраняли адрес текущей инструкции? Обращение по несуществующему адресу?
Я сейчас возможно скажу по дилетснтски офигенную глупость, но опыт ковыряния и чтения доки на 386+ процессоры подсказывает, что сохранять адрес текущей инструкции есть смысл только тогда, когда какие-то телодвижения в обработчике исключения делают разумным ее повторное исполнение без дополнительных танцев с саблями на тему "куда вернуться" уже без возникновения исключения.
То есть когда обработчик исключения способен исправить причину исключения. Например отгруженая в своп страница в памяти. Обработчик способен ее подгрузить и перезапустить команду. А обращение по недействительному адресу ведет за собой снятие программы, поэтому адрес инструкции не настолько важен, по дампу программист можеет и назад вернуться.
От этого наверное и надо отталкиваться.
Спасибо за попытку
Но пытаться я не перестану.
Для начала я тут попробовал переписать на Си ВЕ таймер из файла vm1_tve.v, т.к. он выглядит самым простым.
Получилось такое 51680
На ошибки и компилируемость не проверял, т.к. это просто первичный набросок, с целью выяснения, на правильном ли я пути. Приняты упрощения:
1) логика только булева, состояний x и z нет.
2) wire и однобитовые reg приняты как тип BOOL, многобитовые reg - BYTE и WORD в зависимости от битности.
3) собственно таймер - несамостоятельная штука, вызываемая извне, имеет одну функцию с кучей аргументов - входными и выходными параметрами.
4) оптимизаций с целью получить правильный, с точки зрения традиций, код Си не проводилось, пока в этом нет необходимости.
Соответствует ли то, что получилось у меня оригиналу? Особенно интересуют логические операции, и порядок выполнения.
1) логика только булева, состояний x и z нет.
Да, правильно, и эти состояния у меня в моделях не используются, только 'x' в сравнении с паттерном иногда, но это легко реализовать через ^/&.
Особенно интересуют логические операции, и порядок выполнения.
Общая evel логика такая:
- назначаем переданные входные переменные
- отрабатываем все операторы assign во всем модуле таймера, вычисляем внутренние и выходные переменные. Бывает еще оператор always эквивалентный assign, но я такие не использую, поэтому не паримся
- смотрим, есть ли событие клока (clk или сброс)
- если есть:
- отрабатываем все блоки always с этим событием
- снова обрабатываем assing (переменные же сменились)
- возврашаем выходные переменные
Я вроде вот этой второй отработки блока assign-ов после сработки событий клока в Вашем коде не увидел.
Еще момент, вероятно, Вы знаете, но лучше уточнить. Все назначения в always у меня неблокирующие, то есть выполняются один раз за событие, и все назначения выполненные при событии в предыдущих строках этого блока не должны влиять на последующие. То есть, надо как бы завести снапшот текущего состояния входных переменных блока always, и выполнить вычисление текущих переменных используя входные переменные из этого замороженного снапшота. Да, и снапшот должен быть общим для всех блоков always по этому событию, они как бы выполняются одновременно и независимо, при этом используя только заснапшоченные значения переменных. Более того - снапшот должен быть общим для всех модулей системы, не только таймера.
Поэтому я бы методы назначения параметров и методы обработки событий (клоки и сбросы) разнес бы. Тогда модули сами снапшотили бы свое состояние при обработке событий, и можно было бы обработать события на всех модулях отдельными методами, а потом уже по результатам выполнить все assign-ы и назначения именившихся входных параметров.
Для assign-ов ситуация противоположная, надо их вычислять каждый раз когда меняется хоть одна использованная в них переменная, назависимо ни от каких событий. Поэтому - для вычисления в блоках assign порядок важен, чтобы не вычислять одно и то же по нескольку раз. В принципе, на порядок можно забить, поместить их всех в цикл в произвольном порядке и крутить до тех пор пока выходное состояние не "устаканится", но как бы не очень красивое решение. Но в случае с несколькими модулями (ведь эти assign-ы могут находится в разных модулях), если нельзя влезать внутрь - пригодилось бы.
Что-то как-то слишком сложно и запутанно.
Т.е. получается, что возвращаемые значения берутся из снапшота предыдущего состояния, до вычисления блоков always. Либо после assign, но до вычисления блоков always? А результат вычислений блоков always станет выходным значением при следующем вызове?
Ещё такой момент.
assign tve_zero = (tve_count == 16'h0000) & ~tve_csr[1];
assign tve_back = tve_csr[4] & tve_csr[1] & tve_spclk & tve_tclk4;
assign tve_load = tve_zero & tve_tclk4 & ~tve_csr[1];
В assign tve_load используется какое значение tve_zero, которое вычислено в вышеидущем assign tve_zero, или предыдущее его значение, до этого assignа?
Я вроде вот этой второй отработки блока assign-ов после сработки событий клока в Вашем коде не увидел.
Ещё б. Я же этого не знал.
Еще момент, вероятно, Вы знаете
Именно что не знаю. И всех тонкостей, описанных в посте, не знал.
Поэтому я бы методы назначения параметров и методы обработки событий (клоки и сбросы) разнес бы
Очень не помешал бы пример реализации. Я смотрел код, который строит Verilator, там всё очень и очень сложно и похоже на то, что вы описываете. Но уж очень сложный код там строится, мне почему то кажется, что можно это сделать проще, тем более если подойти к модулям индивидуально.
поместить их всех в цикл в произвольном порядке и крутить до тех пор пока выходное состояние не "устаканится"
А если assignы взаимно модифицируют значения друг-друга, то мы зациклимся навсегда, или это признак неправильно построенной схемы?
Вторая итерация 51688
Что-то как-то слишком сложно и запутанно.
Увы, я не очень хороший объясняльщик :| Не стесняйтесь, задавайте уточняющие вопросы, попробую ответить.
Т.е. получается, что возвращаемые значения берутся из снапшота предыдущего состояния, до вычисления блоков always. Либо после assign, но до вычисления блоков always?
Это зависит от того чем является возвращаемое значение - assign или always. Assign - это логическая комбинационная функция. Если assign - то надо вернуть результат немедленно, если always - то из снапшота.
Я старался писать в такой парадигме - assign это логика, always - триггеры. Вроде нигде этот принцип не нарушал (там ниже будет особый случай always - асинхронный сброс).
assign y = ~(x1 & x2);
Это аналог ЛA3. Никакой памяти предыдущего состояния. Есть на входе набор переменных - на выходе получаем однозначные результаты. Но таких assign-ов несколько, часть выходов одних может являться входами для других - это микросхемки ЛА3 соединили последовательно.
always @(posedge clk) q<= d;
Это аналог триггера, типа ТМ2, например.
А результат вычислений блоков always станет выходным значением при следующем вызове?
Можно и сразу, главное не передавать их другим модулям до того как они отработают свои always. То есть - вызывали метод события клока, модуль внутри взял свой снапшот, вычислил все always, вычислил все assign до статического состояния (когда больше в комбинационных функциях ничего не меняется, а замкнутых петель, порождающих генерацию, нет - на них квартус ругался бы), сохранил новое состояние в актуальных переменных, снапшот выбросил в мусорку и пока все - аналогично вызываем события клока для остальных модулей в системе. А потом уже начинаем обрабатывать связи между модулями как assign-ы, сообщая им новые вычисленные состояния других модулей. При этом могут возникнуть события по типу асинхронного сброса (установки вроде нигде в ВМ1 не применял), но такое как комбинационная логика работает - можно (и наиболее просто) обработать как обычный assign.
Ещё такой момент.
assign tve_zero = (tve_count == 16'h0000) & ~tve_csr[1];
assign tve_back = tve_csr[4] & tve_csr[1] & tve_spclk & tve_tclk4;
assign tve_load = tve_zero & tve_tclk4 & ~tve_csr[1];
В assign tve_load используется какое значение tve_zero, которое вычислено в вышеидущем assign tve_zero, или предыдущее его значение, до этого assignа?
Это assign - значит берется вычисленное новое значение. Можно этот блок взять в цикл и крутить его до тех пор пока выходные значения не перестанут меняться - то есть все вычислено окончательно:
do
{
tve_zero_prev = tve_zero;
tve_back_prev = tve_back;
tve_load_prev = tve_load;
tve_zero = (tve_count == 16'h0000) & ~tve_csr[1];
tve_back = tve_csr[4] & tve_csr[1] & tve_spclk & tve_tclk4;
tve_load = tve_zero & tve_tclk4 & ~tve_csr[1];
}
while( (tve_zero_prev != tve_zero)
||(tve_back_prev != tve_back)
||(tve_load_prev != tve_load));
Этот цикл необязателен и неоптимален, просто демонстрация что результат его выполнения не будет зависеть от порядка операторов присвоения значений, только количество итераций. А если все проанализировать и выбрать верный порядок - то только одна итерация, второй уже фактически не будет - выходные значения не меняются, произойдет выход из цикла.
Очень не помешал бы пример реализации.
Хорошо, для начала пример синхронного сдвигового регистра с асинхронным сбросом подойдет? Возьму модуль из одного триггера, соединю модулей несколько в цепочку, и рассмотрим обработку?
Я смотрел код, который строит Verilator, там всё очень и очень сложно и похоже на то, что вы описываете. Но уж очень сложный код там строится, мне почему то кажется, что можно это сделать проще, тем более если подойти к модулям индивидуально.
Конечно, можно проще, мы же не пишем универсальный моделятор произвольного Верилога, а вполне конкретную модель вполне конкретного процессора написанного в едином стиле без особых HDL-ухищрений, поэтому подходим индивидуально. А Верилятор просто не очень хорошо с анализом справился, видимо.
А если assignы взаимно модифицируют значения друг-друга, то мы зациклимся навсегда, или это признак неправильно построенной схемы?
Не зациклимся, квартус за такое по голове бьет, поэтому петель обратной связи быть не должно. Вообще, если циклов вычисления assign понадобится больше одного до "устаканивания" - это значит просто что мы неоптимально выбрали порядок их вычисления. На Верилоге порядок значения не имеет, а вот для программного последовательного исполнения - важно.
Вторая итерация
Да, уже лучше. Такой момент - события reset проще рассматривать как комбинационные, это НЕ тактовый сигнал, это просто такая форма описания асинхронного сброса (через always). То есть, во всех блоках always что я применял, этот сброс работает как асинхронный - если пришел, то выход немедленно обнуляется и удерживается в нуле несмотря на все события. Это как вход R в микросхемке ТМ2. То есть - тактовых в модели ВМ1 всего два события - положительный и отрицательный фронты клока - posedge vm_clk_p и posedge vm_clk_n.
Можно и сразу, главное не передавать их другим модулям до того как они отработают свои always
Ну так для того и вводится класс, с набором своих внутренних переменных, недоступных никому и функцией eval, которая принимает набор входных переменных, изменяет по своему заданному алгоритму внутренние переменные и выдаёт в конце работы результаты. Пока функция не отработает, результат никому доступен не будет.
Хорошо, для начала пример синхронного сдвигового регистра с асинхронным сбросом подойдет?
Ок, лишь бы был нетривиальный случай. А ещё было бы неплохо посмотреть, как assign со временем (assign #5.25 y1 = x1; ) можно реализовать.
события reset проще рассматривать как комбинационные
Т.е. так?
if (tve_reset)
{
//отрабатываем все блоки always для reset
}
else if (posedge(tve_clk, tve_clk_prev))
{
//отрабатываем все блоки always для clock
} как раз, пока ресет активен ничего вообще не работает, на клок реакции не будет.
---------------------------
Вот кстати ещё вопрос возник.
В модуле vp_037 есть сотни вызовов других модулей. Предполагается, что все они вызываются одновременно, и выходное значение предыдущего модуля не влияет на это же входное значение последующего, или их можно выполнять последовательно как assignы? Чтобы на вход одного модуля приходило значение, изменённое в ранее вызванном модуле?
if (tve_reset)
{
//отрабатываем все блоки always для reset
}
else if (posedge(tve_clk, tve_clk_prev))
{
//отрабатываем все блоки always для clock
}
Да, примерно так. У меня сейчас со временем туго, возможно, на выходных попробую написать пример простого загружаемого счетчика на верилоге и С.
Вот кстати ещё вопрос возник.
В модуле vp_037 есть сотни вызовов других модулей. Предполагается, что все они вызываются одновременно, и выходное значение предыдущего модуля не влияет на это же входное значение последующего, или их можно выполнять последовательно как assignы?
Вот как раз в 037 и во многих остальных автосгенерированных моделях ВП1 есть петли обратной связи и моделировать "в лоб" их нельзя. Квартус матерно ругается на петли, синтезирует с "unsafe behaviour", модельсим же такое переваривает и спокойно моделирует, но это уже далеко не примитивный симулятор.
Все автогенерированные модели ВП1 не предназначались для синтеза, только для моделирования. По-хорошему - 037 надо нормально переписать ручками на вменяемом Верилоге, но я думал что в таком виде - с шиной Qbus - оно никому в синтезабельном виде не понадобится. Потому что саму реальную микросхему никому не надо, а в FPGA оно потребует другой шины и другого интерфейса к памяти, то есть от оригинала не останется ничего.
Я думаю, что пока следует на 037 не смотреть вообще, и с эмуляцией времянок типа #5 не заморачиваться, потому что это очень-очень сильно усложнит эмулятор - придется делать полноценную модель с событиями и их диспетчирезацией. К тому же неадаптированная модель 037 потянет за собой модель платы БК и модели динамической памяти 565РУ5/РУ6. В-общем, имеющийся сейчас автосгенерированный 037 - это очень плохой пример для понимания и написания эмулятора.
ИМХО, пока следует сосредоточится на модели процессора и просто подсовывать ему данные по Qbus-у с регулируемой задержкой по RPLY, когда я буду заниматься БК, то перепишу 037 во вменяемый вид, она несложная, займет немного кода и ее будет гораздо легче эмулировать чем макароны из Базовых Функцикональных Ячеек 1801ВП1.
Я думаю, что пока следует на 037 не смотреть вообще
Ну тогда аналогичный вопрос про vm1_qbus.v, вызов внутри модулей vm1_timer, vm1_pli, vm1_plm обрабатывается по принципу как assign или тоже им на вход нужен снапшот текущего состояния данных, как для always?
Ну тогда аналогичный вопрос про vm1_qbus.v, вызов внутри модулей vm1_timer, vm1_pli, vm1_plm обрабатывается по принципу как assign или тоже им на вход нужен снапшот текущего состояния данных, как для always?
Зависит от того что внутри модуля и чем формируется выходной сигнал - ЛА3 или ТМ2. Общая картинка такая - есть у нас топовый проект на верилоге, скажем, что это плата, на ней стоят ЛА3 (assign) и ТM2 (always) - внутренние операторы, и есть какие-то еще именованные модули. Вот эти модули можно рассматривать как отдельные более сложные микросхемы/платы внутри у которых тоже ЛА3 и ТМ2. В итоге эмулятор должен докопаться до ЛА3/ТМ2 которые стоят на самом нижнем уровне, т.e. сосбственно до неделимых assign/always, независимо сколько оберток-модулей над ними наворочено.
Непонятно. Значит зайдём с другого бока.
Внутри модуля vm1_qbus вызывается
vm1_pli pli_matrix(.rq(rq), .sp(pli)); и vm1_plm plm_matrix(.ir(ir), .mr(ma), .sp(plx));
и ещё vm1_timer timer(много параметров);
Первые два модуля тривиальны, там внутри только assignы, и их можно рассматривать как обычные функции y=f(x), выдающие мгновенное текущее выходное значение, в зависимости от входного значения, vm1_timer чуть посложнее. А среди входных значений для всех них есть регистры, которые модифицируются внутри always.
Так вот, какое входное значение для них должно быть? То же, что и для блоков always, т.е. снапшот состояния перед выполнением всех always или после выполнения или в середине выполнения, там где вызов по коду встретился?
И когда можно выполнять вызов vm1_pli, vm1_plm и vm1_timer , вместе со всеми assignами для vm1_qbus или вместе со всеми alwaysами?
Когда мы пишем программу на обычном Си, мы не можем заставить все функции выполняться одновременно, для этого есть другие языки и средства, поэтому нужно расписать что за чем последовательно выполняется.
И ещё такой вопрос. Как я понимаю, assignы назначают значения только переменным типа wire, а внутри always модифицируются переменные только типа reg или есть исключения?
И имеет смысл снапшотить только те переменные, которые сами модифицируются внутри always. А то в vm1_qbus переменных чуть больше чем дофига, и надо как-то оптимизировать процесс снапшотинья.
, выдающие мгновенное текущее выходное значение, в зависимости от входного значения, vm1_timer чуть посложнее. А среди входных значений для всех них есть регистры, которые модифицируются внутри always.
Так вот, какое входное значение для них должно быть?
То же, что и для блоков always, т.е. снапшот состояния перед выполнением всех always или после выполнения или в середине выполнения, там где вызов по коду встретился?
Аргументы должны делиться не по тому КОМУ они поступают, а по тому ЧТО является их источником. Если источником является сигнал, назначаемый в always, то это триггер ТМ2, он на выход выдает значение из снапшота пока вся схема не отработает событие клока. Если источник assign - то это ЛА3, надо использовать текущее значение. Если assign на входе имеет только аргументы с триггеров, то и результат его будет фактически из снапшота. Если assign использует еще какие внешние сигналы (допустим внешние входы процессора), то результат будет меняться мгновенно при измении этих входов.
И когда можно выполнять вызов vm1_pli, vm1_plm и vm1_timer , вместе со всеми assignами для vm1_qbus или вместе со всеми alwaysами?
assign-ы модулей выполнять вместе со всеми assign-ами
always-ы модулей выполнять вместе со всеми always-ами
Вот есть многоплатная/многомодульная система, все равно на какой плате находится ЛА3 - она работает точно так же как и ЛА3 на других платах. Или ТМ2 - все равно где он находится, он будет работать синхронно с другими ТМ2 на других платах, если у них тактовая общая. Так и тут - все равно где, в каком модуле или файле находится assign - он работает параллельно со всеми остальными assign-ами в проекте. И always-ы - точно так же.
средства, поэтому нужно расписать что за чем последовательно выполняется.
Для комбинационных схем без петель обратной связи нет разницы в порядке выполнения (если будем крутить цикл до "устаканивания", а для триггеров мы вводим снапшот, чтобы нивелировать последовательное вычисление. То есть, есть массиыв триггеров, на вход им приходят значения, и мы берем триггеры из этого массива и вычисяем по одному, а результаты заносим в другой массив, чтобы не ранее вычисленные триггеры не мешали вычислять оставшиеся.
Я предлагаю упростить еще представление таким образом. В блоках always оставить только присвоения триггерам. А выражения представить как функции assign. Пример, было:
always @(posedge vm_clk_n)
begin
plm_ena <= ~sop_out[0] & (mjres | ustb1_h | ~alu_busy_rp) & ~qbus_nrdy;
end
Стало:
assign plm_ena_fc = ~sop_out[0] & (mjres | ustb1_h | ~alu_busy_rp) & ~qbus_nrdy;
always @(posedge vm_clk_n)
begin
plm_ena <= plm_ena_fc;
end
Абсолютно весь проект можно свести к парам "комбинационная функция-триггер":
assign v1 = f1(x1 ... xN, t1 ... tM)
always @clock t1 <= v1;
В исходном проекте функция f1 может быть "размазана" по нескольким модулям, так же как триггеры tn могут находится в разных модулях (они не "размазываются", входят в модуль целиком). Я много где в таком виде писал, суффикс "_fc" означает входная функция триггера срабатывающего по falling clock, а "_rc" - по raising clock.
Сначала вычисляем все комбинационные функции ("ЛА3"), включая асинхронные сбросы триггеров. А потом просто переписываем вычисленные значения в триггеры ("ТМ2") для которых имеет место событие клок. Потом можно снова вычислить комбинационные функции по новым значениям триггеров, можно только для тех которые формируют выходные сигналы для провышения скорости. В этом случае роль снапшота играет массив "вычисленные значения функций".
И ещё такой вопрос. Как я понимаю, assignы назначают значения только переменным типа wire, а внутри always модифицируются переменные только типа reg или есть исключения?
Да, исключений не бывает, в моем проекте ВМ1 так точно.
Похоже, мы не понимаем друг друга из-за принципиально различных базовых понятий программиста и железячника.
assign-ы модулей выполнять вместе со всеми assign-ами
always-ы модулей выполнять вместе со всеми always-ами
т.е. следуя этой логике, vm1_pli pli_matrix(.rq(rq), .sp(pli)); нужно внутри модуля vm1_qbus развернуть как include и все его assignы выполнять как assignы модуля vm1_qbus и не вызывать его как внешнюю функцию. Там и без того получается описание функции длиной с рулон обоев, надо как-то упрощать, разбивать на подфункции.
Для комбинационных схем без петель обратной связи нет разницы в порядке выполнения
А для программы есть. Программ - это последовательное исполнение инструкций одна за другой. Поэтому важен порядок правильного описания последовательности действий. Даже если все действия должны выполняться параллельно одновременно, при алгоритмическом описании, их выполнение расписывается последовательно для определённого кванта времени. Поэтому хочешь-не хочешь, а придётся как-то расписать порядок последовательного выполнения всех assignов и alwaysов, таким образом, чтобы это соответствовало их реальному одновременному параллельному исполнению в определённые дискретные промежутки времени.
Поэтому, если с assignами и alwaysами как-то более-менее понятно, то с вызовом модуля из другого модуля, если его рассматривать именно как внешнюю функцию, возникает неясность.
Я предлагаю упростить еще представление таким образом.
С моей точки зрения это наоборот усложнение. Если always сложный и развесистый, то количество используемых переменных может возрасти кратно, может не получиться выстроить все assignы в порядке, когда не нужно их крутить в цикле. А выход из цикла - сравнение предыдущего состояния списка переменных с новым, там одно это сравнение будет сколько ресурсов отжирать.
Увы, я не очень хороший объясняльщик. Кроме того, хотя я также являюсь и программистом, мне тоже приходится по ходу дискуссии обдумывать как это все лучше реализовать программно. Так что польза от обсуждения обоюдная :)
т.е. следуя этой логике, vm1_pli pli_matrix(.rq(rq), .sp(pli)); нужно внутри модуля vm1_qbus развернуть как include и все его assignы выполнять как assignы модуля vm1_qbus и не вызывать его как внешнюю функцию.
Да, по сущности это инклуд/макрос. Включаются внутренние блоки модуля. Но, синтаксически, сами сигналы можно рассматривать не как переменные, а как функции. Типа:
plm_ena_fc_var = plm_ena_fc(....);
Тогда очень уж сильного разворачивания можно избежать и использовать много готового кода из имеющегося верилога. И часть модулей можно будет представить как функции. Вот vm1_pli и vm1_plm содержат только assign - их можно представить как фукнции. vm1_timer - уже имеет внутри триггеры, его как функцию не представить.
Поэтому, если с assignами и alwaysами как-то более-менее понятно, то с вызовом модуля из другого модуля, если его рассматривать именно как внешнюю функцию, возникает неясность.
Нет, Вы правильно сказали, модуль надо рассматривать как include или макрос, в том смысле что его внутренние блоки включаются в проект и внутренний текст разворачивается - все внутренние assign и always включаются в текущий уровень вложений. То есть модуль - это НЕ вызов функции/функций в общем случае. Отдельный assign - да, можно рассмотреть как вызов программной функции. Кстати, в Верилоге есть свои таски и функции, хорошо что я их тут не применял :)
С моей точки зрения это наоборот усложнение. Если always сложный и развесистый, то количество используемых переменных может возрасти кратно
always не может быть сложным и развеститым, его суть - указать триггеры которые назначаются по событию клока. То, что там внутри понаписаны какие-то функции - это просто синтаксическое удобство, внос неявного assigna. Я просто предложил сделать их явными. Тогда в always вообще не придется ничего вычислять, просто тупо переписать в триггеры по событию клока результаты из массива вычисленных значений функций. Никаких снапшотов и раздумий что и откуда брать.
может не получиться выстроить все assignы в порядке, когда не нужно их крутить в цикле.
Получится, абсолютно весь проект сводится к парам функция/триггер. И все функции имеют в качестве аргументов внешние входные значения (для модуля процессора это внешние пины) и триггеры. Если входным значением вдруг является значение другой функции - то эта другая все равно в итоге доходит до внешних параметров и триггеров. То есть любая функция в проекте зависит только от внешних сигналов и состояния триггеров. Все функции-аргменты являются промежуточными и в итоге тоже приходят к зависимости только от внешних сигналов и триггеров.
Эту промежуточную функцию-аргумент можно или вычислить ее значение и "закешировать" для использования в других выражениях (как аргумент других функций) - то есть назначить результат какой-то переменной, или для начала, для простоты, тупо вычислять каждый раз - тогда не нужен цикл, все результаты будут актуальными сразу.
А выход из цикла - сравнение предыдущего состояния списка переменных с новым, там одно это сравнение будет сколько ресурсов отжирать.
Да, цикл он только для иллюстрации был, как метод вычисления, независящий от порядка операторов. Не лучшая идея, согласен.
---------- Post added at 18:26 ---------- Previous post was at 17:51 ----------
Давайте вместе подумаем на совсем простом примере, я вот набросал 155ИР13, синхронный сдвиговый регистр, с параллельной загрузкой и асинхронным сбросом, чтобы не возиться с описанием взял стандартную микросхему.
module ir13
(
input rst_n, // async reset, active low
input clk, // sync clock, raising edge
input [2:1] s, // shift mode
input [7:0] d, // parallel load data
input dsr, dsl; // new shifted in data bit
output reg [7:0] q // output data
);
wire [7:0] q_rc;
//
// assign form is used deliberately for illustrative purposes
//
assign q_rc[0] = (s == 2'b00) & q[0] // storage
| (s == 2'b01) & q[1] // shift left
| (s == 2'b10) & dsr // shift right
| (s == 2'b11) & d[0]; // parallel load
assign q_rc[1] = (s == 2'b00) & q[1] // storage
| (s == 2'b01) & q[2] // shift left
| (s == 2'b10) & q[0] // shift right
| (s == 2'b11) & d[1]; // parallel load
assign q_rc[2] = (s == 2'b00) & q[2] // storage
| (s == 2'b01) & q[3] // shift left
| (s == 2'b10) & q[1] // shift right
| (s == 2'b11) & d[2]; // parallel load
assign q_rc[3] = (s == 2'b00) & q[3] // storage
| (s == 2'b01) & q[4] // shift left
| (s == 2'b10) & q[2] // shift right
| (s == 2'b11) & d[3]; // parallel load
assign q_rc[4] = (s == 2'b00) & q[4] // storage
| (s == 2'b01) & q[5] // shift left
| (s == 2'b10) & q[3] // shift right
| (s == 2'b11) & d[4]; // parallel load
assign q_rc[5] = (s == 2'b00) & q[5] // storage
| (s == 2'b01) & q[6] // shift left
| (s == 2'b10) & q[4] // shift right
| (s == 2'b11) & d[5]; // parallel load
assign q_rc[6] = (s == 2'b00) & q[6] // storage
| (s == 2'b01) & q[7] // shift left
| (s == 2'b10) & q[5] // shift right
| (s == 2'b11) & d[6]; // parallel load
assign q_rc[7] = (s == 2'b00) & q[7] // storage
| (s == 2'b01) & dsl // shift left
| (s == 2'b10) & q[6] // shift right
| (s == 2'b11) & d[7]; // parallel load
always @(posedge clk or negedge rst_n)
begin
if (~rst_n)
q <= 8'b00000000;
else
q <= q_rc;
end
endmodule
Теперь надо подумать как это представить на С++, можете шаблон создать? Я в плюсах писатель не очень.
vm1_timer - уже имеет внутри триггеры, его как функцию не представить.
Вот этот момент, хоть убей, не могу понять, почему нет? vm1_timer - предполагаем что это некая микросхема с входом и выходом, её внутреннее состояние модуль vm1_qbus вообще никак не должно волновать, потому что он не использует у себя где либо ещё внутренние переменные таймера. Поэтому вообще нет никаких причин не мочь его вызвать как функцию. Просто его надо вызывать среди других alwaysов.
Программная эмуляция предполагает расчёт состояния процессора в определённые дискретные моменты времени, только во время переднего и заднего фронтов ТЧ, в остальное время что происходит с процессором знать нет необходимости. И как-то увязывать внутренности модуля vm1_timer с внутренностями модуля vm1_qbus вообще нет никаких причин. Тем более, что эмулируется обычно математическая модель, а не электрическая.
always не может быть сложным и развеститым
Ну как не может?
if (cond1)
a <= val1;
else if (cond2)
a <= val2;
else if (cond3)
a <= val3;
В одну строку не запишешь, а если условия большие и сложные - вообще будет винегрет. А если конструкция case? Или я чего-то недопонимаю?
Получится, абсолютно весь проект сводится к парам функция/триггер
Как-то это меня смущает, что-то мне подсказывает, что раз всё так просто, значит где-то обязательно подвох какой-нибудь будет. Но попробую на таймере поэкспериментировать.
можете шаблон создать?
Имеется в виду c++ конструкции template<...>...{} ? если да - то это для меня непонятная магия и я этим функционалом не пользуюсь. А что-то другое - сделаю, чуть позже.
А я тут заново попытался сконвертировать верилатором Async\vm1.v выделив каждый модуль в отдельный файл. В результате он почти смог, но стал ругаться так:
%Error-BLKANDNBLK: vm1_qbus.v:107: Unsupported: Blocked and non-blocking assignments to same variable: v.core.ir_seq
%Error-BLKANDNBLK: vm1_qbus.v:138: Unsupported: Blocked and non-blocking assignments to same variable: v.core.abtos
%Error-BLKANDNBLK: vm1_qbus.v:163: Unsupported: Blocked and non-blocking assignments to same variable: v.core.init_out
Путём сравнения пришёл к выводу, что ему не нравится, что указанные переменные используются одновременно в always @(*) и always @(posedge pin_clk) кажется.
Вот этот момент, хоть убей, не могу понять, почему нет?
Я неточно выразился - нельзя представить как комбинационную функцию. Можно представить как объект класса с внутренним состоянием. То есть assign можно выразить как просто функцию без состояния, а вот always требует памяти внутри.
нет необходимости. И как-то увязывать внутренности модуля vm1_timer с внутренностями модуля vm1_qbus вообще нет никаких причин. Тем более, что эмулируется обычно математическая модель, а не электрическая.
Увязывать надо в том смысле что должны правильно вычисляться комбинационные функции по всей системе в целом. А также должны щелкать все триггеры во всей системе одновременно. Например, выход таймера зависит от регистра счетчика, который по клоку готов инкрементироваться. И этот выход подключен к другому регистру внутри процессора, идет чтение регистра счетчика процессором, и регистр готов защелкнуть читаемое входное значение. Если сначала вызвать таймер - он инкрементируется, и при вызове обработки регистра-защетки чтения, будет защелкнуто неверное значение. Поэтому и предлагается решение - вычислить все функции подаваемые на триггеры, а по событию клока только перенести в триггеры.
Ну как не может?
if (cond1)
a <= val1;
else if (cond2)
a <= val2;
else if (cond3)
a <= val3;
Тут смотря какие cond, не указаны они ли в списке чувствительности always @() (как асинхронный сброс, например). Если нет, то можно переписать так:
a <= cond1 ? val1 : (cond2 ? val2 : (cond3 ? val3 : a));
Или так (для битовой переменной)
a <= (cond1 & val1)
| (~cond1 & cond2 & val2)
| (~cond1 & ~cond2 & cond3 & val3)
| (~cond1 & ~cond2 & ~cond3 & a);
Ну и можно просто if-ы перенести в С++ при вычислении assign, тоже будет работать.
В одну строку не запишешь, а если условия большие и сложные - вообще будет винегрет. А если конструкция case? Или я чего-то недопонимаю?
Тем более, сложные условия надо выносить из always, так проще для понимания. case на С++ в большинстве случаев так и опишется "в лоб". И case там у меня в двух местах всего, емнип.
Имеется в виду c++ конструкции template<...>...{} ?
Я имел ввиду просто класс - внутренние переменные и методы. И мы их обсудили бы, для начала на очень простом 155ИР13 - там есть что обсудить для начала на самом простом примере, потом мы эти 155ИР13 скаскадируем по-разному, снова обсудим, а там уже до таймера доберемся.
А я тут заново попытался сконвертировать верилатором Async\vm1.v
Следует взять ветку Qsync, она более предсказуема для моделирования, фазы сигналов в ней такие же, поэтому разницы нет, а моделятор написать будет проще. И в ветке Async есть always-ы с @(*) - на самом деле это assign :), и петли обратной связи.
Путём сравнения пришёл к выводу, что ему не нравится, что указанные переменные используются одновременно в always @(*) и always @(posedge pin_clk) кажется.
Да, но там назначаются разные биты массива, они не перекрываются, фактически это разные отдельные переменные, ничего не нарушено и ничего подозрительного. Но, видимо, появились какие-то дополнительные ограничения для успешной автогенерации С++ кода.
Можно представить как объект класса с внутренним состоянием.
Но так и задумывалось изначально. Каждый модуль - самостоятельный объект класса, с переменными, в которых хранится его внутреннее состояние.
А также должны щелкать все триггеры во всей системе одновременно.
Это условие автоматически соблюдается при выполнении функции eval, внутри которой вызываются другие функции eval объектов класса (модулей, которые вызываются внутри другого модуля).
Если сначала вызвать таймер - он инкрементируется, и при вызове обработки регистра-защетки чтения, будет защелкнуто неверное значение.
А вот как раз это я и пытаюсь уже 3 поста уяснить. Когда именно нужно делать вызов, и что должно быть входом. Поскольку порядок исполнения assignов и alwaysов не важен, то исполняться они будут в том порядке, в каком встречаются по тексту, и тут-то и возник вопрос, куда втыкать вызов eval подчинённых классов. Но это я кажется начинаю уже понимать.
Следует взять ветку Qsync
Эта ветка принципиально не конвертируется, т.к. не хватает модулей. В файле vm1_alib.v есть вызов модулей altsyncram и altpll, которых нету, если нету квартуса или модельсима.
Вот что получилось для ir13. Файл ir13.h
class ir13
{
//input-output
BOOL rst_n, // input rst_n, // async reset, active low
BOOL clk, // input clk, // sync clock, raising edge
BYTE s, // input [2:1] s, // shift mode [1:0]
BYTE d, // input [7:0] d, // parallel load data
BOOL dsr, dsl; // input dsr, dsl; // new shifted in data bit
BYTE q // output reg [7:0] q // output data
//внутренние переменные
BYTE q_rc; //wire [7:0] q_rc;
BOOL clk_prev; //предыдущее значение
//отлов фронта
//вход: sc - текущее состояние сигнала,
// sp - предыдущее состояние сигнала
inline BOOL posedge(BOOL sc, BOOL sp)
{
//выход - когда предыдущее состояние FALSE, а текущее - TRUE
return !sp && sc;
}
//отлов спада
//вход: sc - текущее состояние сигнала,
// sp - предыдущее состояние сигнала
inline BOOL negedge(BOOL sc, BOOL sp)
{
//выход - когда предыдущее состояние TRUE, а текущее - FALSE
return sp && !sc;
}
void assign1();
public:
ir13(void);
~ir13(void){};
void eval(BOOL in_rst_n, BOOL in_clk, BYTE in_s, BYTE in_d, BOOL in_dsr, BOOL in_dsl, BYTE &out_q);
};
файл ir13.cpp
#include "ir13.h"
ir13::ir13(void)
: rst_n(FALSE), clk(FALSE), dsr(FALSE), dsl(FALSE), s(0), d(0), q(0)
{
clk_prev = clk;
}
void ir13::assign1()
{
switch (s & 3)
{
case 0: // storage
q_rc = q;
break;
case 1: // shift left
q_rc = (q >> 1) | (dsl ? 0x80 : 0);
break;
case 2: // shift right
q_rc = (q << 1) | (dsr ? 1 : 0);
break;
case 3: // parallel load
q_rc = d;
break;
}
}
void ir13::eval(BOOL in_rst_n, BOOL in_clk, BYTE in_s, BYTE in_d, BOOL in_dsr, BOOL in_dsl, BYTE &out_q)
{
//назначаем переданные входные переменные, чтобы если много assignов,
//то все они прозрачно использовали внутренние переменные.
rst_n = in_rst_n;
clk = in_clk;
s = in_s;
d = in_d;
dsr = in_dsr;
dsl = in_dsl;
//отрабатываем все операторы assign
//в данном конкретном случае, тело функции assign1 можно перенести сюда
//и все эти переменные, которые выше - будут не нужны.
assign1();
//делаем снапшот текущего состояния внутренних переменных для блоков always - нет необходимости
//выполняем все always
if (!rst_n)
{
q = 0;
}
else if (posedge(clk, clk_prev))
{
q = q_rc;
}
//снова обрабатываем assing (переменные же сменились)
assign1();
//возвращаем выходные переменные
out_q = q;
clk_prev = clk;
};
и примерно как это предполагается использовать:
#include "ir13.h"
....
ir13 m_regIR13_1; //объект регистра ИР13
//переменные для взаимодействия с объектом m_regIR13_1
//они могут принимать значения при взаимодействии с пользовательским UI например.
BOOL m_bSig_Reset; // async reset, active low
BOOL m_bSigClock; // sync clock, raising edge
int m_nFreq; //частота. меняется в диапазоне 1..1000
int m_nShiftMode; //shift mode [1:0]
BYTE m_nData; // parallel load data
BOOL m_nDSR, m_nDSL; // new shifted in data bit
BYTE m_nQ; // output data
....
//рабочий цикл
while (1)
{
//отрабатываем передний фронт и первый полупериод интервала ТЧ
m_bSigClock = TRUE;
//вызываем функцию объекта для переднего фронта ТЧ
m_regIR13_1.eval(m_bSig_Reset, m_bSigClock, LOBYTE(m_nShiftMode), m_nData, m_nDSR, m_nDSL, m_nQ);
//отрабатываем задний фронт и второй полупериод интервала ТЧ
m_bSigClock = FALSE;
//вызываем функцию объекта для заднего фронта ТЧ
m_regIR13_1.eval(m_bSig_Reset, m_bSigClock, LOBYTE(m_nShiftMode), m_nData, m_nDSR, m_nDSL, m_nQ);
sleep(1000/m_nFreq); //вот поэтому и в диапазоне 1..1000
}
....
Я не стал расписывать назначения побитно, т.к. это было бы удобно если бы d, q и q_rc представлялись бы в виде массивов BOOL q[8]. Но такое представление становится неудобным для логики редуцирования и интерпретации значения как числа.
внутри которой вызываются другие функции eval объектов класса (модулей, которые вызываются внутри другого модуля).
Если вызываются внутри, то ОК. Но откуда они возьмут недостающие параметры? Кое-что может быть снаружи текущего модуля? Значит надо делать выходные значения модулей или доступными глобально или писать методы извлечения, или подсовывать как параметры. Или С++ это позволит как-то красиво автоматизировать?
Эта ветка принципиально не конвертируется, т.к. не хватает модулей. В файле vm1_alib.v есть вызов модулей altsyncram и altpll, которых нету, если нету квартуса или модельсима.
Все модули в каталоге нужны только для работы в реальной FPGA, проект готовился для компиляции в железо. Для моделирования самого процессора достаточно файлов: vm1_qbus.v, vm1_timer.v, vm1_plm.v и пара параметров из config.v. Для моделирования верхний модуль - vm1_qbus().
Вот что получилось для ir13. Файл ir13.h
Входные переменные запоминаются внутри объекта? А нужно ли? лишняя память и манипуляции. Если сделать метод assign (у вас он называется eval) отдельным, без параметра clk, и сделать отдельные методы always_p и always_n, то clk вообще нигде не нужен. Я бы хранил только q_rc и все. Примерно так:
BOOL global_change;
class ir13
{
//
// внутренние переменные
//
BYTE q_rc;
public:
ir13 (void){};
~ir13(void){};
void assign(
BOOL rst_n,
BYTE s,
BYTE d,
BOOL dsr,
BOOL dsl);
void always_p(void);
void always_n(void){};
//
// Выходные переменные - доступны всем,
// или написать метод извлечения
//
BYTE q;
};
void ir13::assign(
BOOL rst_n,
BYTE s,
BYTE d,
BOOL dsr,
BOOL dsl)
{
if (!rst_n)
{
q_rc = 0;
if (q != 0)
{
q = 0;
//
// Здесь надо как-то сигнализировать всей системе что изменилось значение
// триггеров и нужен перерасчет остальных функций в системе
// Как вариант - глобальный флаг, или возвращаемое значение
//
global_change = TRUE;
}
return;
}
switch (s & 3)
{
case 0:
//
// storage
//
q_rc = q;
break;
case 1:
//
// shift left
//
q_rc = (q >> 1) | (dsl ? 0x80 : 0);
break;
case 2:
//
// shift right
//
q_rc = (q << 1) | (dsr ? 1 : 0);
break;
case 3:
//
// parallel load
//
q_rc = d;
break;
default:
//
// some debug assert
//
}
void ir13::always_p(void)
{
q = q_rc;
//
// Здесь надо как-то сигнализировать всей системе что изменилось значение
// триггеров и нужен перерасчет остальных функций в системе
// Как вариант - глобальный флаг, или возвращаемое значение
//
// Можно и не сигнализировать, если вызывающая сторона и так знает
// что будут обязательные изменения
//
global_change = TRUE;
}
Я не стал расписывать назначения побитно
Угу, сейчас нам это совсем неважно.
Update: общий расклад по эмулятору:
void assign_process(void)
{
for(;;)
{
global_change = FALSE;
for each module in system
{
module1.assign(parameters);
...
moduleN.assign(parameters);
if (!global_change)
{
break;
}
}
}
}
void always_process_p(void)
{
for each module in system
{
module1.always_p();
...
moduleN.always_p();
}
}
void always_process_n(void)
{
for each module in system
{
module1.always_n();
...
moduleN.always_n();
}
}
//
// Общий цикл эмулятора
//
for(;;)
{
//
// Назначаем нужные входные сигналы процессора перед фронтом такта
// Это сигналы топового модуля проекта - внешняя шина, перрывания и тд
//
top_input_process();
//
// Обрабатываем assign всех модулей
//
assign_process();
//
// Обрабатываем положительный фронт тактовой
//
always_process_p();
//
// Обрабатываем assign всех модулей
//
assign_process();
//
// Забираем результаты с выходов топового модуля
//
top_output_process();
//
// Назначаем нужные входные сигналы процессора перед срезом такта
//
top_input_process();
//
// Обрабатываем assign всех модулей
//
assign_process();
//
// Обрабатываем отрицательный фронт тактовой
//
always_process_n();
//
// Обрабатываем assign всех модулей
//
assign_process();
//
// Забираем результаты с выходов
//
top_output_process();
}
Но откуда они возьмут недостающие параметры?
Какие? Разве модуль может использовать что-то, что не описано как входной параметр или как внутренняя переменная? Если может, то модуль надо переписать так чтобы он использовал только то, что прямо задано во входных параметрах.
А нужно ли? лишняя память и манипуляции.
Это потому что у нас модуль маленький и простой.
Тут надо бы ещё определиться с такой вещью. Какая модель первична. Описанная на верилоге или на си? Т.е. Нужно ли и на си сохранять более менее понятный код, по которому можно разобраться в работе?
Если взять что посложнее, например vm1_qbus.v, то там функция assign неприлично разрастётся и возникнет желание разбить её на несколько частей, хотя бы чтобы самому не запутаться. Но если один раз написать и забыть, и больше туда не заглядывать - то сойдёт.
Имея одну функцию assign и целую кучу awaysов на все случаи жизни, придётся извне вызывать сперва assign, потом разные awaysы, которые необходимо, потом ещё раз assign, т.е. самим выполнить необходимую работу.
Мой же метод предполагает, один раз вызвать одну функцию, там внутри всё само прокрутится по заданным алгоритмам, и на выходе будет конечный результат, который можно использовать снаружи.
Теперь нужно на верилоге написать модуль, который использует модуль ir13 внутри себя, например 16 битный сдвиговый регистр или ещё чего-нибудь. И попробовать переписать всё это на си. Вот тут и станет ясно, чей метод нужно будет использовать в дальнейшем.
Какие? Разве модуль может использовать что-то, что не описано как входной параметр или как внутренняя переменная? Если может, то модуль надо переписать так чтобы он использовал только то, что прямо задано во входных параметрах.
В-общем случае можно "достать" переменные из других модулей, но я таким не злоупотреблял. То есть вопрос закрыт.
Это потому что у нас модуль маленький и простой.
Я все равно не вижу причин чтобы хранить входные переменные внутри модуля - заходят они все равно все сразу кучкой, а детектор фронтов нужен только на clk, проще выделить события clk в отдельные методы.
Тут надо бы ещё определиться с такой вещью. Какая модель первична. Описанная на верилоге или на си? Т.е. Нужно ли и на си сохранять более менее понятный код, по которому можно разобраться в работе?
Это смотря какие планы по дальнейшему использованию эмулятора. Лично я, возможно, буду использовать чтобы разобраться с микрокодом, на подходе еще микрокод от ВМ1Г. Мне некритично, я могу и с модельсимом поработать. Но, весьма вероятно, что найдутся еще люди которым будет интересно тоже на работу микрокода посмотреть и поизучать как работают отдельные инструкции. В любом случае, получится очень точный потактовый эмулятор ВМ1, который не надо будет отлаживать на предмет совпадения с оригиналом - это получается автоматически.
Если взять что посложнее, например vm1_qbus.v, то там функция assign неприлично разрастётся и возникнет желание разбить её на несколько частей, хотя бы чтобы самому не запутаться. Но если один раз написать и забыть, и больше туда не заглядывать - то сойдёт.
Да вроде там нет очень глубокого вложения функций, да и файл основной всего ~2K строк. Для начала можно написать как есть, там видно будет, интересно это кому то еще или нет.
Имея одну функцию assign и целую кучу awaysов на все случаи жизни,
Так always всего два - про положительному и отрицательному фронту такта, и в каждом из блоков один из них пустой. То есть - по факту всего два метода и никакого запоминания входных сигналов, никаких детекторов и прочего.
придётся извне вызывать сперва assign, потом разные awaysы, которые необходимо, потом ещё раз assign, т.е. самим выполнить необходимую работу.
Да, я там в апдейте предыдущего поста подробно основной цикл расписал.
Мой же метод предполагает, один раз вызвать одну функцию, там внутри
Да, функция одна, но тут показан далеко не весь код - нет видно кто и как вызывает ее, формирует входные параметры и где хранит результат. Это я к вопросу создания снашлота чтобы обеспечить "одновременность". В моем случае функции две, но параметры нужны только одной и их можно тупо брать сразу из выходных переменных модулей, не парясь о снапшоте. То есть - данные модуля хранятся в самом же модуле и нигде больше - самое соответствие парадигме ООП, имхо.
всё само прокрутится по заданным алгоритмам, и на выходе будет конечный результат, который можно использовать снаружи.
Насчет "само" я не понял, будет сгенерирован какой-то неявный С++ код чтобы "прокрутить по алгоритмам" ? Или эта реализация таки будет возложена на вызывающую сторону - обдумать где взять параметры (текущие/снапшот) и куда сложить результат?
Теперь нужно на верилоге написать модуль, который использует модуль ir13 внутри себя, например 16 битный сдвиговый регистр
Сделал 16-битный сдвиговый регистр, младший байт может загружаться параллельно и потом сдвигаться в старший байт, в зависимости от переменной mode_s. Последовательно выводимые данные из старшего разряда печатаются на консоль. Пример большой, но больше половины это создание внешних сигналов, сама эмуляция вполне простая.
ir13 low_byte;
ir13 high_byte;
BOOL global_change;
BOOL rst_n;
BYTE mode_s;
BYTE parallel_data_in;
BOOL serial_data_in;
void main(void)
{
//
// Initial reset, both registers
//
rst_n = 0;
mode_s = 0;
parallel_data_in = 0;
serial_data_in = 0;
generate_event(EVENT_INPUT_CHANGE);
//
// Parallel load in low-byte, high byte always shifts
//
rst_n = 1;
parallel_data_in = 0x55; // load 'U' character
mode_s = 3; //
generate_event(EVENT_INPUT_CHANGE);
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
//
// Not needed in this example
//
// generate_event(EVENT_CLOCK_FALL);
//
mode_s = 2;
generate_event(EVENT_INPUT_CHANGE);
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
mode_s = 3;
parallel_data_in = 0x53; // load 'S' character
generate_event(EVENT_INPUT_CHANGE);
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
mode_s = 2;
generate_event(EVENT_INPUT_CHANGE);
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
mode_s = 3;
parallel_data_in = 0x42; // load 'B' character
generate_event(EVENT_INPUT_CHANGE);
generate_event(EVENT_CLOCK_RISE);
mode_s = 2;
generate_event(EVENT_INPUT_CHANGE);
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
generate_event(EVENT_CLOCK_RISE); printf("%c", (high_byte.q & 0x80) ? '1' : '0');
}
void assign_process(void)
{
for(;;)
{
global_change = FALSE;
low_byte.assign(rst_n, mode_s, parallel_data_in, serial_data_in, 0);
high_byte.assign(rst_n, 2, 0, (low_byte.q & 0x80), 0);
if (!global_chabge)
{
break;
}
}
}
void always_process_p(void)
{
low_byte.always_p();
high_byte.always_p();
}
void always_process_n(void)
{
low_byte.always_n();
high_byte.always_n();
}
//
// Отдельный поток/функция, обрабатывающая события эмулятора:
// - измнение входных сигналов
// - положительный и отрицательный фронты тактовой
//
for(;;)
{
switch(wait_event())
{
case EVENT_INPUT_CHANGE:
{
//
// Назначаем нужные входные сигналы всей схемы
// Обрабатываем assign всех модулей
//
assign_process();
break;
}
case EVENT_CLOCK_RISE:
{
//
// Обрабатываем положительный фронт тактовой
//
always_process_p();
assign_process();
break;
}
case EVENT_CLOCK_FALL:
{
//
// Обрабатываем отрицательный фронт тактовой
//
always_process_n();
assign_process();
break;
}
}
}
далеко не весь код - нет видно кто и как вызывает ее, формирует входные параметры и где хранит результат
Это потому что я пока и сам до конца не представляю, как оно всё должно быть.
Насчет "само" я не понял
Имеется в виду, что не я буду заниматься вызовом assignов и alwaysов, а функция.
Я попробовал на таймере вынести из always выражения в assign, в этом случае, делать снапшот уже не нужно, но кол-во переменных увеличивается на кол-во переменных-регистров. И если не знать, что это за код, сходу уже не догадаешься, что это такое. При этом ваш метод передачи параметров выглядит более оправданным.
Сделал 16-битный сдвиговый регистр
А где верилог? Я ещё не до конца отказался от своего метода и хочу ещё убедиться в его оправданности. Или неоправданности. К тому же я всё равно не понимаю, как работает верилоговская модель, я просто механически перевожу код на си. И для меня правильность методики определит только практический эксперимент.
И мне важно провести сопоставление именно вызова одного верилоговского модуля из другого.
А где верилог?
module ir16
(
input rst_n,
input clk,
input [2:1] mode_s,
input [7:0] parallel_data_in,
input serial_data_in,
output serial_data_output
);
reg [7:0] l, h;
assign serial_data_output = h[7];
ir13 low_byte(
.rst_n(rst_n),
.clk(clk),
.s(mode_s),
.d(parallel_data_in),
.dsr(serial_data_in),
.dsl(1'b0)
.q(l2h));
ir13 high_byte(
.rst_n(rst_n),
.clk(clk),
.s(2'b10),
.d(8'b00000000),
.dsr(l[7]),
.dsl(1'b0)
.q(h));
endmodule
И мне важно провести сопоставление именно вызова одного верилоговского модуля из другого.
Да, это минимизирует количество ошибок переноса. Но, имхо, "в лоб" оно плохо переносится. Верилог - это попытка синтаксисом С как-то описать связи аппаратуры. В-общем-то получилось, но часто напоминает "тут - играть, тут не играть, а тут рыбу заворачивали" :)
Общая схема такая - есть набор переменных + тактовый сигнал. Часть из этого набора переменных представляют внешние входные сигналы проекта. Остальное - внутренние триггеры. Входы триггеров представляют собой комбинационые функции от набора переменных (внешних + триггеры). Если меняются переменные в наборе:
- изменение входных сигналов
- изменение состояния триггеров
то функции должны быть вычислены заново. Иногда вычисление функции приводит к немедленному изменению состояния триггера (асинхронный сброс), тогда все функции снова должны быть пересчитаны.
Отдельно в стороне стоит тактовый сигнал, триггеры разбиты на две группы - часть триггеров может изменять состояние по положительному фронту, часть - по отрицательному. Все триггеры в группе меняют состояние одновременно, поэтому просто переписываем вычисленные значения функций без пересчета.
Выходные сигналы проекта также представляют собой комбинационные функции от набора переменных (внешние входы + внутренние триггеры) и должны пересчитываться по тем же правилам как входные функции триггеров.
Каким методом такую систему реализовать - не суть важно.
А вот моё видение, как это примерно должно выглядеть. 51747
В архиве модели ir13, ir16 и примерное устройство, которое использует ir16. Избыточность и многие ненужные действия я убирать таки не стал. По мне, так более наглядно, в ущерб производительности.
Чтобы всё работало, надо где-то создать объект CSampleDevice m_Device;
и циклично вызывать его функцию m_Device.eval_p();
Один вызов этой функции - один такт работы всего устройства, включая всё, из чего оно состоит.
И тут в полный рост вылезла проблема, которую я не могу решить уже два года. Никак не получается организовать отдельный рабочий поток на основе класса, которым можно управлять с помощью сообщений и в котором где-то задать рабочий цикл, в котором бы вызывалось m_Device.eval_p(); с заданной частотой. Как это сделать я не знаю, где подсмотреть - тоже не знаю.
А вот ещё такой вопрос, даже два.
В Qsync\vm1_qbus.v полно таких alwaysов: always @(posedge pin_clk_p or posedge mjres) или always @(posedge pin_clk_n or posedge mjres)
Тут mjres - регистр, и его вроде бы уже нельзя рассматривать по аналогии как с tve_reset в always @(posedge tve_clk or posedge tve_reset) в module vm1_timer
или можно?
А ещё есть такая штука always @(posedge qbus_clk) для неё таки придётся отлавливать фронт при выполнении конкретно этого always среди списков и always_p и always_n ?
ps. А я думал этот пост сольётся с предыдущим. А оказывается нет.
В Qsync\vm1_qbus.v полно таких alwaysов: always @(posedge pin_clk_p or posedge mjres) или always @(posedge pin_clk_n or posedge mjres)
Тут mjres - регистр, и его вроде бы уже нельзя рассматривать по аналогии как с tve_reset
Почему нельзя? Запросто, обычный асинхронный сброс, ну является выходом триггера а не входной переменной, никаких особенностей на него это не накладывает. Если есть какие-то сложности восприятия - то заведите assign mj_res_func = mj_res, типа вычисляемая функция такая.
А ещё есть такая штука always @(posedge qbus_clk)
Это отладочный рудимент остался от переноса с асинхронной модели, если внимательно посмотреть то qbus_clk имеет источником pin_clk_p, то есть - следует qbus_clk там где он остался (я нашел одно место) заменить на pin_clk_p и забыть про него. Никаких других тактов кроме pin_clk_p/n в синхронном проекте нет, специально так разрабатывалось, поэтому насчет детекторов фронтов можно не париться.
ps. А я думал этот пост сольётся с предыдущим. А оказывается нет.
У форума какой-то таймаут на слияние есть, и хорошо что не объединилось, я увидел новое сообщение.
КР1801ВП1-095 полное разрешение (330МБ) (http://u.zeptobars.ru/yuot/1801/KR1801VP1-095-HD.jpg)
КР1801ВП1-095 половинное разрешение (110МБ) (http://u.zeptobars.ru/yuot/1801/KR1801VP1-095-FD.jpg)
Я тут заметил, что логика в vm1_qbus.v не оптимизирована, т.е. в некоторых местах можно вместо ~a | ~b записать ~(a & b) и т.п., а в некоторых выражениях общие члены можно вынести за скобки. Это потому что при моделировании в FPGA такие мелочи несущественны? Или оставлено как есть с целью облегчения поиска ошибок, а оптимизации будут потом?
А ещё вопрос про логику редуцирования.
выражение qbus_tovf <= &qbus_timer[5:2];
можно записать как qbus_tovf <= (qbus_timer[5:2] == 4'b1111);
или есть какая-то принципиальная разница, раз оставлено именно редуцирование?
Это потому что при моделировании в FPGA такие мелочи несущественны?
Совершенно несущественны, синтезатор все минимизирует применительно к архитектуре конкретного семейства FPGA, там не всегда "руками" можно найти оптимальное решение. БОльшую часть уравнений я переписал так чтобы, по моему мнению, было нагляднее, часть осталась просто переписанная со схемы "как есть".
Или оставлено как есть с целью облегчения поиска ошибок, а оптимизации будут потом?
Для FPGA основная оптимизация - это тактовая частота, ресурсов же ВМ1 и так немного берет чтобы за них особо бороться (~1400 ячеек). Под повышение тактовой (чтобы перешагнуть за 100МГц), возможно, перепишу АЛУ, и больше явно узких мест пока не видно.
есть какая-то принципиальная разница, раз оставлено именно редуцирование?
Никакой, просто так захотелось написать :)
Завершил механический перенос vm1_qbus.v -> vm1_qbus.cpp
Получилась какая-то страшная каша-малаша в которой совершенно невозможно разобраться, если не иметь перед глазами исходник на верилоге. Теперь надо заново всё сравнить и поискать насчёт опечаток.
Пока наблюдается две проблемы.
1) все assignы не удаётся расположить так, чтобы выполнить их за один проход, придётся крутить их в цикле пока предыдущее состояние wires не станет совпадать с нововычисленным.
2) выходные значения модулей vm1_reg_ff и vm1_timer - wires поэтому как мне кажется, их неправильно будет вызывать при исполнении always, а при исполнении assign их нельзя вызывать, т.к. внутри них есть alwaysы.
Если сделать их выходное значение регистром, а его значения назначать assignом первоначальному wire, логика не нарушится?
например для vm1_reg_ff сделать так:
assign xr = xr_rc; //тут результат работы vm1_reg_ff
assign yr = yr_rc; //назначается изначальным wire
always @(posedge pin_clk_n) //внутри vm1_reg_ff все always такие же
begin
vm1_reg_ff vreg(
.clk_p(pin_clk_p),
.clk_n(pin_clk_n),
.reset(reset),
.plr(plr),
.xbus_in(x),
.xbus_out(xr_rc),
.ybus_out(yr_rc),
.wstbl(au_alsl),
.wstbh(au_alsh),
.ireg(ir),
.vsel(vsel),
.pa(pin_pa),
.carry(psw[0]));
end
Иначе придётся прямо разворачивать все подмодули как вложения.
1) все assignы не удаётся расположить так, чтобы выполнить их за один проход, придётся крутить их в цикле пока предыдущее состояние wires не станет совпадать с нововычисленным.
Это странно, замкнутых петель в коде нет, то есть вариантов "А зависит от В" и одновременно "В зависит от А" в assign быть не должно. То есть порядок когда все вычисляется за один проход теоретически должен быть. Наглядность записи может при этом пострадать, но принципиальных препятствия для однопроходной реализации вроде нет. Некоторые трудности может создать асинхронный сброс, но мне кажется что они решаемые.
2) выходные значения модулей vm1_reg_ff и vm1_timer - wires поэтому как
Тут я не очень понял в чем проблема. То, что блоки alwyas и assign находятся внутри какого-то модуля, не значит что их можно вычислять отдельно от других блоков проекта. Неважно где описан блок, он все равно работает как и все остальные, на тех же условиях. На HDL модуль скорее соответствует макросу на C, а не функции/процедуре.
замкнутых петель в коде нет
Вот например, строки 1540-1541:
assign cpred = {c[14:0], cl};
assign c = (cpred & oxy) | (~cpred & axy & oxy);
Кстати, в alwaysе, начинающемся со строки 1014, с комментарием "ACLO edge detectors" нет ошибки? Там висящий блок begin end, который перебивает действие того, что было сделано ранее.
На HDL модуль скорее соответствует макросу на C
Это я понял, но я хочу сделать его именно функцией, это очень сильно упрощает программирование. Например модуль vm1_vgen у меня выродился в одну линейную функцию из простых присваиваний, alwaysы там оказались не нужны.
Предположим, что модуль - это такая микросхема с ножками, на вход подаётся ТЧ и входные сигналы, и в заданные моменты снимаются выходные сигналы. При этом, предполагаем, что наша модель идеальна (нет переходных состояний) и дискретна, меняет своё состояние только в моменты фронтов ТЧ. Субмоуль - точно также. При этом модулю, в котором вызывается субмодуль совершенно необязательно знать, что происходит внутри субмодуля, достаточно знать что на входе и что на выходе в заданные конкретные моменты времени, соответствующие его, модуля, дискретным состояниям. Поэтому наверняка субмодуль можно описать в виде триггера блоком(ами) always с условиями которые соответствуют условиям always внутри субмодуля.
Вот например, строки 1540-1541:
assign cpred = {c[14:0], cl};
assign c = (cpred & oxy) | (~cpred & axy & oxy);
Дык, это не замкнутая петля. Каждый бит результата сам от себя не зависит, а только от предыдущего разряда. Перепишем явно с указанием разрядов:
assign cpred[15:0] = {c[14:0], cl};
assign c[15:0] = (cpred[15:0] & oxy[15:0]) | (~cpred[15:0] & axy[15:0] & oxy[15:0]);
Затем рассмотрим, например, разряд c[1]:
assign cpred[1] = c[0];
assign c[1] = (cpred[1] & oxy[1]) | (~cpred[1] & axy[1] & oxy[1]);
Делаем подстановку:
assign c[1] = (c[0] & oxy[1]) | (~c[0] & axy[1] & oxy[1]);
Итого: c[i+1] зависит от c[i], но никак от самого себя. Вычисляем их в таком порядке: c[0], c[1] ... c[14], c[15] и удается все вычислить за один проход. Ну или через сдвиг сразу словом.
Update: тут я, кажется, погорячился - сразу за одну операцию не получится, надо вычислять именно последовательно - разряд за разрядом.
Кстати, в alwaysе, начинающемся со строки 1014, с комментарием "ACLO edge detectors" нет ошибки? Там висящий блок begin end, который перебивает действие того, что было сделано ранее.
Да, спасибо, это ошибка, согласно схемы должно быть так:
always @(posedge pin_clk_p)
begin
if (reset | aclo_ack)
aclo <= 1'b0;
else
if (~pin_aclo)
aclo <= 1'b1;
if (pin_dclo | aclo_ack)
acok <= 1'b0;
else
if (pin_aclo)
acok <= 1'b1;
end
Например модуль vm1_vgen у меня выродился в одну линейную функцию из простых присваиваний, alwaysы там оказались не нужны.
Да, это вот такой верилог, always @(*) вырождается/представляется в набор assign. В-общем-то, и наоборот все assign-ы можно переписать через always @(*). Но я сознательно использовал always для триггеров, а assign для комбинационной логики. Но пару мест все-таки попалось, где это введенное мной стилистическое правило не соблюлось.
модуля, дискретным состояниям. Поэтому наверняка субмодуль можно описать в виде триггера блоком(ами) always с условиями которые соответствуют условиям always внутри субмодуля.
Это да, а в чем вопрос? Представить модуль в виде объекта С++? Можно, но метод always объекта надо будет вызывать в методе always всей системы, и надо будет мониторить изменение выходов объекта. А если совсем не лезть внутрь, то не получится проанализировать и выстроить assign-ы так чтобы вычислить за один проход.
тут я, кажется, погорячился
Т.е. в самом исходнике на верилоге необходимо прямо расписать всё это побитно?
Ну или через сдвиг сразу словом.
Вот это как раз у меня и не получается. Нужно где-то хранить предыдущее состояние одной из переменных чтобы вычислить результат.
Например, если сделать так:
assign c = (cpred & oxy) | (~cpred & axy & oxy);
assign cpred = {c[14:0], cl};
то нужно знать начальное значение cpred и в момент времени 0, его инициализировать например нулём. Или это задом наперёд и неправильно по схеме?
Это да, а в чем вопрос?
Вопрос отпал, я просто развернул vm1_reg_ff как макрос, чтобы не ломать голову.
Т.е. в самом исходнике на верилоге необходимо прямо расписать всё это побитно?
Нет, на верилоге все правильно записано. И в схеме все правильно, тесты же процессор проходит. Необходимо расписать побитно на С, чтобы получить корректный результат. Такая параллельная запись, как на верилоге, прямо перенесенная на С в неизменном виде, не отображает функцию правильно.
То есть, на С вычислять надо развернутый вариант, тогда будет правильный результат за один проход и нет обратной зависимости:
c[0] = (cl & oxy[0]) | (~cl & axy[0] & oxy[0]);
c[1] = (c[0] & oxy[1]) | (~c[0] & axy[1] & oxy[1]);
c[2] = (c[1] & oxy[2]) | (~c[1] & axy[2] & oxy[2]);
...
c[14] = (c[13] & oxy[14]) | (~c[13] & axy[14] & oxy[14]);
c[15] = (c[14] & oxy[15]) | (~c[14] & axy[15] & oxy[15]);
то нужно знать начальное значение cpred
Не нужно, cpred лучше вообще сократить, и вместо него использовать вектор {c[14:0], cl}. То есть - в любой момент времени значение cpred[0] равно cl, cpred[1] равно c[0] и так далее. Вместо cpred можно использовать cl и c[14:0] непосредственно.
То есть, на С вычислять надо развернутый вариант, тогда будет правильный результат за один проход и нет обратной зависимости
А тут во второй строке вместо c[0] нужно подставлять значение c[0], вычисленное в первой строке, или предыдущее значение c[0], которое было до вычисления первой строки?
То есть - в любой момент времени значение cpred[0] равно cl, cpred[1] равно c[0] и так далее. Вместо cpred можно использовать cl и c[14:0] непосредственно.
т.е. если сперва вычислить с побитно, то потом cpred будет гарантированно таким {c[14:0], cl}?
Просто далее cpred используется для вычисления f, а вычислять по отдельному биту - занятие трудоёмкое, лучше использовать логические операции над словами сразу.
А тут во второй строке вместо c[0] нужно подставлять значение c[0], вычисленное в первой строке, или предыдущее значение c[0], которое было до вычисления первой строки?
Это я привел сразу запись нужного алгоритма на Си, вычисление переменной с за один проход, соответственно надо подставлять вычисленное в предыдущей строке значение. По существу - этот код отображает распространение переноса в многоразрядном полном сумматоре.
т.е. если сперва вычислить с побитно, то потом cpred будет гарантированно таким {c[14:0], cl}?
Он всегда будет таким, в любой момент. Ведь функция никакая не вычисляется, это просто алиас на ту же самую переменную. На Си можно рассматривать как (опуская синтаксический вопрос скобок):
#define cpred[0] cl
#define cpred[1] c[0]
...
#define cpred[15] c[14]
cpred - перенос с предыдущего разряда, используется в составе полного многоразрядного сумматора.
Просто далее cpred используется для вычисления f, а вычислять по отдельному биту - занятие трудоёмкое, лучше использовать логические операции над словами сразу.
Имхо, сначала надо корректно перенести реализацию на Си, в любом громоздком и некрасивом виде, заставить это работать, а потом уже можно заниматься оптимизациями. На этом этапе логические операции можно применять только если они эквивалентны операции над массивом бит и все изменения результирующих битов будут обработаны. То есть, как в рассматриваемом случае с c/cpred - изменение каждого бита требует перерасчета следующего, поэтому одной простой операцией над словом этот алгоритм не реализовать.
Осознал и понял как оно там всё. С этим ясно, получилось такое:
bool bAXY[16], bOXY[16], bC[16], bF[16], bH[16]; //битовые массивы
data2bita(oxy, bOXY, 16); //разворачиваем слово в битовый массив
data2bita(axy, bAXY, 16); //и это
data2bita(h, bH, 16); //и это тоже, их значения вычислены ранее
bool cn, cl = curr_reg.cl; //начальная инициализация C
for (int i = 0; i < 16; i++) //цикл по всем битам
{
bC[i] = cn = (cl || (!cl && bAXY[i])) && bOXY[i]; //вычисляем текущий бит C
bF[i] = curr_reg.alu_e ? bH[i] : !(cl ^ bH[i]); //вычисляем текущий бит F
cl = cn; //следующий бит будет такой
}
WORD f; //F нам понадобится потом как слово
bita2data(f, bF, 16); //поэтому сворачиваем битовый массив обратно в слово
//а С нам всё равно нужно только в виде отдельных битов потом
Тут начал прикидывать как получившуюся модель использовать, и получается что и материнскую плату надо в таком же виде моделировать, всё вплоть до отдельных сигналов и проводников. Как-то очень низкоуровнево и муторно получается. Пока что-то нет идей как проверить работоспособность того, что наваялось, кроме как примерно таким же образом сконвертировать нужные кусочки из tbench.v
получается что и материнскую плату надо в таком же виде моделировать, всё вплоть до отдельных сигналов и проводниковВ принципе - для встраивания V-модели процессора в любой существующий эмулятор достаточно написать "прокси-адаптер", который будет преобразовывать циклы шины ( и другие сигналы ) на выходах V-модели в запросы API эмулятора.
Мало того, можно использовать V-модель одновременно со старой абстрактной моделью того же процессора и на каждом шаге эмуляции автоматически сравнивать их поведение.
Фоточки:
КР1801ВП1-096, полное разрешение (365МБ) (http://u.zeptobars.ru/yuot/1801/KR1801VP1-096-HD.jpg)
КР1801ВП1-096, половинное разрешение (48МБ) (http://u.zeptobars.ru/yuot/1801/KR1801VP1-096-FD.jpg)
К581ИК1, полное разрешение (307МБ) (http://u.zeptobars.ru/yuot/581/KR581IK1-HD.jpg)
К581ИК1, половинное разрешение (30МБ) (http://u.zeptobars.ru/yuot/581/KR581IK1-FD.jpg)
К581ИК2, полное разрешение (307МБ) (http://u.zeptobars.ru/yuot/581/KR581IK2-HD.jpg)
К581ИК2, половинное разрешение (42МБ) (http://u.zeptobars.ru/yuot/581/KR581IK2-FD.jpg)
Также ссылки добавил в первый пост.
Съемки всех интересных 1801ВП1 закончены.
Съемки всех интересных 1801ВП1 закончены.
Все-все ВП1 от УКНЦ разве сфотканы?
Все-все ВП1 от УКНЦ разве сфотканы?
Угу, 055 полностью отреверсена, 120 векторизована и схема в пикаде проверяется и форматируется. 065 моделируется. Материалы на УКНЦ-шные ВП1 будут выложены по готовности.
1801ВМ1Г, половинное разрешение (45МБ) (http://u.zeptobars.ru/yuot/1801/K1801VM1G-MET-FD.jpg)
1801ВМ1Г, полное рарешение (525МБ) (http://u.zeptobars.ru/yuot/1801/K1801VM1G-MET-HD.jpg)
Подставил слой металла ВМ1Г в дралоскоп под проект ВМ1А, слои металла полностью совпали. То есть - слои металла ВМ1А и ВМ1Г одинаковы. Возможно, будут отличия в схеме на уровне поликремния или диффузии - это визуально сложно обнаружить, но уже есть хороший шанс что процессоры отличаются только микропрограммой.
есть хороший шанс что процессоры отличаются только микропрограммой.
Вот это было бы интересно.
Если умножение сделано микропрограммно, то оно, наверное, весьма медленное.
NovaStorm
24.04.2015, 21:38
Тут MOV может на сотню тактов растянуться, а он об умножении... =)
Тут MOV может на сотню тактов растянуться, а он об умножении... =)
Если поставить сверхтормозную память, то и на 1000. Да что там 1000... Миллион)
NovaStorm
24.04.2015, 22:28
Ну с нашими РУшками главный тормоз всё равно же именно проц? И это видимо не только из-за кривой архитектуры с диким обменом с памятью. А из-за реализации.
главный тормоз всё равно же именно проц? И это видимо не только из-за кривой архитектуры с диким обменом с памятью. А из-за реализации.
Да, процессор является главным тормозом, а реализация такая, какую транзисторный бюджет на тот момент позволял. Сейчас потихоньку смотрю LSI-11, есть у меня подозрение что оно еще медленее. И, много знакомого, люди делавшие ВМ1 явно LSI-11 и его устройство и микропрограмму изучали.
NovaStorm
26.04.2015, 10:55
а реализация такая, какую транзисторный бюджет на тот момент позволял
На тот момент уже были 8086 и 68k примерно на тех же технологиях. Да и 8МГц тогда было вполне на уровне. Так что, наверное, не в кремнии дело. Ведь IPC ВМ2 почему-то катастрофически маленький =(
В принципе - для встраивания V-модели процессора в любой существующий эмулятор достаточно написать "прокси-адаптер", который будет преобразовывать циклы шины ( и другие сигналы ) на выходах V-модели в запросы API эмулятора.
Ну, мне в этом плане проще гораздо, нет никакого апи - не нужно ничего
адаптировать, зато можно нужное апи создать.
Мало того, можно использовать V-модель одновременно со старой абстрактной моделью того же процессора и на каждом шаге эмуляции автоматически сравнивать их поведение.
Вам может и можно, а у меня всё гораздо проще, что вижу - о том и пою, имеющаяся у меня абстрактная модель абсолютно несовместима с V-моделью. Там даже сравнивать нечего, как мокрое с мягким.
Вот я пытаюсь собрать минимальный инструментарий для запуска сдаточных тестов ВМ1: модель процессора (правильность которой как раз и над проверить), простейшая модель памяти, простейшая модель терминального устройства, и всё это взаимодействует между собой посредством сигналов ОШ МПИ, по диаграммам из соответствующего ГОСТа (без учёта времянок естественно, лишь бы сигналы выставлялись в соответствующем порядке). Как-то тяжко идёт. У меня есть подозрение, что это дело можно сэмулировать как-то проще, но в голову ничего иного не приходит.
есть подозрение, что это дело можно сэмулировать как-то прощеЕсли согласовать интерфейс V-модели процессора 1801ВМ1 ( т.е. полный перечень публичных методов для чтения/записи сигналов на входах и выходах процессора ) - я могу сделать специальный вариант эмулятора ДВК-1 с исходниками отдельного модуля ( в виде проекта Visual C++ 2005 ), создающего объекты с таким интерфейсом. Тогда V-модель можно будет запускать в эмуляторе ДВК.
т.е. полный перечень публичных методов для чтения/записи сигналов на входах и выходах процессора
А их нету, так что подойдут любые.
Сейчас у меня сделано так: есть структура MPI в которой заданы все гостовские сигналы типа bool, а шина адрес-данные - слово unsigned short int, всё это в инверсном виде, для аутентичности, потому что у Vslav модель процессора тоже получает/выдаёт инверсные данные.
И есть единственная функция, отрабатывающая один такт устройства:
void tboard::maincycle()
{
m_CPU.eval_p(&m_MPI); //отработаем передний фронт ТЧ
m_Memory.eval(&m_MPI);
m_IRPS.eval(&m_MPI);
OutLog();
m_CPU.eval_n(&m_MPI); //отработаем задний фронт ТЧ
m_Memory.eval(&m_MPI);
m_IRPS.eval(&m_MPI);
OutLog();
m_nClk++;
}
CPU получает сигналы МПИ, отрабатывает полутакт ТЧ, если есть изменения - изменяет данные сигналов структуры МПИ, затем все остальные устройства, сидящие на шине, таким же образом. Всё это теоретически. Практически работоспособность не проверена, т.к. сейчас модель процессора не работает, ищу опечатки, так что даже публиковать исходники смысла нету. Конкретно сейчас - цикл dati не завершается, после съёма внешним устройством RPLY, процессор не хочет снимать SYNC, поэтому дело дальше не идёт.
Vslav, QSync\vm1_qbus.v, строка 683 - там длинное условие получается такое
if (pin_dmgi & ... & ~pin_dmgi), т.е. никогда не true, нету ли ошибки?
if (pin_dmgi & ... & ~pin_dmgi), т.е. никогда не true, нету ли ошибки?
Да, получается есть ошибка, внесена при переносе на синхронную модель, должно быть так:
if (pin_dmgi & (qbus_nosr_rc | (sync_out & ~qbus_win)) & qbus_req)
Выходит что генерация SACК привязана к срезу DMGI_IN, а не к уровню, потому что работает дополнительный триггер qbus_req.
CPU получает сигналы МПИПравильно ли я понимаю, что следующий интерфейс является исчерпывающим для V-модели 1801ВМ1:
typedef unsigned short word;
struct MPI_signals_type
{
bool SACK;
bool DMGO;
bool DMR;
bool SEL1;
bool SEL2;
word AD;
bool BSY;
bool DCLO;
bool ACLO;
bool IRQ1;
bool IRQ2;
bool IRQ3;
bool INIT;
bool VIRQ;
bool IAKO;
bool DOUT;
bool DIN;
bool RPLY;
bool WTBT;
bool SYNC;
};
class VM1_MPI_interface {
public:
virtual void Clock()=0;
virtual MPI_signals_type & Link_MPI()
{
return MPI_signals;
}
protected:
MPI_signals_type MPI_signals;
};
А есть желание именно МПИ эмулировать? С Wishbone оно немножко попроще будет. И синхронно с тактовой. К тому же у меня сил и времени хватит полноценно поддерживать только одну модель - Wsync, с Wishbone. В Async и Qsync только грубые ошибки будут исправляться.
А есть желание именно МПИ эмулировать? С Wishbone оно немножко попроще будет.Большой разницы нет - всё равно дальше "прокси-адаптера" сигналы шины ( на данном этапе ) не пойдут.
Можно сделать два адаптера - сначала для МПИ, потом для Wishbone.
Про роль синхронности модели я немного не понимаю. Адаптер должен непрерывно вызывать Clock() и после каждого вызова проверять SYNC - вряд ли для синхронной и асинхронной моделей здесь может быть какая-то разница.
Про роль синхронности модели я немного не понимаю. Адаптер должен непрерывно вызывать Clock() и после каждого вызова проверять SYNC - вряд ли для синхронной и асинхронной моделей здесь может быть какая-то разница.
Для модели без времянок внутренней логики, да, большой разницы нет. Только для данной модели надо будет два метода - нарастающий и ниспадающий фронты тактовой.
Только для данной модели надо будет два метода - нарастающий и ниспадающий фронты тактовой.Это не проблема адаптера. Адаптер вызывает Clock(), а асинхронная модель уже реализует его ( например ) так:
void VM1::Clock()
{
m_CPU.eval_p(); // отработаем передний фронт ТЧ
m_CPU.eval_n(); // отработаем задний фронт ТЧ
m_nClk++;
}
---------- Post added at 21:34 ---------- Previous post was at 21:09 ----------
Адаптеру можно без проблем добавить интерфейс "виртуального логического анализатора", поэтому для полноты кайфа можно добавить к списку сигналов МПИ текущее время шины в наносекундах, а при подключении интерфейса к адаптеру передавать модели процессора callback SignalsChanged(), который можно вызывать внутри V-модели в любой момент для отображения изменений сигналов МПИ. Тогда на экране виртуального осциллографа сигналы будут выглядеть более натурально:
void VM1::Clock()
{
m_CPU.eval_p(); // отработаем передний фронт ТЧ
m_nTimeNS += 100; // первая половина такта заняла 100 нс
SignalsChanged(); // сообщим осциллогафу об изменениях
m_CPU.eval_n(); // отработаем задний фронт ТЧ
m_nTimeNS += 100; // вторая половина такта заняла 100 нс
m_nClk++;
}
Правильно ли я понимаю, что следующий интерфейс является исчерпывающим для V-модели 1801ВМ1:
Не совсем исчерпывающим. У ВМ1 ещё есть ноги со своими сигналами, не относящимися к МПИ: SEL1, SEL2 и т.п. модель ВМ1 предполагает их использование, даже логика на них завязана, например используются ноги PA (processor number) при работе с захватом шины. Собственно МПИ нужен для связи между объектами.
Я использую не оптимальный способ. у класса VM1 есть ещё внутренняя структура, по типу описанной, но перечисляющая все ноги процессора, допустим VM1_PINS. В функции eval_p() первым делом все данные, являющиеся входными для ВМ1 копируются из структуры МПИ в структуру VM1_PINS, затем выполняется вся логика, и при выходе из eval_p() выполняется обратное копирование всех данных, являющихся выходными для ВМ1, в структуру МПИ. При условии, что копирование разрешено. А то там Z состояние может быть, тогда копирование таких сигналов игнорируется. Сам класс VM1 является обёрткой над vm1_qbus - рабочим телом, у которого входных/выходных сигналов почти в 2 раза больше, чем ног у ВМ1.
Я сейчас не об этом думаю, мне сейчас надо любым доступным способом (желательно с минимумом умственных затрат) добиться работоспособности модели ВМ1, и я делаю это в лоб - эмуляцией шевеления сигналов на ножках процессора (ну такая вот модель у Vslav, он ведь не под программное использование её затачивал, а под хардварное), чтобы ничего нового не придумывать, а использовать придуманное ранее. А когда она заработает - максимально визуализировать внутреннюю логику работы. Делать эмулятор ЭВМ пока даже задача не стоит.
Чтобы модель ВМ1 можно было как-то программно использовать, её сначала надо бы оптимизировать под программное использование.
Wishbone оно немножко попроще будет.
А оно сильно от Qsync отличается? Просто в архиве vm1_rev12j.rar папка Wsync без v файлов была, так что не с чем сравнивать, да и внутри БКшек тоже МПИ, привычно.
А оно сильно от Qsync отличается? Просто в архиве vm1_rev12j.rar папка Wsync без v файлов была, так что не с чем сравнивать, да и внутри БКшек тоже МПИ, привычно.
Пока несильно, только внешним интерфейсом, в итоге будет не один МПИ, а три отдельных Wishbone - процессорный мастер, периферийный ведомый, и отдельный для приема вектора прерывания. Еще, очень вероятно, будет переделываться АЛУ (функционал тот же, но запись будет компактной и позволит использовать арифметический режим ячеек в FPGA), добавится микрокод ВМ1Г (это вероятно получится внести и в Qsync), блок таймера и периферийных регистров выделится в отдельный модуль. Но Qsync сейчас вполне рабочая и законченная модель, можно на ней остановится для эмуляции.
Похоже, задача оказалась мне не по силам.
Никак не работает то, что получилось.
После долгой и кропотливой трассировки, я пришёл к выводу, что сигнал SYNC никогда не снимется, потому что wire oe_clr_fc устанавливается в 1, что привело бы к снятию sync, на очень короткое время, wire oe_clr_fc используется в always @(posedge pin_clk_n), а перед этим, на полтакта ранее в always @(posedge pin_clk_p) складываются условия, что после oe_clr_fc == 1, оно уже никогда не будет 1. Т.е. простыми словами, модель прощёлкивает момент, когда SYNC можно будет сбросить, а потом уже поздно.
Опечаток как ни искал, не нашёл, то ли где-то логический изъян, то ли я что-то так и не понял как надо правильно преобразовать модель. Вот архив с исходниками 52018
Может кто найдёт, где косяк.
SYNC никогда не снимется, потому что wire oe_clr_fc устанавливается в 1, что привело бы к снятию sync, на очень короткое
На SYNC и прочих сигналах МПИ в тестбенче tbench.v висят резисторы подтяжки на +5V:
tri1 din; // data input strobe
tri1 dout; // data output strobe
tri1 wtbt; // write/byte status
tri1 sync; // address strobe
tri1 rply; // transaction reply
tri1 dmr; // bus request shared line
tri1 sack; // bus acknowlegement
tri1 iako; // interrupt vector input
tri1 bsy; //
tri1 значит что когда pin_sync_ena равен нулю, то на "физической" ножке sync будет высокий уровень (за счет резистора), соответственно на pin_sync_in в модели ядра будет неактивный низкий.
В реальном процессоре nSYNC так и работает - активизируется низкий nSYNC и разрешается работа буфера - на ножке видим низкий активный уровень, потом nSYNC переводится в высокий при помощи разрешенного буфера - чтобы нормальный фронт сигнала получить, а потом, через такт, буфер запрещается, и уже сформированный буфером высокий уровень будет удерживаться резистором. Под буфером тут понимается "драйвер" - комплементарная пара мощных выходных транзисторов, которые могут (неодновременно, конечно) выдать низкий или высокий уровень на ножке, или быть оба одновременно запрещены.
время, wire oe_clr_fc используется в always @(posedge pin_clk_n), а перед этим, на полтакта ранее в always @(posedge pin_clk_p) складываются условия, что после oe_clr_fc == 1, оно уже никогда не будет 1.
Я не очень понял в чем проблема - oe_clr_fc используется только в clk_n, qbus_win нормально работает, ниже картинка сигналов в модели Qsync в ModelSim, надеюсь, поможет (кликабельно, формат png без потерь).
http://s011.radikal.ru/i318/1504/da/70e92e895941t.jpg (http://s011.radikal.ru/i318/1504/da/70e92e895941.png)
висят резисторы подтяжки на +5V
Я не использовал тестбенч. Неохота настолько подробно углубляться.
У меня sync не снимал сам процессор, после снятия rply устройством, pin_sync_ena всегда было 1, т.к. sync_ena и sync_out навсегда устанавливались в 1, пофиксилось тем, что у alwaysов в строках 625-643 инвертировал фронт ТЧ. После этого вроде как цикл dati стал выполняться как задумано, но выяснилось, что не работает больше половины остальных регистров, т.е. стартовый адрес из 177716 читать вроде пытается, но внутрь он не сохраняется, т.к. условия не создаются.
Я не очень понял в чем проблема
Проблема в том, что мне придётся таки осваивать ModelSim, чтобы потактово сравнивать работу моделей.
Я не использовал тестбенч. Неохота настолько подробно углубляться.
Тестбенч для эмулятора и не надо использовать, это просто тестовая платформа, но надо помнить что реальный процессор без внешних резисторов на МПИ не работает, в данной модели эти резисторы также внешние - описаны в тестбенче. Для модели с Wishbone таких проблем нет, поскольку там нет МПИ с третьим состоянием.
устанавливались в 1, пофиксилось тем, что у alwaysов в строках 625-643 инвертировал фронт ТЧ.
Имхо, ошибка где-то в другом месте, модель Qsync рабочая - это показывает modelsim и приведенные диаграммы совпадают со снятыми с реального процессора при помощи логического анализатора (третье состояние не видно, конечно, но фронты на своих местах).
Проблема в том, что мне придётся таки осваивать ModelSim, чтобы потактово сравнивать работу моделей.
А Вы рассматривайте это не как проблему, а как бонус :).
PS. Маленький бонус для всех:
1801ВМ1Г, диффузия, полное разрешение (472МБ) (http://u.zeptobars.ru/yuot/1801/K1801VM1G-DIF-HD.jpg)
1801ВМ1Г, диффузия, половинное разрешение (56МБ) (http://u.zeptobars.ru/yuot/1801/K1801VM1G-DIF-FD.jpg)
Вроде отличия только в микропрограмме, насколько видно при наложении векторов на новые картинки.
NovaStorm
30.04.2015, 09:13
Вроде отличия только в микропрограмме
Тогда получается младшие процы сознательно кастрировали в FP? Не в браке ПЗУ микрокода же дело?
Не в браке ПЗУ микрокода же дело?
Думаю что не в браке, а в объеме. Например, для LSI-11 объем микропрограммы без FP и с FP отличается в полтора раза (но там он исходно большой - ODT в микропрограмму встроен), для F-11 - вообще в три. И обычно микрокод составляет весьма существенную часть бюджета - для получения версии ВМ1Г надо будет более трети схемы перерисовать.
NovaStorm
30.04.2015, 15:54
не в браке, а в объеме
Но кристалл-то одинаковый? Откуда разный объём ПЗУ?
Но кристалл-то одинаковый? Откуда разный объём ПЗУ?
Это 1801ВМ1А и 1801ВМ1Г одинаковые, и отличие документировано только в одной команде, объем ПЗУ одинаковый. А в LSI-11 и F-11 ПЗУ с микрокодом внешние, там для поддержки FP в LSI-11 добавляется одна микросхема к имеющимся двум ПЗУ, а в F-11 - две микросхемы к имеющейся одной ПЗУ.
Это 1801ВМ1А и 1801ВМ1Г одинаковые, и отличие документировано только в одной команде, объем ПЗУ одинаковый.
Зачем тогда вообще ВМ1А, если можно штамповать по той же цене ВМ1Г?
Зачем тогда вообще ВМ1А, если можно штамповать по той же цене ВМ1Г?
Я думаю что сначала осилили (разработали в имеющемся объеме ПЗУ) ВМ1А, а потом военные решительно потребовали аппаратную команду умножения (нужна для цифровой обработки сигналов), для них сделали отдельную версию. Ходят же разговоры что ВМ1Г кому попало не поставлялись.
Я думаю что сначала осилили (разработали в имеющемся объеме ПЗУ) ВМ1А, а потом военные решительно потребовали аппаратную команду умножения (нужна для цифровой обработки сигналов), для них сделали отдельную версию. Ходят же разговоры что ВМ1Г кому попало не поставлялись.
Надо обязательно микропрограмму с ВМ1Г снять)
Надо обязательно микропрограмму с ВМ1Г снять)
Разве что для истории, так-то 1801ВМ2 - дальнейшее развитие архитектуры, а ВМ1Г - костыль, но дорог ценителям как коллекционная редкость.
В этом плане ВМ3 и ВМ4 гораздо интереснее - что там наши Кулибины наизобретали с конвейером и диспетчером памяти.
старая тема http://zx-pk.ru/showthread.php?t=17313
Сшил панораму ранее отснятой 581РУ2:
КР581РУ2, полное разрешение (220МБ) (http://u.zeptobars.ru/yuot/581/KR581RU2-HD.jpg)
КР581РУ2, половинное разрешение (30МБ) (http://u.zeptobars.ru/yuot/581/KR581RU2-FD.jpg)
Это специальное ПЗУ для микропроцессорного комплекта 581 серии, 512*22 бита, вскрыл его только чтобы разобраться в подробностях как оно взаимодействует с остальными микросхемами комплекта. Отрисовывать саму матрицу нет необходимости - ее можно из микросхемы просто прочитать.
Sergei Frolov
07.05.2015, 12:06
О, Микро-7
Матрица приоритетного шифратора прерываний в ВМ1Г тоже очень заметно переработана, работу этого блока придется изучать заново по полной. И как бы там не начало работать прерывание от таймера - линия запроса от него уже явно обрабатывается в матрице. Насколько я понимаю, таймер в ВМ1Г заявлен официально, так что вполне может быть.
Получена микропрограмма 1801ВМ1Г. Модель ВМ1Г прошла тесты 791401 и 791404 (без проверки исключений по недопустимым командам).
Команда MUL работает, микропрограммно и о-о-о-чень долго - примерно ~270 тактов. При частоте 5МГц - почти 60 мкс, печалька и привет прерываниям. Сам алгорим умножения точно пока не разобрал - он там классический на сдвигах, но какой-то инваринт. Счетчика циклов нет, поэтому есть вероятность что время исполнения умножения зависит от умножаемых данных (например, умножаем пока не досдвигаем аргумент до нуля). Эмуляторщикам ахтунг :)
Модель, версия 1.2k (http://u.zeptobars.ru/yuot/1801/VM1/vm1_rev12k.rar)
Схема 1801ВМ1А (http://u.zeptobars.ru/yuot/1801/VM1/CAD/vm1_ma.pdf)
Схема 1801ВМ1Г (http://u.zeptobars.ru/yuot/1801/VM1/CAD/vm1_mg.pdf)
Итого 1801ВМ1Г содержит 16646 транзисторов.
Прерывание от таймера точно поддерживается, пока не тестировал, приоритет - ниже чем у IRQ1/HALT, и выше чем IRQ2/IRQ3/VIRQ.
Получена микропрограмма 1801ВМ1Г....
Прерывание от таймера точно поддерживается, пока не тестировал, приоритет - ниже чем у IRQ1/HALT, и выше чем IRQ2/IRQ3/VIRQ.
Самое главное .
Особенно - вектор и обстоятельства прерывания.
* * *
Припоминаю, что ( предположительно ! ) кристаллы ВМ1Г могли поставлять вместо ВМ1А без уведомления потребителя - году так в 1991 выпустили извещение на Э.
И еще там нажимали на частотную градацию - т.к. ВМ1Г вовсе не 100% шли на 6.0-6.5 мгц при 25 градусах ( цеховые ворота, быт и военка ).
Vslav, я тут между попытками освоения multisima скармливал verilator'у модель, чтобы посмотреть, что он выдаст, и верилатор предлагал в vm1_plm.v в функции cmp вместо casex использовать casez, якобы это более эффективно. Как я понимаю, такая замена на уровне логического описания, а так же программирования абсолютно идентична? Ну т.е. если соответственно и в аргументах 'x' заменить на '?'.
Ещё в Qsync\vm1_qbus.v в строке 1567
assign flag[2] = (~plr[18] | (alu[15:0] == 8'o000)) & (alu[7:0] == 8'o000); // Z
16 разрядов сравнивается с 8ю разрядами. Верилатор говорит по этому поводу warning. В принципе такая конструкция полностью работоспособна и выдаёт ожидаемый результат, но наверное изначально планировалось сравнивать только старший байт? Вот так:
assign flag[2] = (~plr[18] | (alu[15:8] == 8'o000)) & (alu[7:0] == 8'o000); // Z
Или сперва слово сравнивать с 0 и для надёжности ещё и его мл.байт с нулём? Вот так:
assign flag[2] = (~plr[18] | (alu[15:0] == 16'o000)) & (alu[7:0] == 8'o000); // Z
И ещё, есть множество alwaysов с одинаковым условием, состоящих всего из одного назначения каждый. Если их объединить в один блок always, это будет просто эквивалентная запись или есть какие-то принципиальные предпосылки так не делать?
---------- Post added at 13:35 ---------- Previous post was at 13:12 ----------
Бегло сравнил модели Qsync и Wsync, на первый взгляд разница только в том, что в Wsync исчезли ноги, отвечающие за ПДП: DMR, SACK, DMGI, DMGO и соответствующая логика, всё остальное - не принципиально. Они действительно настолько не нужны?
casex использовать casez
В нашем случае абсолютно без разницы. casex более широкий случай, рассматривает 'x' и 'z' как не влияющие на результат сравнения, а casez - только 'z'. У меня там вообще были проблемы чтобы получить максимально совместимый синтезируемый (а не только для модели) код для сравнения переменных с 'x', поэтому лучше ничего не трогать.
В приложении - кусочек кода на Си, который я использую для анализа микрокода. Он совершенно "наколенный", но там есть функция вычисления матрицы plm (пока только для ВМ1А), надеюсь, поможет с реализацией.
Ещё в Qsync\vm1_qbus.v в строке 1567
assign flag[2] = (~plr[18] | (alu[15:0] == 8'o000)) & (alu[7:0] == 8'o000); // Z
16 разрядов сравнивается с 8ю разрядами.
Да, это опечатка. В данном случае к ошибке не приводит - 8 разрядов автоматически расширяются до 16 и получается эквивалент alu[15:0] == 16'o000000. Лучше исправить на alu[15:8] == 8'o000, хотя для получаемого после оптимизации результата разницы никакой не будет.
И ещё, есть множество alwaysов с одинаковым условием, состоящих всего из одного назначения каждый. Если их объединить в один блок
always, это будет просто эквивалентная запись или есть какие-то принципиальные предпосылки так не делать?
Если все назначения неблокирующие - со значком '<=', то разницы никакой не будет, можно объединить. В принципе, я старался чтобы все присваивания были или непрерывными (через assign) или неблокирующие (через <=). Блокирующие (через =) не планировались и могли проскочить только случайно, как опечатка. В блоке always они часто будут работать точно как неблокирующие (зависит от уравнений в блоке).
в Wsync исчезли ноги, отвечающие за ПДП: DMR, SACK, DMGI, DMGO и соответствующая логика, всё остальное - не принципиально. Они действительно настолько не нужны?
Угу, Wsync - это последняя актуальная модель, в итоге будет использовать шину Wishbone, там вопрос арбитража доступа к шине решается внешним компонентом, поэтому все эти подробности из этой модели потихоньку удаляются. Wsync - это потактовая копия, предназначенная для синтеза в реальные проекты на FPGA.
В Qsync будет вносится минимум - микропрограмма для ВМ1Г, переход на общую систему конфигурации проекта, и исправление опечаток. Хотя Qsync тоже синтезируемая, но ее реальное использование в FPGA не предполагается, основное ее назначение - моделирование на промежуточном этапе разработки. Скажем так - это "приличная" модель, очень близкая к оригиналу и позволяющая комфортно моделировать.
Async - это еще более точная модель, максимально близкая, но из-за асинхронных особенностей есть некоторые проблемы с моделированием, а надежный синтез практически невозможен. Модифицироваться никак не будет (только грубые опечатки), оставлена в пакете как демка для поборников "расовой чистоты" :)
PS. Впишу-ка я этот текст в readme, а то народ в почту возмущается что Async кучу варнингов при синтезе дает :)
PPS. А приложение-то я и позабыл, исправляюсь :)
Нашел в закромах БК11 с экситоновским КР1801ВМ1Г - но вот есть сомнения в его 3 точках. Не будет ли возможным как-нибуть разузнать вектор от встроенного в процессор таймера ?
*
Кстати, сам экз. процессора - Г. конкретное, с Большой буквы. Т.к. его проверяли на запуск БК0010 - но вот отработкой тестов из ТМОС явно не озадачивались. Не сменил его в машинке исключительно из интереса к таймеру и его вектору.
http://storage4.static.itmages.ru/i/15/0524/s_1432495040_4523231_bc3759fd9f.jpg (http://itmages.ru/image/view/2578802/bc3759fd)
Нашел в закромах БК11 с экситоновским КР1801ВМ1Г - но вот есть сомнения в его 3 точках.
А разве три точки это ВМ1Г? Вот подробное исследование (http://sovietcpu.com/articles/69-label-1801) со сканами ТУ и изменениями, получается что ВМ1Г это две точки или четыре, но никак не три. Тем не менее, дописал модель в части прерываний (модуль таймера не имел выходного прерывания и его сброса по подтверждению) и запустил небольшой тестик - прерывается по 270 вектору, сохраняет PC/PSW в текущем стеке.
Действительно, сейчас попробовал на машинке команду 070027 000001 - трапанула по 10 вектору.
MUL R0, #1
*
И такой тоже трапует по 10-му вектору :
http://storage1.static.itmages.ru/i/15/0526/s_1432656389_5983732_cf23405a35.jpg (http://itmages.ru/image/view/2584020/cf23405a)
прерывается по 270 вектору
А тогда у IRQ3 какой вектор? Вроде бы у него было как раз 270.
И если вектора одинаковые, как теперь узнать, откуда пришло прерывание? Может где-то флаги дополнительные введены?
А тогда у IRQ3 какой вектор? Вроде бы у него было как раз 270.
И если вектора одинаковые, как теперь узнать, откуда пришло прерывание? Может где-то флаги дополнительные введены?
Повторю свой немного отредактированный пост с ч/б форума для всех:
Да, пока 270 - это значение вытащенное по результатам реверса. Но тест проходит гладко - таймер генерирует прерывание, оно попадает на матрицу приоритетного шифратор, матрица вектор не вырабатывает, она вырабатывает 4-х битный индекс для таблички. Табличка (генератор векторов и адресов) содержит 13 констант, они все известны -
160002, 160006, 160012, 177716, 000004, 000010, 000014, 000020, 000024, 000030, 000034, 000100 и 000270. То есть никакого нового вектора в ВМ1Г не добавлено, я табличку векторов в ВМ1Г специально перепроверял, может что и просмотрел, но вероятность небольшая.
Также матрица вырабатывает 3-битный индекс для сброса запроса - и этот сброс точно попадает на запрос таймера, вероятность что неправильно разобрана матрица тоже небольшая.
Но - да, для того чтобы окончательно убедиться, надо перепроверить на реальном процессоре, мне пока негде - нету платы с ВМ1Г.
Мой тест, запустить и посмотреть куда улетит:
TLIM = 177706
TCNT = 177710
TCSR = 177712
.word 0
.word 0
.word 0
stack:
entry:
mov #stack, SP
mov #0, @#TCSR
mov #2, @#TLIM
mov #24, @#TCSR
mtps #0
wait
br entry
Флагов никаких дополнительных не обнаружено, увы. А IRQ3 где-то используется? В БК-0011М выходит на внешние разъемы, какие из внешних модулей с IRQ3 работают? В МС1201.01 также через перемычку выходит на шину и все.
И такой тоже трапует по 10-му вектору :
http://storage1.static.itmages.ru/i/15/0526/s_1432656389_5983732_cf23405a35.jpg (http://itmages.ru/image/view/2584020/cf23405a)
Оп-па, получается что пять точек - это непонятно что. Может быть какая специальная приемка, но не ВМ1 с микропрограммой умножения. И дата выпуска 9102, может быть еще какое изменение ТУ вышло.
Это не те 5 точек, которые нужно.
Это могут быть, предположительно, отметки о корпусе, точнее его материале - или серебро, или еще чего более нехорошее.
*
Сейчас порыл в закромах - нету процессоров с 2-мя точками производства Э.. Т.к. они отвратительно шли даже на 5 мгц, я их пристроил металлистам по 2 р. 50 к. за 1 грамм. Есть только отличные А. в натуральном японском компаунде, и отбраковка от милитаристов в керамике из Аниона.
А IRQ3 где-то используется?
В БК11М оно выведено как на разъём МПИ (конт. B15), так и на УП (конт. B2), и вроде бы потенциально в каком-то из подключаемых к УП блоков использовалось, то ли КМ, то ли КМК, то ли ещё чего-то. В какой-то документации к какому-то из блоков, которых у меня всё равно не было, упоминался вектор 270. Вроде бы это было как-то связано с печатью на принтер, но могу ошибаться.
Но поскольку у большинства людей ни блоков, ни софта соответствующего не имеется, то можно считать, что IRQ3 на БК11(М) не используется.
Vslav, Судя по ТУ новое обозначение начинается с 1990 года, но у меня на КМД процессор без точки от 89 10 изг. Ангстрем.
В БК11М оно выведено как на разъём МПИ (конт. B15), так и на УП (конт. B2)... Но поскольку у большинства людей ни блоков, ни софта соответствующего не имеется, то можно считать, что IRQ3 на БК11(М) не используется.
Оно использовалось под функциональную кнопку "Print Screen", немного софта под неё существует -- печать графической копии экрана на принтер, например, сохранение дампа памяти в файл. Кто-то на форумах писал, что часы туда навешивал.
Сейчас, благодаря эмуляторам БК для РС, эти функции мало востребованы, но лучше бы иметь "перемычку" для выбора источника прерывания по вектору 270.
По результатам "вскрытия" ВМ1А у меня отложилось, что там на недокументированную ногу выводится сигнал прерывания от внутреннего таймера?
В блоке КПУ от простой БК11 IRQ270 не выведено :
http://storage2.static.itmages.ru/i/15/0526/s_1432663160_8185947_b32bfa1a48.jpg (http://itmages.ru/image/view/2584448/b32bfa1a)http://storage1.static.itmages.ru/i/15/0526/s_1432663396_1626050_a4c7882ee1.jpg (http://itmages.ru/image/view/2584451/a4c7882e)http://storage1.static.itmages.ru/i/15/0526/s_1432663553_7775357_0d7c3ab787.jpg (http://itmages.ru/image/view/2584460/0d7c3ab7)
По результатам "вскрытия" ВМ1А у меня отложилось, что там на недокументированную ногу выводится сигнал прерывания от внутреннего таймера?
Не совсем так. Недокументированная нога (вывод 6) это подача внешней частоты на таймер или управление захватом значения счетчика, работает в ВМ1А и ВМ1Г. Прерывание (вектор 270) работает только в ВМ1Г, а у ВМ1А отсутствует нужная внутренняя цепь и матрица приоритетного шифратора не поддерживает это прерывание.
Кстати, матрица вырабатывает 4-х битный индекс вектора в таблице, 13 значений - это вектора-константы, а еще 3 - это динамический вектор принимаемый по шине (IAKO), еще один индекс отдается на таблицу констант (не векторов, если выбран этот индекс, то работает совсем другая таблица, она содержит логические константы для разных операций в АЛУ), и последний индекс это стартовый вектор из регистра 177702 (планировалось прерывание по записи в 177702, но не подключено внутри и не работает). Итого все 16 индексов расписаны и новому вектору просто неоткуда взяться - матрица шифратора не может адресовать 17-ое значение 4-х битным индексом.
Недокументированная нога (вывод 6) это подача внешней частоты на таймер или управление захватом значения счетчика, работает в ВМ1А и ВМ1Г.
Т.е. если на вывод 6 подать постоянную "1" (или "0"?), то таймер как бы отключится может совсем?
Т.е. если на вывод 6 подать постоянную "1" (или "0"?), то таймер как бы отключится может совсем?
Не отключится, просто не будут работать некоторые режимы - захват текущего значения счетчика, и счет от внешней частоты. Считать от тактовой частоты процессора (вход 1) таймер cможет.
Vslav, Судя по ТУ новое обозначение начинается с 1990 года, но у меня на КМД процессор без точки от 89 10 изг. Ангстрем.
Похоже что ТУ созданы чтобы их нарушать :). Может Ангстрем перешел на маркировку раньше, может просто микросхема неотмаркирована, мы все равно уже не узнаем почему так. По-крайней мере, без точки может быть только ВМ1А.
КМ1801ВМ2, полное разрешение (546МБ) (http://u.zeptobars.ru/yuot/1801/KM1801VM2-MET-HD.jpg)
КМ1801ВМ2, разрешение четверть (51МБ) (http://u.zeptobars.ru/yuot/1801/KM1801VM2-MET-FD.jpg)
Красивая картинка получилась, самому нравится :)
КМ1801ВМ2, полное разрешение (546МБ) (http://u.zeptobars.ru/yuot/1801/KM1801VM2-MET-HD.jpg)
КМ1801ВМ2, разрешение четверть (51МБ) (http://u.zeptobars.ru/yuot/1801/KM1801VM2-MET-FD.jpg)
Красивая картинка получилась, самому нравится :)
УК-НЦшкин !
А какой вскрывали - керамический или чёрный пластмассовый ВМ2 ?
И чей АНГСТРЕМовский или СЭМЗовский? )
Красивая картинка получилась, самому нравится :)
Уже и схема, поди, не за горами)
УК-НЦшкин !
А какой вскрывали - керамический или чёрный пластмассовый ВМ2 ?
И чей АНГСТРЕМовский или СЭМЗовский? )
Керамический, Ангстрем, дата 9105, одна точка.
Как я понял, было две версии ВМ2, имеются в сети две разных фотографии кристалла, я их называю "ранняя" и "поздняя". Страничка на 155la3 (http://www.155la3.ru/k1801.htm) утверждает что одна из них Ангстремовская, вторая СЭМЗ, но у меня в ангстремовском корпусе оказалась "поздняя" (типа СЭМЗ), из видимых отличий - в ней добавлен еще один генератор смещения подложки.
Как я понял, было две версии ВМ2, имеются в сети две разных фотографии кристалла, я их называю "ранняя" и "поздняя". Страничка на 155la3 (http://www.155la3.ru/k1801.htm) утверждает что одна из них Ангстремовская, вторая СЭМЗ, но у меня в ангстремовском корпусе оказалась "поздняя" (типа СЭМЗ)
Ну почти как УКНЦ.
По ходу разработки адаптера шины ВМ1 для эмулятора ДВК - возникли некоторые вопросы.
1. Когда блок питания снимает DCLO - ВМ1 снимает INIT. Через сколько тактов это происходит ?
2. Когда блок питания выставляет DCLO - ВМ1 выставляет INIT. Через сколько тактов это происходит ?
3. Нога 34 ( INIT ) у ВМ1 обозначена как вход-выход. Когда ВМ1 читает этот вход и что делает, если этот вход активен ?
1. Когда блок питания снимает DCLO - ВМ1 снимает INIT. Через сколько тактов это происходит ?
Есть два отдельных сброса - ядра (на схеме цепь RESET) и периферии (цепь INIT_IN)
Когда вход ~DCLO переходит в низкий (активный), то ~INIT переходит в низкий немедленно. Ядро процессора также сбрасывается (внутренний RESET высокий). При этом запускается внутренний счетчик на 2 такта, чтобы длительность внутреннего сброса была не менее 2 тактов. При этом если вход ~ACLO вдруг высокий (при низком ~DCLО это неверная комбинация), то этот счетчик в игноре.
Итого, при переходе ~DCLO в высокий уровень
а) если ~ACLO низкий, ~INIT перейдет в высокий сразу, если прошло 2 и более тактов CLC с момента ниспадающего фронта ~DCLO.
б) если ~ACLO высокий, ~INIT перейдет в высокий сразу
2. Когда блок питания выставляет DCLO - ВМ1 выставляет INIT. Через сколько тактов это происходит ?
Реальный процессор - немедленно. Синхронная модель - по первому нарастающему фронту CLС.
3. Нога 34 ( INIT ) у ВМ1 обозначена как вход-выход. Когда ВМ1 читает этот вход и что делает, если этот вход активен ?
Эта ножка с выходом типа открытый коллектор. Поэтому может формировать только низкий уровень, который будет восприниматься как этим же входом, так и другими устройствами. Цепь образует монтажное ИЛИ, позволяя формировать сигнал периферийного сброса другим устройствам.
Низкий уровень на выходе формируется при сбросе ядра процессора (DCLO/ACLO) и при выполнении команды RESET.
Низкий уровень на входе (подключен просто параллельно к выходу, к самой ножке), осуществляет периферийный сброс - сброс ВЕ-таймера, сброс запроса шины (временно маскирует DMR) и сброс детекторов ниспадающего фронта IRQ2 и IRQ3.
Низкий уровень на выходе INIT формируется при сбросе ядра процессора (DCLO/ACLO) и при выполнении команды RESET.Насколько я понимаю - ВМ1 работает следующим образом.
Сброс ядра процессора ( и появление низкого уровня на выходе INIT ) происходит только при установке DCLO = ON ( низкий уровень ).
Когда происходит событие DCLO & ACLO == 0FF ( оба высокий уровень ) - тогда через 10 тактов происходит чтение SEL1 и переход на адрес старта, поэтому и при включении питания, и при отпускании кнопки "Сброс", закорачивающей DCLO - процессор действует одинаково.
Если при DCLO == OFF возникает ACLO = ON - немедленно происходит TRAP24. Если после этого сохраняется DCLO == OFF, то после ACLO = OFF - происходит новый старт, но сброс процессора при этом не происходит и состояние INIT всё время неактивное ( высокий уровень ).
Стандарт Q-Bus такую ситуацию прямо запрещает, требуя от систем питания, чтобы любая активация ACLO всегда завершалась активацией DCLO, но если такого не будет - вряд ли ВМ1 сбросит ядро без активации DCLO.
Стандарт Q-Bus такую ситуацию прямо запрещает, требуя от систем питания, чтобы любая активация ACLO всегда завершалась активацией DCLO, но если такого не будет - вряд ли ВМ1 сбросит ядро без активации DCLO.
Отчасти (а то и вовсе) не в тему - сильно внимательно не вчитывался...
Строго говоря, на Q-Bus нет сигналов ACLO, DCLO - это UNIBUSные сигналы. Но в прицнипе с учетом обратной полярности по отношению к POK и DCOK которые стандартны для Q-Bus, можно считать, что это одно и то же... В описании KDJ11 помню также встречались какие-то отличия от чего-то там ранее привычного, сейчас лень искать, но кажется речь шла только о времени между сигналами которое должно быть не меньше чем что-то (а больше - хоть годы)... Также рассматривается случай (все еще про KDJ11) когда DCOK убирается и появляется вновь пока POK остается активным (с учетом... то же самое?) - используется для перезагрузки. А вообще поскольку разборки с POK/DCOK касаются исключительно процессора, то и ккие-либо стандарты Q-Bus здесь довольно условны (о чем и пишется скорее всего во всех доках по процессорам новее древних UNIBUSных) :)
Терминология:
*CLO = ON - низкий уровень на входе *CLO
*CLO = OFF - высокий уровень на входе *CLO
Активация *CLO - ниспадающий фронт на входе *CLO
Деактивация *CLO - нарастающий фронт на входе *CLO
Пока я считаю что работает так:
Вход DCLO = ON всегда безусловно переводит ядро в состояние сброса и оно там остается до деактивации DCLO. При этом сбрасывается матрица приоритетного шифратора прерываний, а также детекторы фронтов (ниспадающего и нарастающего) ACLO. После выхода из сброса (DCLO перешло в OFF) ядро безусловно переходит к ожиданию фронта деактивации ACLO = ON->OFF, независимо в каком состоянии находилось ACLO при деактивации DCLO. При обнаружении фронта деактивации ACLO ядро начинает выполнять микропрограмму начального пуска, детектор фронта деактивации ACLO отключается и больше не работает до следующего сброса ядра. Детектор фронта активации ACLO работает всегда и при обнаружении происходит прерывание 24. ACLO может активироваться и деактивироваться многократно, вызывая при активации прерывание 24 (этот факт многократности надо проверить моделированием). Также есть возможность при ACLO=ON выполнить сброс всего процессора командой RESET - при этом сбрасывается не только периферия но и ядро (этот вывод также надо проверить).
Сброс ядра процессора ( и появление низкого уровня на выходе INIT ) происходит только при установке DCLO = ON ( низкий уровень )
А также по команде RESET если ACLO = ON.
Когда происходит событие DCLO & ACLO == 0FF ( оба высокий
Тут важен именно переход ACLO -> 0FF при уже неактивном DCLO. Нельзя сделать ACLO=OFF, а потом DCLO=OFF, старта не будет.
Если при DCLO == OFF возникает ACLO = ON - немедленно происходит TRAP24. Если после этого сохраняется DCLO == OFF, то после ACLO = OFF - происходит новый старт
Нет, не происходит ничего.
Update: Кстати, я там выложил схемы в формате пикада, можно загружать и смотреть цепи уже в электронном виде. Навигация делается так - выделяем цепь, ПКМ->Edit Nets - там будет окошко с подключенными транзисторами, можно вбрать и нажать Jump to Node - перейдет к указанному транзистору.
Update2: полноценный пикад можно не разворачивать - достаточно просмотровщика (можно взять тут (http://p-cad-2006-viewer.software.informer.com/)), навигация в нем тоже работает.
Нельзя сделать ACLO=OFF, а потом DCLO=OFF, старта не будет.Т.е. процессор просто зависнет, никак не реагируя на CLC ?
Получается, что для перезапуска ВМ1 просто коротить DCLO недостаточно - нужно обязательно закорачивать и DCLO, и ACLO ( в любой последовательности, если не важен TRAP24 ), а потом воспроизводить деактивацию сигналов DCLO и ACLO при включении питания.
Для ВМ2 минимальная стартовая задержка между DCLO = OFF и ACLO = OFF составляет 10 тактов ( если верить ТО ), а для ВМ1 эта величина известна ?
Т.е. процессор просто зависнет, никак не реагируя на CLC ?
Да, он будет ждать фронта деактивации ACLO. По фронту деактивации ACLO - стартанет. При этом, в процессе ожидания детектор фронта активации ACLO также работает, если таковое событие зафиксируется, то после старта процессора будет выставлен запрос на TRAP24.
Структурно оно выглядит так - есть два независимых детектора фронтов активации и деактивации ACLO, их выходы поступают на матрицу шифратора контроллера прерываний. У детекторов есть внутри триггер, для детектора активации устанавливается по ACLO=OFF, для детектора деактивации - по ACLO=ON, оба триггера сбрасываются по общему сбросу ядра, а также при подтверждении обработки события матрицей прерываний. Выход детектора AND-ится с внутренним триггером. То есть - запрос по активации ACLO будет высоким если ACLO=ON И триггер был ранее установлен по ACLO=OFF. При активации ACLO будет генерироваться прерывание, при деактивации ACLO специальное событие СТАРТ. Прерывание подтверждается (триггер внутри детектора сбрасывается) переходом на обработку, СТАРТ подтверждается выходом из ОЖИДАНИЯ.
DCLO=ON сбрасывает все - детекторы, контроллер прерываний, ядро, пока DCLO=ON все блоки находятся перманентно в состоянии сброса. Когда происходит деактивация DCLO->OFF контроллер прерываний переходит в специальное состояние - ОЖИДАНИЕ старта, при этом основной микропрограммный автомат опрашивает контроллер прерываний с интервалом 3 CLC и также ожидает, пока контроллер прерываний не выйдет из состояния ОЖИДАНИЕ. Дальше тут два возможных варианта - назад в СБРОС по DCLO=ON, или переход в рабочее состояние по событию СТАРТ (детектор деактивации ACLO дает запрос).
Возможные состояния и табличка переходов (события условия указаны в скобках)
СБРОС -> СБРОС (DCLO=ON), ОЖИДАНИЕ (DCLO=OFF)
ОЖИДАНИЕ -> СБРОС (DCLO=ON), ОЖИДАНИЕ (нет событий), РАБОТА (деактивация ACLO)
РАБОТА -> СБРОС(DCLO=ON), РАБОТА (нет событий), TRAP24 (активация ACLO)
Для ВМ2 минимальная стартовая задержка между DCLO = OFF и ACLO = OFF составляет 10 тактов, а для ВМ1 эта величина известна ?
Для ВМ1 незначительная, порядка 1 CLC, тут важно чтобы детектор деактивации ACLO успел выйти из сброса, остальное менее важно. Я проверял реальный процессор на 4МГц с интервалом примерно 1мкс (4 такта) между снятием DCLO и ACLO - надежно стартовал. Может там еще какие физические ограничения есть, но логических не видно.
Да, и с учетом периода опроса в 3 CLC возможен разброс момента выхода в режим работы относительно деактивации ACLO.
Update: по памяти немного неверно описал работу детекторов, посмотрел схему и исправил пост. Запоминания и отложенной обработки TRAP24 не будет - оба детектора сбрасываются одинм сигналом, возникающим по TRAP24 и по СТАРТ.
А возможен ли выход из ожидания старта при поступлении сигналов IRQ1, IRQ2, IRQ3, VIRQ ?
А возможен ли выход из ожидания старта при поступлении сигналов IRQ1, IRQ2, IRQ3, VIRQ ?
Нет, все состояния, касающиеся сброса, и события переходов между ними - в табличке. В состоянии ОЖИДАНИЕ контроллер прерываний (и весь процессор) реагирует только на СТАРТ или СБРОС.
CodeMaster
02.06.2015, 15:30
Пока с переводом Верилога в Си вроде затихло, я стал понимать больше слов в сообщениях ;-) и возникло несколько исторических вопросов, а вот сколько не читаешь всё-равно пазл полностью не складывается.
1. Система команд у 1801ВЕ1 изначально была близка к DEC или почему ВМ1 решили делать на базе ВЕ1? Просто адаптировать, даже через микрокод ИМХО всё-равно трудоёмко, ведь проектирование было ручным, а адаптировалась ведь не только система команд. Доработки, верификация, не проще ли было создать совсем с нуля?
2. Правильно ли я понимаю, что ВМ1 делали когда не было реверса кристаллов T-11 и F-11 и не было 1807ВМ1 и 1811ВМ/ВУ/ВТ и их делали по описаниям на систему команд и шины Q-Bus? Или реверс кристаллов был, но в другом министерстве, а PDP-11 совместимый процессор делать было надо?
3. Развитие не полностью совместимой линейки ВМ1/2/3, при наличии полных аналогов это тоже борьба министерств или они были чем-то лучше 1807/1811?
И немножко ближе к теме :-)
Получается, что для перезапуска ВМ1 просто коротить DCLO недостаточно
Все вот эти заморочи со стартом проца это унаследованное для работы с UNIBUS/Q-Bus? В смысле можно бы было обойтись и отпусканием RESET, но для соблюдения стандарта надо использовать ACLO/DCLO. Как и где использовался TRAP24 при пропадании внешнего питания, что делали системы при обработке этого прерывания? И какой смысл останавливать проц по DCLO перед его обесточиванием ведь в итоге вся система и так остановится?
Вроде Уважаемый Юрий Отрохов на ixbt писал что это чуть ли не личная его измена ;-)
Его начальство жаждало "двигать" свою систему команд НЦ, а высокое начальство говорило что-то "а зачем нам НЦ и Э60, может деньги только на что-нибудь одно тратить?" После этого разработчики (к большому неудовольствию своего начальства) предложили перевести ВЕ1 на систему команд Э60.
Так что дублирование - это был шаг к унификации. Хотя некоторые держат на предложившего эту идею большой зуб и считают что именно это "отодвинуло" Зеленоград на второй план.
Но это всё только моё понимание прочитанного, лучше на ixbt первоисточник перечитать.
CodeMaster
02.06.2015, 16:09
лучше на ixbt первоисточник перечитать.
Я читал эти 100 страниц обсуждения (или больше в 2 темах?), но чёткой картины у меня не сложилось.
Так что дублирование - это был шаг к унификации.
Допустим, но зачем брать вертолёт и допиливать его напильником до комбайна?
Бит 10 в PSW запрещает TRAP24 при активации ACLO или нет ?
Бит 10 в PSW запрещает TRAP24 при активации ACLO или нет ?
По приоритетам прерываний. Открываем файлик vm1_plm.v, в нем находим модуль vm1a_pli или vm1g_pli. Там я упорядочил более-менее по приоритетам, надо искать строчку где интересующий сигнал запроса прерывания неинвертирован. Для ACLO там есть такое:
assign p[12] = plir & ~psw[10] & ~psw[4] & ~qbto & aclo & ~uerr;
Откуда следует, что TRAP24 по запросу ACLO будет замаскирован битами psw[4] и psw[10], а также более приоритетными прерываниями и исключениями: тайм-аут qbus (qbto), неизвестный опкод (uerr). plir - это флажок режима работы (то есть произошел старт).
---------- Post added at 17:58 ---------- Previous post was at 17:37 ----------
1. Система команд у 1801ВЕ1 изначально была близка к DEC или почему ВМ1 решили делать на базе ВЕ1? Просто адаптировать, даже через микрокод ИМХО всё-равно трудоёмко
А вот и нет. Там была какая-то система проектирования микрокода, тот же микроассемблер. Поэтому, имея готовую подходящую микромашину, переписать для нее микрокод можно достаточно быстро. Шаблон ПЛМ же можно буквально за пару дней (а может и быстрее, если автоматизировано) сделать. Именно поэтому победил микропрограммный подход - разрабатывать софт гораздо проще чем жесткую логику. Я про средства разработки спрашивал, ответа насчет микрокода никакого нет, увы.
Я сделаю описание микропрограммы, если будут желающие - то можно будет сделать эмулятор именно микромашины (и на его базе тактовый эмуль ВМ1), и попытаться попрограммировать ее. Хотя бы добавить команды деления и арифметического сдвига - в FPGA у нас ПЛМ не столь ограничена как в оригинальном кристалле.
2. Правильно ли я понимаю, что ВМ1 делали когда не было реверса кристаллов T-11 и F-11 и не было 1807ВМ1 и 1811ВМ/ВУ/ВТ и их делали по описаниям на систему команд и шины Q-Bus?
Был уже LSI-11, тот же микропрограммный подход, и я вижу много общего у ВМ1 и LSI-11. Не буквально, ВМ1 достаточно оригинален, но кое-то принципиально общее.
3. Развитие не полностью совместимой линейки ВМ1/2/3, при наличии полных аналогов это тоже борьба министерств или они были чем-то лучше 1807/1811?
Если у меня есть своя разработка и вполне успешная, причем однокристаллная, в отличие от LSI-11/F-11/J-11, то почему бы ее не развивать? Да, это элемент конкуренции, но на выходе есть неплохое изделие, значит можно выпускать.
Все вот эти заморочи со стартом проца это унаследованное для работы с UNIBUS/Q-Bus? В смысле можно бы было обойтись и отпусканием RESET, но для соблюдения стандарта надо использовать ACLO/DCLO
Это очень древний принцип управления питанием и он до сих пор актуален. И в моих сегодняшних изделиях используется. ACLO - это сигнал раннего оповещения о проблемах с питанием. Это знакомый стрелочник с перегона позвонил и сообщил что поезд "Белопесецк-Москва" (на котором едет теща в гости) проследовал, и можно начать прятать пустые бутылки, пылесосить и собирать раскиданные носки. А DCLO - это когда уже поезд приехал, жена маму встретила, они тебя не дождались на вокзале, взяли такси, приехали и трезвонят в дверь, а у тебя еще головка бо-бо и перегар не выветрился :)
По ACLO завершают критические операции, сносят кеши на диски, по-быстрому закрывают сетевые соединения, переводят исполнительные органы (механику) в безопасное состояние и ждут DCLO. Если его долго нет, и ACLO восстановилось - то рестарт. В некоторых моих изделиях есть даже маленький литиевый аккумулятор, как в детском вертолетике, как раз на 10-15 секунд хватает гарантировано завершить самое критическое (снос кешей FAT-а например).
Это очень древний принцип управления питанием и он до сих пор актуален.Скорее - имелось в виду, что зависание процессора 1801ВМ1 при передёргивании DCLO - это дань совместимости с LSI-11, потому что стандарт Q-Bus не запрещает ресетить процессор закорачиванием DCLO при неизменном ACLO, а рестарт в такой ситуаци выглядит более адекватной реакцией, нежели зависание.
а рестарт в такой ситуаци выглядит более адекватной реакцией, нежели зависание.
Да, и я даже подумываю сделать в реплике автоматическую внутреннюю активацию ACLO при активации DCLO, чтобы процессор именно рестартовал и можно было обойтись одним простым сбросом при желании.
Да, и я даже подумываю сделать в реплике автоматическую внутреннюю активацию ACLO при активации DCLOДостаточно заменить детектор деактивации ACLO на детектор деактивации логического произведения ACLO и DCLO - тогда передёргивание любого из них будет в итоге приводить к рестарту, а вся остальная логика работы не изменится.
Активация ACLO без последующей активации DCLO прямо запрещена стандартом Q-Bus, а значит, обладая статусом "неопределённого поведения" - имеет законное право приводить к чему угодно, включая рестарт.
прямо запрещена
Ссылку в студию!
Особенно ссылку на несуществующие ACLO/DCLO относительно Q-Bus. Ибо логика сигналов всеже неколько отличается несмотря на некоторую схожесть (POK =~ -ACLO), (DCOK =~ -DCLO)
Ссылку в студию!http://pic.pdp-11.ru/images/aclo.png
http://pic.pdp-11.ru/images/aclo.png
Не имеет смысла без контекста.
Не имеет смысла без контекста.Смысл однозначен - каждая активация ACLO должна ( через заданное стандартом врямя ) сопровождаться активацией DCLO.
Смысл однозначен - каждая активация ACLO должна ( через заданное стандартом врямя ) сопровождаться активацией DCLO.
Смысл чего? Это пустые строчки вырванные из описания неизвестно чего (разумеется не из стандартов - там такого просто нету). Но хотя бы ссылку на то из чего они вырваны привести наверное надо? ;)
http://pic.pdp-11.ru/images/aclo2.png
---------- Post added at 20:58 ---------- Previous post was at 20:55 ----------
STD 160 - LSI-11 Bus Specification (http://bitsavers.trailing-edge.com/pdf/dec/standards/EL-00160-00-0_A_DEC_STD_160_LSI-11_Bus_Specification_Sep91.pdf)
А теперь перечитаем все описания процессоров (и стандартов) по времени после него и что имеем? ;)
---------- Post added 03.06.2015 at 00:01 ---------- Previous post was 02.06.2015 at 23:59 ----------
Не забудем также перечитать указанный стандарт - внимательно весь, а не вырвать отрывок из него!
Не забудем также перечитать указанный стандарт - внимательно весь, а не вырвать отрывок из него!В каком месте стандарта Q-Bus (http://bitsavers.trailing-edge.com/pdf/dec/standards/EL-00160-00-0_A_DEC_STD_160_LSI-11_Bus_Specification_Sep91.pdf) отменяется или уточняется прямое требование: once BPOK_H is negated - the entire power down sequence MUST be completed ?
ГОСТ 26765.51-86 (МПИ) допускает не подавать AИП (DCLO) при активном АСП (ACLO) если питание восстанавливается быстрее чем 4 мс. Но от процессора все равно по обработке TRAP24 требует перейти к программе пуска. На ВМ1 требования стандарта легко удовлетворить.
В каком месте стандарта Q-Bus отменяется или уточняется прямое требование: once BPOK_H is negated - the entire power down sequence MUST be completed ?
Начнем с простого... В каком месте речь идет о данных сигналах? Ибо несмотря на кажущуюся идентичность, даже простая логика показывает их различие... Насчет отмены - такого нет... Но... Еще раз внимательно перечитаем все перед и после эжтой фразы... Тогда может будет понятно, что это не фраза на все случаи жизни? Также перечитаем все описания процессоров где присутствует фраза о том, что это не соответствует данному стандарту... Ссылки сейчас приводить не буду. Навскидку - тот же KDJ11B - прямо так можно и искать по слову sequence и найдется место где написано про несовпадение...
ГОСТ 26765.51-86 (МПИ) допускает не подавать AИП (DCLO) при активном АСП (ACLO) если питание восстанавливается быстрее чем 4 мс. Но от процессора все равно по обработке TRAP24 требует перейти к программе пуска.Для того команда RESET при активном ACLO и вызывает рестарт у 1801ВМ1 - чтобы угодить "мудрым" требованиям ГОСТа, позволяющим системе питания нарушать стандарт Q-Bus и не активировать DCLO после ACLO.
CodeMaster
02.06.2015, 21:26
А вот и нет. Там была какая-то система проектирования микрокода, тот же микроассемблер. Поэтому, имея готовую подходящую микромашину, переписать для нее микрокод можно достаточно быстро.
То есть на базе ВЕ1 (при достаточности места в ПЛМ) можно было создать аналог любого 16-ти битного МП например 8086, 68000 или всё таки нет?
Был уже LSI-11, тот же микропрограммный подход, и я вижу много общего у ВМ1 и LSI-11. Не буквально, ВМ1 достаточно оригинален, но кое-то принципиально общее.
Т.е. ВМ1 как наследник ВЕ1 был близок к LSI-11, т.е. ВЕ1 был DEC-ориентированным МП или я опять не правильно понял?
По ACLO завершают критические операции, сносят кеши на диски, по-быстрому закрывают сетевые соединения, переводят исполнительные органы (механику) в безопасное состояние и ждут DCLO.
Схематически да, но каков должен быть интервал между ACLO и DCLO что бы (с учетом быстродействия тех лет) завершить эти критические операции? К тому же у периферии (например дисков) этот интервал должен быть минимум в 2 раза больше.
В некоторых моих изделиях есть даже маленький литиевый аккумулятор, как в детском вертолетике, как раз на 10-15 секунд хватает гарантировано завершить самое критическое
Но, раньше таких АКБ не было, а что на это хватит электролитов в БП, что-то берут сомнения.
Навскидку - тот же KDJ11BРечь про официальные стандарты, одобренные инженерным комитетом DEC. Как видим - вплоть до 1981 г. стандарт Q-Bus прямо запрещал оставлять DCLO неактивным между активацией и деактивацией ACLO. Других редакций стандарта Q-Bus в сети нет.
Диаграмма с БП ДВК.
http://pic.pdp-11.ru/images/sm.jpg
То есть на базе ВЕ1 (при достаточности места в ПЛМ) можно было создать аналог любого 16-ти битного МП например 8086, 68000 или всё таки нет?
Надо чтобы хватало ресурсов микромашины - был регистровый файл достаточного размера, вычислялись нужные флаги (например AC для 580ВМ80А), формат специальных регистров должен соответствовать (PSW например), но переформатировать аппаратно очень недолго, а микропрограммно - может быть сложно), то есть это все мелочи для реализации аппаратным путем, но если их нет - то нужную архитектуру реализовать сложно. Ну как вот реализовать i8080 если регистр инструкций исключительно 16-битный :). В-общем, одной микропрограммой при смене системы команд не обойтись, но модификация микромашины может быть вполне косметической.
ВЕ1 был DEC-ориентированным МП или я опять не правильно понял?
Не знаю, я не знаком с архитектурой и системой команд НЦ.
Схематически да, но каков должен быть интервал между ACLO и DCLO что бы (с учетом быстродействия тех лет) завершить эти критические операции?
Да тогда и файловые системы были попроще, и памяти и размеры кешей поменьше. А сейчас, пишешь на SD-карту, а у нее - бац, и спорадическая задержка записи на 300мс - это контроллер внутри карты решил чего-то перенести/переформатировать. И делай чего хочешь, чтобы вторая копия FAT не испортилась.
CodeMaster
03.06.2015, 07:50
Да тогда и файловые системы были попроще, и памяти и размеры кешей поменьше. А сейчас, пишешь на SD-карту, а у нее - бац, и спорадическая задержка записи на 300мс - это контроллер внутри карты решил чего-то перенести/переформатировать.
Ай, та ладно, а если вывод производился на перфо- или магнитную ленту? Ведь этот стандарт был введён именно в те времена. И задержку в питании на 300 мс при потреблении в мА легко компенсировать, а когда система потребляла киловатты?
Но, это всё теория, которая в принципе понятна и не оспаривается, а мне собственно интересно было услышать от тех кто был хорошо знаком с этими машинами изнутри - на каких системах производилась обработка этого прерывания? Ну, например в RT-11 есть обработка TRAP24 или в какой-то другой, причём не "пустышка", а именно с сохранением данных?
CodeMaster, Добавлю и я информации.
Вы знаете что такое промышленная сеть?
Так вот, в промышленной сети используется 2 ввода с разных электростанций. Когда по какой либо причине пропадает напряжение с одного ввода, то автоматически ( с помощью АВР ) переключается на второй ввод. Между переключениями существует какое-то время (не больше 0,5с) на которое ЭВМ остаётся без питания. На это время и должен блок питания ЭВМ продолжать работать автономно.
Но, раньше таких АКБ не было, а что на это хватит электролитов в БП, что-то берут сомнения. Вы видели электролиты в БП ЭВМ? В классическом трансформаторном (не импульсном) БП электролит стоит размером со стакан. В импульсном стоят два электролита (на два плеча силовых транзисторов) чуть поменьше в размерах. Емкость примерна 22000мкф-33000мкф. Его достаточно чтобы ЭВМ "не заметил" переключения АВР, и достаточно чтобы сохранить временные данные и припарковать головы ЖД. И это не считая электролитов по питанию +5/12 которые тоже помогают держать питание.
Ай, та ладно, а если вывод производился на перфо- или магнитную ленту? На перфоленте нет кеша, там нечего сохранять. Магнитная лента в основном используется для архивации так-как это медленный носитель. Вообще принцип записи такой что ЭВМ пока от источника к приёмнику не передаст все данные и не сравнит их на отсутствие различий, то данные считаются не записанными.
Так-же добавлю что АСП и АИП использует не только процессор, но и контроллеры тоже для установки, перевод режима работы например.
CodeMaster
03.06.2015, 10:42
его достаточно чтобы ЭВМ "не заметил" переключения АВР
Но, ведь в реальности она заметит это по активации АСП/ACLO? И выполнить TRAP24 и будет произведён рестарт?
На это время и должен блок питания ЭВМ продолжать работать автономно.
Т.е. по твоему мнению ACLO и DCLO именно для это задумывались? Тогда после активации ACLO система должна гарантированно обработать TRAP24, подготовится к активации DCLO, и если DCLO не активировался, а ACLO деактивировался, продолжить работу с прерванного места?
Вы видели электролиты в БП ЭВМ?
Да, конечно, но всё это надо рассматривать относительно потребляемой системой мощности.
и достаточно чтобы сохранить временные данные
Вот мне и интересно, в каких системах это использовалась на практике
и припарковать головы ЖД.
Кстати, а как решается вопрос с тем, что у ЖД свой БП? Этот БП должен тогда соответствовать каким-то минимальным требованиям между активацией ACLO и DCLO? А то ведь процессор попытается что-то сделать, а система НЖМД будет уже в ауте.
Но, ведь в реальности она заметит это по активации АСП/ACLO? И выполнить TRAP24 и будет произведён рестарт?Там ещё 10мс БП даёт на раздумье, и только после этого БП снимает АСП. Я сам пробовал ресетнуть ДВК с помощью выключателя питания. Так вот, это не всегда получалось так-как ДВК сохранял загрузку даже спустя 3-5 сек без питания.
Т.е. по твоему мнению ACLO и DCLO именно для это задумывались? Тогда после активации ACLO система должна гарантированно обработать TRAP24, подготовится к активации DCLO, и если DCLO не активировался, а ACLO деактивировался, продолжить работу с прерванного места?Если в течении 10мс пришло сетевое питание то БП поднимет АСLO обратно.
Да, конечно, но всё это надо рассматривать относительно потребляемой системой мощности.Да. Кроме этого, ёмкость конденсаторов рассчитывается так чтоб они поддерживали питание при максимальной нагрузке,+ %емк. накидывается ещё на перепады температур и + %емк. на старение конденсаторов.
Вот мне и интересно, в каких системах это использовалась на практике В тех системах где требуется непрерывная работа и без потери данных.
Кстати, а как решается вопрос с тем, что у ЖД свой БП? Этот БП должен тогда соответствовать каким-то минимальным требованиям между активацией ACLO и DCLO? А то ведь процессор попытается что-то сделать, а система НЖМД будет уже в ауте.Я не слышал про отдельные БП для ЖД.
NovaStorm
03.06.2015, 12:18
Не знаю, я не знаком с архитектурой и системой команд НЦ.
А на неё доки вообще есть?
CodeMaster
03.06.2015, 13:02
Я сам пробовал ресетнуть ДВК с помощью выключателя питания. Так вот, это не всегда получалось так-как ДВК сохранял загрузку даже спустя 3-5 сек без питания.
Это говорит только о инертности в БП, или в этой ситуации ACLO и DCLO как-то задействованы, что позволяет сохранить загрузку?
Если в течении 10мс пришло сетевое питание то БП поднимет АСLO обратно.
Ты же писал, что переключаться может до 50 мс, интересен именно этот вариант.
Я не слышал про отдельные БП для ЖД.
Этот (http://my.mail.ru/mail/ak_kislov/photo/_myphoto/21.html) разве не с отдельным БП?
Это говорит только о инертности в БП, или в этой ситуации ACLO и DCLO как-то задействованы, что позволяет сохранить загрузку?Я точных измерений не проводил. ACLO то точно снимается, а DCLO снимается видимо при привышении порога разряда конденсатора. То-есть конденсатор не до конца разряжен но уже не хватает заряда для удержания уровней питания.
Ты же писал, что переключаться может до 50 мс, интересен именно этот вариант.Это где такое?
Если имеется ввиду АВР, то переключение не более 0,5с=500мс . И это по ГОСТу а в реалии переключение гораздо меньше.
Этот разве не с отдельным БП?С отдельным, но питается от одной сети с ЭВМ. Но там вроде пружина (просто механика) должна отвести головы при отключении питания.
CodeMaster
03.06.2015, 14:04
Ладно, закроем вопрос с ACLO/DCLO, то ли я не так спрашиваю, то ли меня не так понимают ;-)
А кто-нибудь догадается что это за микросхема? :)
(четверть разрешения, 56МБ) (http://u.zeptobars.ru/yuot/MISC/unk-fd.jpg)
Ладно, закроем вопрос с ACLO/DCLO, то ли я не так спрашиваю, то ли меня не так понимают ;-) Скорее я не так сказал. Ещё один момент есть. После загрузки МС1201-04 при нажатии кнопки УСТ. вместо того чтобы попасть в пульт, у меня появлялась приглашение RT. Так вот я из-за этого и перезагружал с помощью выключателя (ну можно было и через кнопку ПУЛЬТ). То-есть процессор ВМ3 находился в каком-то хитром режиме что выходил в RT даже при перещёлкивании тумблера. Ну и БП не был на 100% загружен. Из потребителей только плата МС и КЦГД.
По поводу БП, ранее при объяснении немного напутал, поэтому исправляюсь.
Там ACLO гарантированно должно удерживается не менее 10мс и после 10мс не менее 4мс DCLO. В реалии может быть и больше но не меньше.
CodeMaster
03.06.2015, 21:22
А кто-нибудь догадается что это за микросхема?
Видимо нет, или ещё не все посмотрели?
Скорее я не так сказал.
Может и так, но я довольно давно прочитал на сайте form'а этот текст:
Существует миф, что PDP-11 машины нужно непременно питать родным блоком питания, так как при запуске нужно правильно выставлять сигналы состояния питания. На самом деле вся "трудность" заключается в том, чтобы выставить сигналы BDCOK и BPOK друг за другом после включения питания. При этом предъявляется единственное требование - между этими двумя сигналами должно пройти достаточное время (не стану даже лезть в документацию, чтобы уточнять).
Т.е. в моём обывательском представлении все эти танцы с бубном питанием, просто потому, что этого требует стандарт и в реальности не очень востребовано. Но, судя по сложности задумки это явно было придумано не просто что бы жизнь мёдом не казалась.
Но, судя по сложности задумки это явно было придумано не просто что бы жизнь мёдом не казалась.
Кстати говоря - ИМХО: привязка к родному железу \ сервису - как минимум
обычная вещь у крупных западных брэндов и опять же - давайте не будем
забывать где и зачем в первую очередь все эти железяки пользовались
в первую очередь? ) (* хотя я могу и ошибаться в своём мнении).
Вопросы про сигнал SEL1.
1. Этот сигнал активируется только при обращении к адресу 177716 или ещё когда-нибудь ?
Например - процессор ВМ2 по непонятной причине активирует SEL не только при чтении регистра начального пуска, но и при чтении PC и PSW из вектора начального пуска.
2. Сколько тактов активен сигнал SEL1 при чтении и записи ?
3. Через сколько тактов после активации сигнала SEL1 считывается состояние шины при чтении SEL1.
Видимо нет, или ещё не все посмотрели?
Хорошо, буду давать подсказки. А то неинтересно сразу вывалить ответ :)
Подсказка 1: это советская микросхема, выпуск середины 80-х.
Вопросы про сигнал SEL1.
1. Этот синал активируется только при обращении к адресу 177716 или ещё когда-нибудь ?
Процессор следит за адресной шиной (AD15-AD1) и SYNC постоянно (входы прицеплены прямо к ножкам), независимо от того кто в данный момент управляет шиной. Сигнал обращения к периферийному блоку (177x00, зависит от номера процессора), а также разряды адреса AD3-AD1 фиксируются на латче, который разрешен при высоком SYNC. А выходов латчей уже формируется комбинационная функция SEL1.
Таким образом:
- если SYNC высокий, то SEL1 непрерывно отображает состояние AD15-AD1 (постоянно проверяется на 177716).
- если SYNC низкий, то SEL1 фиксирует состояние которое было в момент ниспадающего фронта SYNC
Аналогично SEL2 для 177714.
Хорошо, буду давать подсказки. А то неинтересно сразу вывалить ответ :)
Подсказка 1: это советская микросхема, выпуск середины 80-х.
Это было и так ясно, по русским буквам и по тому что это заказная матрица которых токо ангстрем и делал.
offtop: вот бы глянуть как оно все в 556рт1 сделанно... ну или хотябы 155ре3
Это было и так ясно, по русским буквам и по тому что это заказная матрица которых токо ангстрем и делал.
Хорошо :) Кристаллизуем достигнутый результат: это советский БМК, середины 80-х
Подсказка 2: там два слоя металла, их хорошо видно
offtop: вот бы глянуть как оно все в 556рт1 сделанно... ну или хотябы 155ре3
У меня есть 556РТ4А, но пока лень пилить, в эту партию отвариваемых микросхем не попадет.
Например - процессор ВМ2 по непонятной причине активирует SEL не только при чтении регистра начального пуска, но и при чтении PC и PSW из вектора начального пуска.
В ВМ2 SEL используется не только при чтении регистра начального пуска, но и для обращения к адресному пространству HALT. Если при активном SEL, SYNC неактивен и активен DIN, то это читается регистр начального пуска, а если SEL активен при активном SYNC, то тогда идет обращение к адресному пространству HALT. А вектор начального пуска как раз и находится в адресном пространстве HALT.
Судя по этой осциллограмме, при чтении адреса начального пуска - SEL1 активен 5 тактов, после чего идёт пауза в 9 тактов :
http://pic.pdp-11.ru/images/sel1.png
...
При выполнении команды START процессор 1801ВМ1 первым делом производит цикл ЧТЕНИЕ-МОДИФИКАЦИЯ-ЗАПИСЬ в режиме SEL1. Интересно узнать, сколько тактов проходит между снятием DIN и выставлением данных записи.
---------- Post added at 00:48 ---------- Previous post was at 00:47 ----------
В ВМ2 SEL используется не только при чтении регистра начального пуска, но и для обращения к адресному пространству HALT.Точно!
Судя по этой осциллограмме, при чтении адреса начального пуска - SEL1 активен 5 тактов
SEL привязан к SYNC. Если шиной владеет процессор, то SYNC будет синхронизирован с CLC, если внешний агент - то совсем необязательно.
При выполнении команды START процессор 1801ВМ1 первым делом производит цикл ЧТЕНИЕ-МОДИФИКАЦИЯ-ЗАПИСЬ в режиме SEL1. Интересно узнать, сколько тактов проходит между снятием DIN и выставлением данных записи.
А в той же теме есть ответ (http://zx-pk.ru/showpost.php?p=591840&postcount=36). Циклы DATIO определяются автоматом шины, а АЛУ всегда успевает отдать данные за 3 такта (ALU работает по ниспадающему фронту). Поэтому как зафиксировалось чтение (два нарастающих фронта CLC при которых DIN & RPLY активны), так через 3 такта начнется запись. Это для всех DATIO.
Хорошо, буду давать подсказки. А то неинтересно сразу вывалить ответ
Серия 1515 ?
offtop: вот бы глянуть как оно все в 556рт1 сделанно... ну или хотябы 155ре3
Зайди на форум Портативное ретрорадио, тема ПРОЗЕКТОРСКАЯ, последние страницы - РТ4, РТ5 и РЕ3 вскрывались.
Серия 1515 ?
Нет, 1515 - один металл, и 1515 - это КМОП :)
Подсказка 2.5: на квестовом снимке два слоя металла, и был завод который такую технологию надежно в 80-ые освоил, и это не Ангстрем.
- если SYNC высокий, то SEL1 непрерывно отображает состояние AD15-AD0 (постоянно проверяется на 177716).
- если SYNC низкий, то SEL1 фиксирует состояние которое было в момент ниспадающего фронта SYNC
Аналогично SEL2 для 177714.А как отреагируют сигналы SEL1 и SEL2 на появление адресов 177717 и 177715 ?
А как отреагируют сигналы SEL1 и SEL2 на появление адресов 177717 и 177715 ?
Как на 177716 и 177714, младший разряд адреса декодером игнорируется.
Как на 177716 и 177714, младший разряд адреса декодером игнорируется.Тогда есть смысл подкорректировать изначальное утверждение :
"SEL1 непрерывно отображает состояние AD15-AD1 (постоянно проверяется на 177716)"
...
Правильно ли я понимаю, что ВМ1 и ВМ2 всегда выставляют адрес на шину "как есть", никогда не очищая младший бит ?
...
Правильно ли я понимаю, что ВМ1 и ВМ2 всегда выставляют адрес на шину "как есть", никогда не очищая младший бит ?
ВМ1 - да, младший бит адреса не видоизменяет.
Ячейка ввода-вывода "неизвестного" БМК. Биполярная технлогия достаточно сложно поддается визуальной расшифровке - диоды не отличимы от диодов Шоттки, от стабилитронов и транзисторов. Это 3D структуры, вглубь кристалла уходят, полярность транзистора тоже по внешнему виду не определяется. Но если есть хоть страничка документации - то не все так плохо :)
http://s50.radikal.ru/i130/1506/5d/898b4757ecdet.jpg (http://s50.radikal.ru/i130/1506/5d/898b4757ecde.jpg)
Вот тут на картинке уже есть разные диоды, резисторы и транзисторы обеих полярностей.
Ячейка ввода-вывода "неизвестного" БМК.
Не томите нас, графиня, откройте свой секрет. )
Не томите нас, графиня, откройте свой секрет. )
Сказал граф, стуча манжетами по паркету. :biggrin:
Подсказка 3: выпускалась заводом ВЗПП, он на тот момент нормально освоил планаризацию первого слоя металла и умел выпускать микросхемы с двумя слоями металла. У Ангстрема были проблемы с этим. Технология "квестовой" микросхемы биполярная - ТТЛШ.
CodeMaster
04.06.2015, 23:44
Подсказка 3: выпускалась заводом ВЗПП,
Предположу, что H1806XM1. Интересно, что ВЗПП до сих пор выпускает серию 1804
---------- Post added at 23:42 ---------- Previous post was at 23:33 ----------
Предположу, что H1806XM1
Не, неправ, думал что-то PDPшное должно быть. Вообще из ТТЛШ 1527ХМ1, 1540ХМ1, 1547ХМ1, 1548ХМ1, 1548ХМ2 осталось определить, что выпускал ВЗПП.
---------- Post added at 23:44 ---------- Previous post was at 23:42 ----------
Технология "квестовой" микросхемы биполярная
Это не оно?
биполярный БМК на 3000 вентилей оригинальной конструкции
1527ХМ1
Ура, наконец-то! Да, это КМ1527ХМ1-715, любезно подаренная yursav. С моей стороны было бы садизмом просить чтобы еще и номер шаблона определили (715) :)
1527ХМ1-715 (http://u.zeptobars.ru/yuot/1527/xm1-715-hd.jpg) (полное разрешение, 596МБ)
1527ХМ1-715 (http://u.zeptobars.ru/yuot/1527/xm1-715-hd.jpg) (четверть разрешение, 56МБ)
Ещё вопросы про работу 1801ВМ1:
1. В какой момент при обслуживании VIRQ принимается адрес вектора - до сохранения PC и PSW или после ?
2. Через сколько тактов после прихода RPLY снимается DOUT ?
3. Сколько тактов держится DOUT при записи в режиме SEL1 / SEL2 ?
1. В какой момент при обслуживании VIRQ принимается адрес вектора - до сохранения PC и PSW или после ?
Порядок такой:
-(SP) = PSW
-(SP) = PC
R12 = vector (чтение по IAKO)
PC = (R12)+ (R12 невидимый программно регистр из блока R0-R13)
PSW = (R12)
2. Через сколько тактов после прихода RPLY снимается DOUT ?
Должен быть обнаружен активный RPLY на нарастающем фронте CLK, и на следующем нарастающем фронте CLK активный DOUT будет снят. То есть - один такт.
3. Сколько тактов держится DOUT при записи в режиме SEL1 / SEL2 ?
Полтора такта, начинается на ниспадающем фронте CLK, и заканчивается на нарастающем фронте через один нарастающий фронт. ЕМНИП, это я вроде проверял и на реальном процессоре.
Настраивая тайминги абстрактного потактового эмулятора 1801ВМ1 снова натыкаюсь на странности некоторых таймингов, замеченные при тестировании.
Например, простую на вид команду [ MOV R1,(R0)+ ] - ВМ1 при частоте 5.3 МГц и задержке памяти 6 тактов - выполняет аж 41 такт, при том, что команду [ MOV R1,R0 ] - этот же процессор выполняет всего за 14 тактов.
Интересно, что в это время происходит на шине. Нет ли там какой-то отменённой предвыборки или цикла ЧТЕНИЕ-МОДИФИКАЦИЯ-ЗАПИСЬ ..
Например, простую на вид команду [ MOV R1,(R0)+ ] - ВМ1 при частоте 5.3 МГц и задержке памяти 6 тактов - выполняет аж 41 такт,
В модели QSync с задержкой памяти такт выполняет 30 тактов - между фронтами SYNC от это команды и до следующей. Но там что-то не совсем то - исполняется за 8 микрокоманд.
Интересно, что в это время происходит на шине. Нет ли там какой-то отменённой предвыборки или цикла ЧТЕНИЕ-МОДИФИКАЦИЯ-ЗАПИСЬ ..
На шине ничего не происходит - законные одно чтение кода инструкции и одна запись данных, долго вычисляется адрес записи, начинается транзакция, и потом долго предоставляются данные для записи (время от SYNC до DOUT).
при том, что команду [ MOV R1,R0 ] - этот же процессор выполняет всего за 14 тактов.
А тут только 4 микроинструкции
Новости проекта: 1801ВМ1 версия 1.2m (http://u.zeptobars.ru/yuot/1801/VM1/vm1_rev12m.rar)
- внешняя шина Qbus заменена на синхронную шину Wishbone
- исправлена ошибка в таймера (неверно работал в режиме slow - с разными частотами ядра и блока таймера, в оригинальном процессоре этой ошибки нет - там частоты одинаковы)
- окончательно добавлена и протестирована микропрограмма для кристалла с буквой Г.
В-общем, это пока еще тестировалось на модели, но внешняя шина Wishbone хорошо подходит для ПЛИС, буду делать проект на реальной железке.
Да, на модели цикл чтения Wishbone занимает три такта (лень было переписывать модель ОЗУ с тестом), а цикл записи 2 такта, и по сравнению с Qbus исполнение теста 791404 заняло примерно на 10 процентов меньше времени. Тормозит само микроядро, внешний интерфейс влияет слабо.
Сделал тестовый проект на реальной FPGA - на плате Altera DE0. Прикрутил 4KW ОЗУ с тестовой программой, прикрутил регистры 177714 на 7-сегментный индикатор, процессор вывел тестовую цифру "1801". Проект собрался на EP3C16-7 на 100 МГц. Тест 791401 пока не проходит.
Текущее состояние: Версия 1.3a (http://u.zeptobars.ru/yuot/1801/VM1/vm1_rev13a.rar)
В-общем, запустить тесты, написать и добавить ВП1-065, нарастить ОЗУ до 56К (это быстро - из внутренней памяти) и практически готова МС1201.01.
В-общем, запустить тесты, написать и добавить ВП1-065, нарастить ОЗУ до 56К (это быстро - из внутренней памяти) и практически готова МС1201.01.
Для ретрокомпьютерщиков так же принципиальна важна способность работать в точности на оригинальной растактовке. А измененная шина - это уже уход от подобной возможности.
в точности на оригинальной растактовке.
Для этого предназначена ветка Qsync - там сохранена оригинальная шина, и на базе этой ветки можно сделать такой себе ВМ-улятор - плату заменяющую собственно микросхему ВМ1. Только устанавливался бы такой ВМ-улятор в старые платы и был бы ограничен их возможностями, абсолютно ничего нового он не дал бы. И микросхема 1801ВМ1 не является дефицитом. Но задачу "оригинальной растактовки" ВМ-улятор решает.
Поэтому так - ветка Qsync закончена, можно больше ничего не делать, проект закрыт, реплика готова. А можно пойти дальше - сделать полную систему на ПЛИС, работающую на высокой частоте, но с времянкой реального устройства.
В ветке Wsync предусмотрен режим эмуляции точной растактовки - работа в "медленном" режиме. Точная растактовка имеет смысл исключительно для повторения времянки реальных устройств, тут есть два фактора - частота ядра до 5МГц и задержки ответа внешних устройств, особенно тут интересна репликация ВП1-037 в случае повторения БК. Поэтому соответственно есть два блока поддержки точного тактирования - внутри процессора и во внешней системе. Имея быстрый Wishbone на 100МГц можно эмулировать времянку медленного Qbus на 5 МГц с неплохой точностью.
Внутри процессора предусмотрен "дульный компенсатор", ядро работает постоянно на высокой частоте - это делает его простым и "идентичным натуральному" и работает специальный счетчик, считает такты высокой частоты - инкрементируется на 100МГц. В режиме точной растактовки, при запуске транзакции на внешней шине этот счетчик начинает считать такты медленной частоты - декрементируется на 5МГц. По достижению нуля транзакция будет запущена на шине. Внешний блок (например БК) уже эмулирует работу системы, и завершает транзакцию согласно своим представлениям. Тут будет специальный механизм - сама транзакция выполнится на полной скорости, чтобы не нарушать работу системы (нельзя захватиьт шину на 2 мкс и мешать, например, работе видеоконтроллера из разделяемой SDRAM), но процессору будет сообщен момент когда она закончится в реальном времени.
Итого - все транзакции быстрые, работают на быстрой шине снаружи процессора и запускаются и завершаются синхронно с частотой 5МГц в те же самые такты когда они появиись бы в реальной системе.
К тому же, в собственно реальных системах с точной растактовкой не все гладко - ВМ1А и ВМ1Г работают в этом плане по-разному, есть отличия, точно я их не искал, но тесты 791401 и 791404 на разных микропрограммах исполняются разное время. Также в процессоре есть индетерминизим - прескайлеры таймера шины и ВЕ-таймера не сбрасываются, после старта имеют произвольные значения, таким образом, оригинальный ВМ1 может работать минимум в 8 разных фазах относительно контроллера динамической памяти. На практике тайм-аут шины происходит редко, поэтому фазы исполнения команд довольно быстро (за несколько циклов максимум) сойдутся к одной, но расфазировка все равно будет вылазить при каждом тайм-ауте.
Ну и максимально точная растактовка имеет какой-то смысл только для БК. А в ДВК был зоопарк процессоров, плат и частот - не везде даже кварц в генераторе стоял, там ПО не очень требовательно к точности растактовки.
Для этого предназначена ветка Qsync - там сохранена оригинальная шина, и на базе этой ветки можно сделать такой себе ВМ-улятор - плату заменяющую собственно микросхему ВМ1. Только устанавливался бы такой ВМ-улятор в старые платы и был бы ограничен их возможностями, абсолютно ничего нового он не дал бы. И микросхема 1801ВМ1 не является дефицитом. Но задачу "оригинальной растактовки" ВМ-улятор решает.
Сейчас ВМ1 не является дефицитом, но настанет время, когда его перестанут выпускать. Тут и пригодится точный клон на ПЛИС или на КПЛИС(квантовом плис 21 века, который наверняка появится ;-)).
А исправляются ли ошибки, найденные при разработках в других ветках, в ветке Qsync? Что-то там про таймер вы писали или я не помню уже, какая была ошибка.
А исправляются ли ошибки, найденные при разработках в других ветках
Да. К тому же тактирование моделей Async и Qsync полностью совпадает, на обоих был прогнан тест 791401 и достигнуто полное совпаление осциллограмм на внешних выводах - все сигналы Qbus ставляться и снимаются в той же фазе по тем же ниспадающим и нарастающим фронтам в обоих моделях, и совпадают с реальным процессором (тут я смотрел отдельные транзакции, правда). Сейчас отлаженная модель Wsync (v1.3b) имеет единственное отличие - DATIOx выполняется на 2 такта меньше чем в оригинале, чистые же DATI и DATOx выполняютс ровно столько же. Такое отличие дало ускорение времени теста менее чем на 1 процент.
В-общем, поисправлял ошибки, добавил в проект заглушку на терминал (в моделсиме можно и вывод увидеть) для генерации прерываний, и успешно прошлись тесты 791401 и 791404 на реальной ПЛИС для ВМ1А/Г.
Версия 1.3b (http://u.zeptobars.ru/yuot/1801/VM1/vm1_rev13b.rar)
Используемые ресурсы:
http://s017.radikal.ru/i425/1506/d0/ada2caa5f978.png
Частотные характеристики:
http://s017.radikal.ru/i440/1506/28/3d007c069614.png
Готов первый вариант давно обещанного адаптера шины МПИ:
Адаптер шины МПИ для эмулятора ДВК-1 (http://zx-pk.ru/showthread.php?t=25252)
Теперь C-модели МПИ-устройств можно с минимальными переделками запускать в эмуляторе ДВК.
Потихоньку изучаю микрокод и пишу документацию, чтобы завершить проект.
На днях еще попался симпатичный экземпляр ВМ1Г:
http://s020.radikal.ru/i719/1508/19/500079f4ecc5t.jpg (http://radikal.ru/fp/4bb87533cfd74146a9bc0238f9875f50)
На плате еще невиданные ранее ВП-шки - 026, 028, 031, 038, а также РЕ-шки 072, 074, 076, 0209. ПЗУ, наверное, имеет смысл считать - это недолго, а ковырять матрицы - уже нет.
http://s017.radikal.ru/i430/1508/f0/53faad417582t.jpg (http://radikal.ru/fp/cd3aceb7ea234e58919ac487c0feb344)
ПЛМ по схеме Sum-Of-Products - это представление любой логической функции в канонической дизъюнктивной форме. ПЛМ можно просто рассматрифать как набор некоторых логических функций (можно реализовать ЛЮБУЮ комбинационную от данного числа входных переменных) с фиксированной одинаковой задержкой распространения.
Насчет почитать - я в свое время наткнулся на тоненькую книжицу "Секционные процессоры и их программирование" (Гришин, Угольков), она меня в 90-ом году потрясла просто, стало ясно как процессоры внутри устроены. В электронном виде я эту книжку не нашел, но копать в сторону этой тематики.
Вот тут кое-что есть по предмету.
Цифровые устройства и микропроцессоры, стр.84 "Построение цифровой схемы по произвольной таблице истинности"
https://books.google.ru/books?id=kEgmNkl47eoC&printsec=frontcover#v=onepage&q&f=false
Vslav, вчера мне удалось с помощью вашей программы mcode вычислить все невалидные операции процессора, просто подав 0x7E в качестве начального адреса и вычисляя выходной сигнал 12.
Для того чтобы сигнал 12 выдавался правильно, на входе должно быть:
mr[14] = 0
mr[13] = 0
mr[12] = 1
mr[11:7] -- (флаги) любые
mr[6:0] -- (адрес) 0x7E
Пытаюсь заставить мою модель ВМ1 на си проходить тест 791404.
В основном проблемы возникали из-за того, что эмулятор терминала неправильно выполнял цикл генерации векторного прерывания.
Но тут возникла непонятная ситуация в тесте 71
Вот кусок теста
2$: tstb tps ;tps = 177564
bpl 2$ ;
mov #0, tpb ;tpb = 177566
bis #100, tps ;
clr status ;
mtps status ;
wate3: wait ;
командой bis #100, tps разрешается генерация векторного прерывания терминалом. Но поскольку прерывания разрешены, то сразу возникает прерывание по вектору 064. Путём манипуляций с задержкой выдачи VIRQ удалось достичь того, что прерывание возникает сразу перед выполнением wait. Увеличение задержки ведёт к тому, что предыдущие тесты перестают проходить. Как можно добиться того, чтобы прерывание происходило тогда, когда этого задумали авторы теста?
Есть ли где-то информация по растактовке работы ВП1-035/ВП1-065 ? Или описание их работы с временными задержками?
vslav, когда будете заниматься ВМ2 -- напомню что есть Техническое описание от Titus'а:
http://zx-pk.ru/showthread.php?t=17284
Вот прямая ссылка на архив: http://www.felixl.com/1801VM2SPECS.zip
В частности, про блок микропрограммного управления (БМУ) там говорится что он тоже построен на ПЛМ:
дешифратор команд (ДШК) на 50 логических произведений,
накопитель микрокоманд (НМК) на 200 логических произведений.
Ну и по микрокомандам там кое-что расписано.
И там есть конечно некоторые параллели с тем что мы видим в микрокомандах ВМ1.
командой bis #100, tps разрешается генерация векторного прерывания терминалом. Но поскольку прерывания разрешены, то сразу возникает прерывание по вектору 064.
А почему сразу разрешается прерывание? Ведь до этого в TPB записывается значение, соответственно в TPS сбрасывается бит готовности в 7-ом разряде. Поэтому установка бита разрешения прерывания происходит при сброшенном бите готовности. А вот когда 1801ВП1-035/065 передаст этот байтик в терминал, то тогда и появится бит готовности, ну и произойдет прерывание.
1801ВП1-035/065 имеют буферный регистр. Т.е. если ничего не передается, то первый записываемый байт из буферного регистра сразу же передается в сдвиговый и начинает передаваться со скоростью 9600. Следующий записанный байт остается в буферном регистре и ждет освобождения сдвигового. Вот когда сдвиговый освободится, то он перепишется туда, ну соответственно установится и бит готовности. Поэтому в тесте специально идет запись двух нулей в TPB. При первой записи значение практически сразу же попадает в сдвиговый регистр, ну и соответственно устанавливается бит готовности, а вот при записи второго байта оно уже остается в буферном регистре и ждет освобождения сдвигового, соответственно и бит готовности сброшен. За это время ожидания исполнятся и CLR STATUS, и MTPS STATUS. Ну и начнет выполняться WAIT.
При первой записи значение практически сразу же попадает в сдвиговый регистр, ну и соответственно устанавливается бит готовностиКстати - мы это тестировали и получилось, что 1801ВП1-035/065 после первой записи выдаёт прерывание ( если не ошибаюсь ) через 70 тактов CLC.
А почему сразу разрешается прерывание?
Потому, что я не знаю временных задержек, и написанная мной модель работает мгновенно. Где эти самые задержки посмотреть, я тоже не знаю.
Записанное значение в TPB когда начинает передаваться с заданной скоростью (9600 по умолчанию), сразу после записи в регистр или через сколько-то тактов?
Бит готовности устанавливается после окончания передачи сразу, или с какой-то задержкой?
Когда возникает запрос на прерывание, если оно разрешено, после установки бита готовности, сразу, или через сколько-то тактов?
Кстати - мы это тестировали и получилось
Где можно посмотреть результаты?
Где можно посмотреть результаты?Тест здесь (http://zx-pk.ru/showthread.php?postid=567532), результат запуска там же - в следующем сообщении.
Записанное значение в TPB когда начинает передаваться с заданной скоростью (9600 по умолчанию), сразу после записи в регистр или через сколько-то тактов?После передачи байта из буферного регистра в сдвиговый. В тот же момент освобождается буферный регистр и устанавливается бит готовности. Время это для разных портов различается. Для 035/065 - это ( похоже ) 32 такта, для портов DEC больше ( весьма похоже на половину времени передачи одного бита ).
Бит готовности устанавливается после окончания передачи сразу, или с какой-то задержкой?Бит готовности устанавливается после передачи байта из буферного регистра в сдвиговый. Время до установки бита готовности равно [ оставшееся время передачи байта из сдвигового регистра в линию + время передачи байта из буферного регистра в сдвиговый ]. Когда никакой байт ещё не передаётся - первое слагаемое равно нулю.
Когда возникает запрос на прерывание, если оно разрешено, после установки бита готовности, сразу, или через сколько-то тактов?Похоже, что бит готовности и бит разрешения прерывания объединяются по И в сигнал IRQ - задержка там вряд ли привязана к тактам.
Наконец-то получилась рабочая модель процессора ВМ1 сконвертированная из верилога в си. Проходит тесты 791401 и 791404 (для типа процессора ВМ1А).
Из недостатков: модель терминала так и осталась притянутой за уши, лишь бы тест 791404 проходил. Нет никаких удобств по разного рода тестированиям и экспериментам, всё интересуемое делается через правку исходников и перекомпиляцию в среде VS. Недостаточная визуализация работы процессора. Кривоватая реализация Z-состояний выходов процессора, без которой долго ничего не хотело работало.
Из достоинств: в директории vm1cpu находится рабочая потактовая модель процессора (неоптимизированная, простой механический перевод из верилога как есть), пример её использования находится в директории qbusboard, там реализована простейшая материнская плата.
Исходник 53262
Давно было отфотографировано и отреверсено, оставалось доделать схему и промоделировать, ожидало пока время и настроение будут: Цифровая археология 1801: В чащах юга жил бы цитрус 065 (http://forum.pk-fpga.ru/viewtopic.php?f=43&t=5587)
Сейчас в процессе перенос на "человеческий" Верилог (не автогенерация из пикада по библиотечным ячейкам) и прикручивание Wishbone. Думаю еще опционально FIFO на прием добавить, а также приемную передискретизацию и мажоритарку.
Процессор 1801ВМ1, ревизия 1.4a (http://u.zeptobars.ru/yuot/1801/VM1/vm1_rev14a.rar):
- удалены параметры глобальной конфигурации собственно из модуля vm1, теперь конфигурируется через параметры самого модуля (которые устанавливаются при имплементации модуля из глобальных параметров). Это рекомендация ведущих собаководов HDL-стиля OpenCores, позволяет в одном проекте применить модули с разными параметрами, например ВМ1А и ВМ1Г одновременно
- написан параметризованный контроллер прерываний. Прерывания вынесены из периферии и собраны в одном месте, это упрощает модули периферии и позволяет легко модифицировать всю систему - осуществлять управление векторами и приоритетами запросов
- написан UART в стиле 1801ВП1-065, содержит синтезатор опорной частоты для приемопередатчика на основе фазового аккумлятора (это позволяет сэкономить одну PLL), поддерживает скорости обмена до 921600 Бод, длину слова от 5 до 8 бит, а также дополнительную опцию одного стоп-бита (оригинальный 065 всегда посылал два стоп-бита, на приемник эта опция не влияет - всегда принимается половинка первого стоп-бита). Скорость обмена настраивается от 50 до 921600 Бод
- реализован таймер генерации 50Гц на входе прерывания IRQ2
- написан программа простого эхо-тест консоли, использует прерывания приемника и передатчика
Итого - имеется реальная плата DE0, на которой поднята модель ВМ1 и 8КБ памяти, плата подключается по RS-232 к PC (где мы, разумеется, запустим терминалку), работают все таймеры, можно компилировать и запускать разнообразные тесты.
А почему бы не продолжить и не сделать модуль для замены пин-то-пин КР1801ВМ1Г-50 мгц ? Параметры МПИ выставлять перемычками, адаптер шины - на Н530АП2/530АП2 ( в Москве ~ по 60 руб 1 шт. ).
Какая могла бы быть цена такого модуля для россиян ?
Для согласования тормозов МПИ предусмотреть следующие перемычки ( по 3 бита, со значением от 0 до 7 х ) :
1. Предвыставление адреса ( до SYNC ) на МПИ - от 50 до 400 нс с шагом 50 нс
2. Удержание адреса на МПИ ( после SYNC ) - от 50 до 400 нс с шагом 50 нс
3. Предвыставление данных до подачи DOUT - от 50 до 400 нс
4. Удержание данных после снятия DOUT - от 50 до 400 нс
5. Задержка чтения процессором данны с МПИ после прихода RPLY - от 0 до 350 нс
6. Умножение частоты со входа CLC - от 2 до 14 ( ? )
Таймер при этом целесообразно тактировать от натурального входа CLC ( ? ).
Вероятно, могут понадобится еще какие-нибуть настройки таймингов МПИ.
Если мало ног у ПЛИС - поставить ЭКФ1564КП5 3-4 шт, на входы - перемычки.
Спрос на такой модуль будет непременно, по крайней мере 1 шт. :v2_dizzy_mutant:
А почему бы не продолжить и не сделать модуль для замены пин-то-пин КР1801ВМ1Г-50 мгц ?
А чего только 50МГц? Проект с запасом на 100МГц собрался.
Пока новый ретропроект начинать не хочу, надо старые доделать - точные реплики БК/ДВК.
Как вариант - можно взять плату модуля на 2x1801ВМ1 и сделать макетку ВМ-мулятора на ней, оно же в обе стороны работать будет. То есть - вместо процессора на плату модуля запаять цанговые штыри, и втыкать вместо основного процессора, скажем, в плату БК. А сам проект FPGA-процессора разместить в той же DE0.
Но старые платы не дадут большого выигрыша от замены процессора - там память медленная, надо будет еще и кеш внутри размещать. В-общем, посмотрим, но не "прямо щаз", пока есть что доделывать.
Параметры МПИ выставлять перемычками, адаптер шины - на >Н530АП2/530АП2 ( в Москве ~ по 60 руб 1 шт. ).
Да, адаптер МПИ буду делать, нужен для проекта ДВК, пока не решил - будет это заводская печатная плата или просто на макетке спаяю "для себя".
Прикидывал ноги чтобы подключить МПИ в тот же штатный 40-пиновый разъем на платах DE, на 22-битный доступ немножко не хватает, а какой уплотнитель-мост городить не хочется, пока тоже обдумываю.
balu_dark
05.09.2015, 09:27
Но что то мне подсказывает что даже 50 МГц не будет работать в старых машинах из за памяти....
Ведь там везде стоит динамика - средняя скорость доступа от 200 до 500 нс. Неужели будет работать без торможения проца ?
Неужели будет работать без торможения проца ?
Кеширование большей частью решает эту проблему. Современные FPGA имеют блоки памяти на борту, сделать внутри быстрый кеш - не проблема. Причем нормальные такие объемы, например, для БК-001х 32 килобайта кеша на бекашные 128 килобайт общего объема - просто отлично, для записи в видеопамять сделать очередь буферов отложенной записи. Для плат типа 1201.01 и 1201.02 с 56 килобайтами памяти кеш тоже отлично будет работать. Проблема в том что это будет не универсальная прошивка "заменитель ВМ1 на все случаи жизни", а придется под каждый тип платы делать свои модификации. А универсальный "заменитель ВМ1" будет без кеша и заметного выигрыша в скорости на старых платах не получится.
... А универсальный "заменитель ВМ1" будет без кеша и заметного выигрыша в скорости на старых платах не получится.
Получится, примерно в 2-3 раза для МС1201.01 - т.е. по скорости ДОЗУ будет работать фактически. А с ПЗУ - и 3-5 раз ускорение получится.
*
В БК11М кэшъ должен быть довольно сложный - т.к. странички-то ДОЗУ переключаются...
Хотя для БК11М целесообразно сделать комплексный модуль с СОЗУ ( и даже с ПЗУ ) - что бы все мегагерцы работали без простоя, но вот о совместимости с современными демами лучшет в этом случае не заводить разговор...
*
Какова может быть цена модуля для БК11М с полным СОЗУ и ПЗУ - который с видеовыходом БК ?
Какова может быть цена модуля для БК11М с полным СОЗУ и ПЗУ - который с видеовыходом БК ?
Это у Voland-а с его Бустером спросить нужно, практически это оно и есть, если с полным СОЗУ, ПЗУ и видеовыходом.
Сейчас я бы сделал крошечный модуль в формате микросхемы процессора (как примерно с РЕ-мулятором получилось) на новой MAX10C4 или MAX10C8 (там и флешка внутри есть для ПЗУ), с буферами на 74HCT для согласования. Себестоимость платы и детали - ~$25-30.
на новой MAX10C4 или MAX10C8
Гугл почему-то не знает таких микросхем)
Гугл почему-то не знает таких микросхем)
Так это же Гугль, он вечно к точности формулировок придирается. :) Любители же ретро на Альтерах знают (https://www.altera.com/products/fpga/max-series/max-10/features.html) о чем речь и так :)
... с буферами на 74HCT для согласования. Себестоимость платы и детали - ~$25-30.
Порыл на сайте Альтеры - или там хакеры завелись, или мышление у создателей сайта чрезмерно альтернативное, но ответ для обыкновенного вопроса " максимальное входное напряжение на сигнальных линиях ?"
не нашел, даже даташит не грузится - вываливается в ошибку IE.
Терпит ли Альтера +5.5 в. на сигнальных входах-выходах ?
Ведь питание у неё +3.3 в...
balu_dark
06.09.2015, 20:42
значится так - теперь фишку работы с +5в называют 5V tolerant. Ищите такие словеса в даташите. Но насколько помню только старые CPLD и очень старые FPGA поддерживали 5в из каробки. Остальное - надо согласовывать.
Если этого нет - на сайте Альтеры был какой то общий даташит для любых ее микросхем по согласованию с 5 вольтами. В простейшем случае - это токо ограничивающий резистор в цепи.
Терпит ли Альтера +5.5 в. на сигнальных входах-выходах ?
Ведь питание у неё +3.3 в...
Смотря какое семейство. Из FPGA последнее, которое "терпит" 5 вольт на входах (как было точно замечено обзывается эта способность 5V-tolerant), было семейство Acex 1K, сняты с массового производства лет 10 назад. Из CPLD - последнее 5V-совместимое семейство MAX3000. Возможности у всех этих микросхем скромные, ВМ1 влезет разве что в старшие Acex-ы (50-ку и 100-ку), будет дорого и здоровый корпус самой микросхемы.
Из новых - Циклоны 3, 4, 5 даже 3.3V на входах держат плоховато, особенно если на плате цепи длинные и сигналы быстрые - возникают выбросы, превышающие напряжение питания (overshoot) и меньше "земли" (undershoot), срабатывают внутренние защитные диоды. При этом даташитом производителя нормируется некая средняя мощность таких выбросов. И на практике - входы, бывает, "подгорают".
В этом свете не совсем ясно, почему использовать для буфетизации ( реплики 1801ВМ1Г ) именно 74HCT ( 245 ? ) ? Они вроде как с "закрытым коллектором", тем более КМОП - а это значит поддадут на выход все, что есть на питании ( +5в. минимум ).
Никак нельзя применить именно Н530АП2 ?
Есть, конечно, 8-битные N74F641D (приемопередатчики с Ок), но они едут из Китая 50 дней !
http://storage7.static.itmages.ru/i/15/0906/h_1441581692_4023211_67e7896e26.jpg (http://itmages.ru/image/view/2970895/67e7896e)
И цена ! ( И знак какой-то незнакомый ... )
balu_dark
07.09.2015, 07:11
А зачем вам открытый коллектор ? чтобы заведомо убить скорость? Если нужно использовать захват шины - так для этого есть перевод в Z состояние буфера.
CodeMaster
07.09.2015, 08:18
но они едут из Китая 50 дней
Придедут когда-то же.
И знак какой-то незнакомый
Philips
В этом свете не совсем ясно, почему использовать для буфетизации ( реплики 1801ВМ1Г ) именно 74HCT ( 245 ? )
...
Никак нельзя применить именно Н530АП2 ?
Серия HCT - это ТТЛ-совместимый по входу КМОП. То есть - входное напряжение высокого уровня у него начинается с 2.4 вольта, в отличие от обычного КМОП серии 74HC, где для гарантированного высокого логического уровня надо подавать 0.8*Vcc (то есть - 4 вольта при 5-вольтовом питании). Впрочем НСТ тоже не пойдет, тут надо LVC при питании 3.3 вольта - они переживают входное напряжение, превышающее напряжение питания.
Мы заменяем процессор, а не всю плату типа МС1201.01. Процессор же напрямую в МПИ не втыкается? Поэтому достаточно соответствовать его хиленьким выходным втекающим 3.2ма при "0" и -200мкА при "1". Ну и по входному току - не более 10мкА, по этому параметру никакие ТТЛ-серии тут не подойдут.
Впрочем, ничто не может помешать желающим впаять на плату 74А245MTC (https://www.fairchildsemi.com/datasheets/74/74F245.pdf) вместо 74HCT245, особенно если сделать схему портом Б наружу :)
А чтобы в корзинку ДВК сразу втыкать - это уже другое изделие, там и ног побольше чем у ВМ1 надо, и буфера надо, и отключаемые терминаторы (глупо только процессор такой платой имитировать, можно же все что угодно - контроллер дисковода, КЦГД, КМД, КЖД и прочее).
Сошлифовался КМ1801ВМ2:
КМ1801ВМ2, диффузия, полное разрешение (487 MB) (http://u.zeptobars.ru/yuot/1801/KM1801VM2-DIF-HD.jpg)
КМ1801ВМ2, диффузия, низкое разрешение (68 MB) (http://u.zeptobars.ru/yuot/1801/KM1801VM2-DIF-FD.jpg)
Переписал маловменяемый автосгенерированный ВП1-037 на "человеческом" верилоге:
//
// Copyright (c) 2013 by 1801BM1@gmail.com
//__________________________________________________ ____________________________
//
`timescale 1ns / 100ps
module va_037
(
input PIN_CLK, //
input PIN_R, //
input PIN_C, //
//
inout[15:0] PIN_nAD, // Address/Data inverted bus
input PIN_nSYNC, //
input PIN_nDIN, //
input PIN_nDOUT, //
input PIN_nWTBT, //
output PIN_nRPLY, //
//
output[6:0] PIN_A, //
output[1:0] PIN_nCAS, //
output PIN_nRAS, //
output PIN_nWE, //
//
output PIN_nE, //
output PIN_nBS, //
output PIN_WTI, //
output PIN_WTD, //
output PIN_nVSYNC //
);
//__________________________________________________ ____________________________
//
reg nWTBT; // BYTE flag
reg [15:0] A; // Q-bus address latch
reg [7:0] RA; // 177664 register start address
reg M256; // 1/4 screen mode
//
wire RWR, ROE; // 177664 register strobes
//
wire RESET; // external reset
wire CLKIN; // external clock (pixel/2)
//
reg [2:0] PC; // pixel counter
reg [13:1] VA; // video address counter
reg [7:0] LC; // line counter
//
wire ALOAD, TV51; // base address load strobe
reg HGATE, VGATE; //
reg PC90; //
reg RASEL, AMUXSEL;//
wire CAS; //
reg CAS322; //
wire FREEZ; //
//
reg VTSYN; //
wire SYNC2, SYNC5; //
//
wire RPLY; //
reg TRPLY; //
//
//__________________________________________________ ____________________________
//
assign PIN_nRPLY = ~(ROE | RWR | RPLY);
assign PIN_nAD[7:0] = ~ROE ? 8'hZZ : ~RA[7:0]; // read 177664 register back
assign PIN_nAD[8] = 1'bZ; //
assign PIN_nAD[9] = ~ROE ? 1'bZ : ~M256; //
assign PIN_nAD[14:10] = 5'hZZ; //
assign PIN_nAD[15] = (~PIN_nDIN & ~PIN_nSYNC & // start address vector 177716
(A[15:1] == (16'o177716 >> 1))) ? 1'b0 : 1'bZ;
assign PIN_nE = PIN_nSYNC | PIN_nDIN | (A[15:7] != (16'o177600 >> 7));
assign PIN_nBS = ~(A[15:2] == (16'o177660 >> 2));
always @(*) if (PIN_nDOUT) nWTBT <= PIN_nWTBT;
assign RWR = ~PIN_nSYNC & ~PIN_nDOUT & (A[15:1] == (16'o177664 >> 1));
assign ROE = ~PIN_nSYNC & ~PIN_nDIN & (A[15:1] == (16'o177664 >> 1));
assign RESET = PIN_R;
assign CLKIN = PIN_CLK;
always @(*) if (PIN_nSYNC) A[15:0] = ~PIN_nAD[15:0];
always @(*) if (RWR) RA[7:0] = ~PIN_nAD[7:0];
always @(*) if (RWR) M256 = ~PIN_nAD[9];
always @(*) if (RASEL) TRPLY = 1'b1; else if (PIN_nDIN & PIN_nDOUT) TRPLY = 1'b0;
assign RPLY = TRPLY & ~RASEL;
//__________________________________________________ ____________________________
//
// Pixel counter (within word)
//
always @(negedge CLKIN) if (~PC[0]) PC90 <= PC[1];
always @(negedge CLKIN or posedge RESET)
begin
if (RESET)
PC[2:0] <= 3'b000;
else
PC[2:0] <= PC[2:0] + 3'b001;
end
//
// Word counter (within line)
//
always @(negedge CLKIN or posedge RESET)
begin
if (RESET)
VA[5:1] <= 5'b00000;
else
if (&PC[2:0])
begin
VA[4:1] <= VA[4:1] + 4'b0001;
if (&VA[4:1] & ~HGATE) VA[5] <= ~VA[5];
end
end
//
// Test clock assignment
//
assign TV51 = PIN_C ? 1'b0 : ~(&PC[2:0] & &VA[5:1]);
//
// RWR at active RESET is added for simulation and debug purposes
//
assign ALOAD = (FREEZ & ~LC[1]) | (RESET & RWR);
//
// Loadable part of video address counter
//
always @(negedge CLKIN)
begin
if (ALOAD)
VA[13:6] <= RA[7:0];
else
if (~TV51 & ~FREEZ)
VA[13:6] <= VA[13:6] + 8'b00000001;
end
always @(negedge CLKIN or posedge RESET)
begin
if (RESET)
begin
HGATE <= 1'b0;
VGATE <= 1'b1;
end
else
begin
if (&PC[2:0] & &VA[4:1] & (HGATE | VA[5]))
HGATE <= ~HGATE;
if (~TV51 & (LC[5:0] == 6'b000000) & (VGATE | (LC[7:6] == 2'b00)))
VGATE <= ~VGATE;
end
end
//
// Line counter
//
always @(negedge CLKIN or posedge RESET)
begin
if (RESET)
LC[7:0] <= 8'b11111111;
else
if (~TV51)
begin
LC[5:0] <= LC[5:0] - 6'b000001;
if ((LC[5:0] == 6'b000000) & ~VGATE) LC[6] <= ~LC[6];
if ( LC[6:0] == 7'b0000000) LC[7] <= ~LC[7];
end
end
assign FREEZ = VGATE & (LC[5:2] == 4'b1010);
//__________________________________________________ ____________________________
//
// DRAM address mux and controls
//
assign PIN_A[6:0] = RASEL ? (AMUXSEL ? ~A[7:1] : ~A[14:8])
: (AMUXSEL ? ~VA[7:1] : {1'b0, ~VA[13:8]});
assign PIN_nCAS[0] = ~(CAS & (~A[0] | ~PC[2] | nWTBT));
assign PIN_nCAS[1] = ~(CAS & ( A[0] | ~PC[2] | nWTBT));
assign PIN_nRAS = PC90 | (PC[2] & ~RASEL);
assign PIN_WTI = ~VGATE & ~HGATE & (M256 | (LC[6] & LC[7])) & PC90 & ~PC[2] & PC[1];
assign PIN_WTD = RPLY & ~PIN_nDIN;
assign PIN_nWE = ~(RASEL & ~PIN_nDOUT);
//
// Converted to synchronous flip-flop without asynch reset, generates the same waveform
//
// always @(posedge CLKIN or posedge (PC[1] & AMUXSEL))
// begin
// if (PC[1] & AMUXSEL)
// RASEL <= 1'b0;
// else
// if (PC90 & ~PC[1] & PC[2])
// RASEL <= ~(PIN_nSYNC | A[15] | RPLY | (PIN_nDIN & PIN_nDOUT));
// end
//
always @(posedge CLKIN) AMUXSEL <= PC90;
always @(posedge CLKIN)
begin
if (PC90 & PC[1])
RASEL <= 1'b0;
else
if (PC90 & ~PC[1] & PC[2])
RASEL <= ~(PIN_nSYNC | A[15] | RPLY | (PIN_nDIN & PIN_nDOUT));
end
always @(negedge CLKIN) CAS322 <= RASEL;
assign CAS = (CAS322 & PC[1] & PC[2]) | (~VGATE & ~HGATE & PC[1] & ~PC[2]);
//__________________________________________________ ____________________________
//
// Video synchronization
//
always @(negedge CLKIN or posedge RESET)
begin
if (RESET)
VTSYN <= 1'b0;
else
begin
if ((VA[3:1] == 3'b000) & (PC[1:0] == 2'b11))
VTSYN <= 1'b1;
if ((VA[4:1] == 4'b0100) & (PC[2:0] == 3'b111))
VTSYN <= 1'b0;
end
end
//
// Latch converted to clock synchronous flip-flop, generates the same waveform
//
// assign VTSET = (VA[3:1] == 3'b000) & PC[2];
// assign VTCLR = (VA[4:1] == 4'b0101);
//
// always @(*)
// begin
// if (VTCLR) VTSYN = 1'b0;
// if (VTSET) VTSYN = 1'b1;
// end
//
assign SYNC2 = ~(VGATE & (LC[5:2] == 4'b1010) & ~(LC[0] & LC[1]));
assign SYNC5 = VTSYN | (SYNC2 & ~HGATE);
assign PIN_nVSYNC = ~(SYNC2 ^ SYNC5);
//__________________________________________________ ____________________________
//
endmodule
По размеру усохло раза в три, и теперь можно хоть осмысленно в текст вникать.
Update:
- исправил опечатку (спасибо anasana) в комментарии и переписал CAS в более внятном виде
- исправлен декодер адреса регистра 177664 (спасибо gid)
- исправлена ошибка с флагом nWTBT
Заметил копипасту в коде.
При формировании сигналов RWR и ROE проверка адреса должна быть
(A[15:1] == (16'o177664 >> 1))
А в чём преимущество такого alwaysа
always @(*) if (PIN_nSYNC) A[15:0] = ~PIN_nAD[15:0];
перед таким:
always @(posedge PIN_nSYNC) A[15:0] <= ~PIN_nAD[15:0];
Можно ли вот как я сейчас, произвольно менять блокирующее присваивание на неблокирующее? Или есть какие-то хитрости, когда и где использовать определённые виды присваиваний?
И почему
always @(negedge PIN_nDOUT) nWTBT <= PIN_nWTBT;записан так, а не так:
always @(*) if (~PIN_nDOUT) nWTBT <= PIN_nWTBT;, как вроде бы подобные alwaysы ниже, это дело вкуса или есть какие-то тонкости в способах написания?
Заметил копипасту в коде.
При формировании сигналов RWR и ROE проверка адреса должна быть
(A[15:1] == (16'o177664 >> 1))
Да, спасибо, упустил я A1.
А в чём преимущество такого alwaysа
always @(*) if (PIN_nSYNC) A[15:0] = ~PIN_nAD[15:0];
перед таким:
always @(posedge PIN_nSYNC) A[15:0] <= ~PIN_nAD[15:0];
Тут не преимущество, тут есть разница - первый пример это латч (типа ИР22), а второй пример - флип-флоп (типа ИР23). Как и в обычных схемах они часто взаимоменяемы, если во втором примере заменить posedge на negedge, то тоже будет работать. Времянка на выходе будет немножно другая, но для данной модели это непринципально.
Можно ли вот как я сейчас, произвольно менять блокирующее присваивание на неблокирующее? Или есть какие-то хитрости, когда и где использовать определённые виды присваиваний?
В данном тексте - обычно можно.
Блокирующие присваивания (=) они выполняются как бы немедленно по тексту. А неблокирующие (<=) - откладываются и как бы накапливаются, переменная в левой части не получает присвоенное значение немедленно, собственно накопленные присвоения выполняются все сразу в конце блока always.
Вот тут (http://svo.2.staticpublic.s3-website-us-east-1.amazonaws.com/verilog/assignments/) более-менее внятно описано.
И почему
always @(negedge PIN_nDOUT) nWTBT <= PIN_nWTBT;записан так, а не так:
always @(*) if (~PIN_nDOUT) nWTBT <= PIN_nWTBT;, как вроде бы подобные alwaysы ниже, это дело вкуса или есть какие-то тонкости в способах написания?
Тут нет никакого сакрального смысла, меня просто квартус раздражал предупреждениями о латчах, я решил их немного подсократить. Но зато появились предупреждения о левых тактовых сигналах. Поскольку непосредственно этот исходник для синтеза в ПЛИС не предназначается, то предупреждения не имеют значения.
Кстати, получается что именно в этом месте я внес ошибку - nWTBT сохраняет свое значение после последнего среза PIN_nDOUT и может влиять на последующий циклы чтения (где среза PIN_nDOUT нет), запрещая один из CAS-ов. Так что правильно будет так:
always @(*) if (PIN_nDOUT) nWTBT <= PIN_nWTBT;
Исходник в предыдущем посте подправил. Оно, конечно, моделировалось, но покрытие далеко не полное.
В случае с флип-флоп разница между присваиваниями очевидна
module demo
( input CLK,
input d_in,
output pin_A, pin_B, pin_C,
output pin_D, pin_E, pin_F
);
reg A,B,C;
reg D,E,F;
assign pin_A = A, pin_B = B, pin_C = C;
assign pin_D = D, pin_E = E, pin_F = F;
always @(negedge CLK )
begin
A <= d_in;
B <= A;
C <= B;
end
always @(negedge CLK )
begin
D = d_in;
E = D;
F = E;
end
endmodule
http://storage9.static.itmages.ru/i/15/0917/s_1442517696_8248389_c8a4c95522.png (http://itmages.ru/image/view/3007322/c8a4c955)
С латчами генерируется та-же схема, а вот разницы в работе не будет(с оговоркой про задержки)
module demo2
( input CLK,
input d_in,
output pin_A, pin_B, pin_C,
output pin_D, pin_E, pin_F
);
reg A,B,C;
reg D,E,F;
assign pin_A = A, pin_B = B, pin_C = C;
assign pin_D = D, pin_E = E, pin_F = F;
always @(*)
begin
if (CLK)
begin A <= d_in;
B <= A;
C <= B;
end
end
always @(*)
begin
if (CLK)
begin D = d_in;
E = D;
F = E;
end
end
endmodule
http://storage9.static.itmages.ru/i/15/0917/s_1442517696_6465301_d244da2f8b.png (http://itmages.ru/image/view/3007321/d244da2f)
Будем надеяться ВМ2 не "выбежит" за 20К транзисторов:
http://s017.radikal.ru/i429/1509/b5/82a88c603bbbt.jpg (http://s017.radikal.ru/i429/1509/b5/82a88c603bbb.gif)
Сейчас 10K, еще осталась примерно половина площади ядра.
Update: картинка кликабельна, можно дорожки рассмотреть (в гифке все)
Будем надеяться ВМ2 не "выбежит" за 20К транзисторов
А если выбежит, то что?
А если выбежит, то что?
То работающий Верилог будет в 2017-ом :)
То работающий Верилог будет в 2017-ом :)
Как эти события коррелируются?)
Как эти события коррелируются?)
Время реверса является степенной функцией от числа транзисторов :)
Время реверса является степенной функцией от числа транзисторов :)
А че ж не линейной???
А че ж не линейной???
Потому что число связей растет нелинейно. Слабо, но тем не менее. И сами связи становятся разветвленней и сложнее - больше времени на редактирование.
http://s020.radikal.ru/i709/1510/68/3eb8837a145et.jpg (http://s020.radikal.ru/i709/1510/68/3eb8837a145e.gif)
(картинка кликабельна, гифка около 8 мегабайт)
Всего примерно 18500 транзисторов. Документация на ВМ2 говорит о 120К интегральных элементов. ВМ1 содержит 16800 транзисторов и заявлено всего 50К элементов. По ходу элементы размножаются :), можно было ожидать в ВМ2 30К тразисторов, но их нет. Нормы ВМ2 примерно 4 мкм против 5 мкм у ВМ1, кристалл по размеру примерно такой же. В-общем, можно считать ВМ2 "работой над ошибками в ВМ1", архитектурно и технологически оно несильно отличается.
Всего примерно 18500 транзисторов. Документация на ВМ2 говорит о 120К интегральных элементов. ВМ1 содержит 16800 транзисторов и заявлено всего 50К элементов. По ходу элементы размножаются :), можно было ожидать в ВМ2 30К тразисторов, но их нет. Нормы ВМ2 примерно 4 мкм против 5 мкм у ВМ1, кристалл по размеру примерно такой же. В-общем, можно считать ВМ2 "работой над ошибками в ВМ1", архитектурно и технологически оно несильно отличается.
Думается, что вы гораздо легче разберетесь с ВМ2, нежели с ВМ1, ибо есть уже опыт с ВМ1, а так же полнейшая дока на ВМ2.
А мы, программисты, воспользуемся вашими исследованиями, чтобы наконец сделать точный эмуль ВМ2. Разумеется, нужна логическая схема, а не верилог.
NovaStorm
05.10.2015, 13:08
Vslav, а можно на картинке в рамочках написать, где какие блоки находятся?
Вот например два поля слева-вверху, что-то из этого ПЗУ микрокода?
Vslav, а можно на картинке в рамочках написать, где какие блоки находятся?
Вот например два поля слева-вверху, что-то из этого ПЗУ микрокода?
Вот такая картинка есть из ТО на ВМ2.
Насколько соответствует -- не скажу.
Sergei Frolov
05.10.2015, 13:22
А сколько примерно транзисторов в К1801ВМ4? Википедия говорит, что в районе 50к.
И ещё одна картинка из ТО.
До кучи, нашёл ещё вот такие снимки:
https://commons.wikimedia.org/wiki/File:Soviet_K1801VM1_die.JPG
https://commons.wikimedia.org/wiki/File:Soviet_KM1801VM2_die.JPG
https://commons.wikimedia.org/wiki/File:Soviet_KM1801VM3_die.JPG
https://commons.wikimedia.org/wiki/File:Soviet_N1806VM2_die.JPG
И общая галерея Pauli Rautakorpi:
https://commons.wikimedia.org/wiki/User:Birdman86
Vslav, а можно на картинке в рамочках написать, где какие блоки находятся?
Угу, я по ВМ1 такую картинку рисую, по ВМ2, соответственно, тоже будет, по окончанию реверса.
Вот например два поля слева-вверху, что-то из этого ПЗУ микрокода?
Из ТО на ВМ2 картинка правильная, только моя фотография перевернута на 180 градусов. Видимо, правильная ориентация фотографии - по надписи "1983", но она на реверс не влияет и я ее смело игнорирую :).
На моем фото два поля слева вверху - верхнее поле это products - логические произведения (and), нижнее поле - это SOP-ы - sum-of-products - суммы (or) логических произведений (выходов верхней части). Выходы нижней части подаются с программируемой полярностью (то есть еще можно опционально задавать инверсию выхода) на защелки, вместе все это образует ПЛМ, со структурой, аналогичной PAL/GAL. Только оно масочное (не программируемое пользователем) и разрядность очень большая.
Справа вверху на фотографии тоже ПЛМ-ка, только поменьше - это предварительное декодирование кода инструкции. Слева примерно чуть ниже середины можно найти ПЛМ приоритетного шифратора прерываний. Нижнюю половину фотографии занимает операционный блок (АЛУ/блок регистров). Самый ужас - схемы управления, они нерегулярные, долго и занудно их реверсить - на серединке фотографии.
А сколько примерно транзисторов в К1801ВМ4? Википедия говорит, что в районе 50к.
Википедии в этом вопросе особой веры нет - туда перекочевали цифры данные изготовителем по мифическим интегральным элементам, а как показали ВМ1 и ВМ2 - элементы за транзисторы считать нельзя, и коэффициента пересчета постоянного тоже нет. Надо открывать и смотреть на сам кристалл.
Vslav, у вас же это описание есть ?
http://zx-pk.ru/showthread.php?t=17284
To All: Выложите файлики еще раз пожалуйста ?
To All: Выложите файлики еще раз пожалуйста ?
Так вот же: http://www.felixl.com/1801VM2SPECS.zip
Vslav, у вас же это описание есть ?
http://zx-pk.ru/showthread.php?t=17284
Да, есть. Помогло с реверсом ВМ1, архитектура практически такая же.
To All: Выложите файлики еще раз пожалуйста ?
http://archive.pdp-11.org.ru/BIBLIOTEKA/1801VM2SPECS/
Задокументировал выполнение чтения процессором 1801ВМ1. Ниже приведена диаграмма выполнения микропроцессором цикла чтения данных по шине МПИ в качестве ведущего устройства. Пунктирными линиями показано состояние высокого импеданса. Обычно в системе на линиях магистрали имеются подтягивающие резисторы, поэтому можно полагать что в эти моменты времени присутствует напряжение высокого уровня, все управляющие сигналы при этом принимают неактивное значение, на шине адрес/данных устанавливается нулевое значения, поскольку шина инвертирована.
Также указана привязка установки и снятия управляющих выходов к фронтам тактового сигнала, при этом не учитывается внутренняя задержка, которая может достигать десятков наносекунд и, при высоком значении тактовой частоты, может быть сравнимой с длительностью такта.
http://s017.radikal.ru/i438/1510/bc/746181eabf6a.gif (http://radikal.ru/big/b0128803af6a4007b63bd6fcc7eee65c)
В такте Т0 на магистрали МПИ нет ведущего устройства, обмен не выполняется, полагаем это состояние исходным. По срезу Т1 микропроцессор осуществляет захват магистрали (процесс арбитража будет рассмотрен позже), при этом разрешается управление выходами nWTBT, nDIN, nDOUT, nIAKO, A/D. Сигнал nBSY принимает активный низкий уровень, на шине адреса/данных устанавливается адрес читаемых данных. Выход сигнала nSYNC остается запрещенным - это особенность К1801ВМ1, для корректной работы требуется внешний подтягивающий резистор. На выходе nWTBT установлен высокий уровень, что означает выполнение операции чтения. По срезу Т2 на выходе nSYNC формируется активный низкий уровень. По фронту Т2 выставленный адрес снимается с шины и выводы переводятся в высокоимпедансное состояние, также выставляется активный низкий уровень на линии nDIN, далее микропроцессор переходит в состояние ожидания данных и сигнала подтверждения nRPLY от ведомого устройства. При этом запускается специальный таймер монитора шины, который осуществляет счет с частотой CLC/8. Если сигнал подтверждения nRPLY не будет получен в течение 56..64 тактов CLC, то цикл обмена прерывается и возникает программное исключение по вектору 048. Разброс в величине таймаута связан с тем что фаза предделителя частоты CLC/8 при запуске таймера не обнуляется - предделитель считает непрерывно.
На диаграмме в такте Т4 внешнее устройство выставляет данные, которые подлежат считыванию микропроцессором, и затем, по прошествии некоторого интервала формирует активный низкий уровень сигнала подтверждения nRPLY. В общем же случае, согласно ГОСТ 26765.51-86, актуальные данные должны быть выставлены ведомым устройством не позднее чем 200 нс после среза активации сигнала nRPLY. На диаграмме момент выставления nRPLY, который определяется внешним устройством, намерено показан после фронта T4. Микропроцессор фиксирует сигнал на своем входе nRPLY по фронту тактового сигнала, при обнаружении активного низкого nRPLY два фронта тактового сигнала подряд (на приведенной диаграмме это T5 и T6, два цикла нужны так как данные от ведомого устройства могут запаздывать на интервал до 200 нс относительно активации nRPLY), цикл обмена полагается законченным, по фронту T6 фиксируются читаемые данные и передаются внутренним схемам процессора, снимается активный сигнал nDIN и останавливается таймер монитора шины, затем происходит переход в ожидание снятия активного сигнала nRPLY. При этом по срезу тактового сигнала осуществляется детектирование неактивного высокого уровня nRPLY и при обнаружении такового запрещаются выходы управляющих сигналов nDIN, nDOUT, nWTBT. Далее, еще через один такт происходит снятие сигнала nBSY и выставление высокого уровня сигнала nSYNC. На диаграмме фронт сигнала nRPLY обнаруживается по срезу Т7, а снятие сигналов nSYNC и nBSY происходит по срезу Т8. Высокий неактивный уровень nSYNC удерживается в течение одного такта и по срезу Т9 выход переходит в высокоимпедансное состояние, микропроцессор перестает выполнять роль ведущего устройства, магистраль полагается незанятой. Следует отметить, что таймер монитора шины в состоянии ожидания деактивации nRPLY не работает, поэтому имеется теоретическая возможность зависания системы.
Микропроцессор К1801ВМ1 чтение данных всегда выполняет 16-битными словами. Если требуется только один из байтов, то ненужные разряды игнорируются. Также микропроцессор выставляет адрес "как есть", и чтение слов по нечетному адресу может приводить к некорректно прочитанным данным, поскольку внешняя память и устройства обычно не поддерживают такой режим.
В такте Т4 внешнее устройство выставляет данные, которые подлежат считыванию микропроцессором, и затем, по прошествии некоторого интервала формирует активный низкий уровень сигнала подтверждения nRPLY. На диаграмме момент выставления nRPLY, который определяется внешним устройством, намерено показан после фронта T4.
В общем случае это неправильно. Стандарт Q-Bus допускает выставление сигнала RPLY максимум за 200 нс до выставления данных:
http://pic.pdp-11.ru/images/dati.jpg
Стандарт МПИ в точности копирует это требование:
http://pic.pdp-11.ru/images/datimpi.jpg
Именно поэтому перед считыванием данных выполняется такая большая задержка.
Кстати, тестирование показало, что даже сам 1801ВМ1 при чтении его регистров выставляет RPLY раньше данных:
http://s1.hostingkartinok.com/uploads/images/2012/10/115368d71e9929414aa7c50c3ab6e587.png
В общем случае это неправильно. Стандарт Q-Bus допускает выставление сигнала RPLY максимум за 200 нс до выставления данных
Да, спасибо за уточнение, внесу его.
Пост выше является копипастой из составляемого сейчас документа, там есть фраза что работа внешних устройств регламентируется ГОСТ-ом , а документ сосредоточен на особенностях процессора. Поэтому акцента когда и чего делает ведомое устройство тут нет.
документ сосредоточен на особенностях процессораНо всё же может быть интересно уточнить тайминги выставления сигнала RPLY процессором 1801ВМ1 при обращении по шине МПИ к его регистрам - это ведь тоже особенность процессора.
---------- Post added at 12:32 ---------- Previous post was at 12:27 ----------
В документе не грех и уточнить, что значительная задержка между приёмом RPLY и считыванием данных выполняется процессором 1801ВМ1 не потому, что так захотели разработчики, а потому, что только так можно соблюсти стандарт, предусматривающий возможность выставления RPLY внешним устройством задолго ДО выставления данных.
---------- Post added at 12:41 ---------- Previous post was at 12:32 ----------
Ещё один интересный момент, относящийся к особенностям процессора 1801ВМ1 - содержащееся в ТУ ( по утверждению разработчиков ) обязательное требование выравнивания сигнала RPLY на входе процессора по фронту такта CLC при помощи внешней обвязки. С учётом этого требования изображать сигнал RPLY на входе процессора лучше всего на фронте следующего такта CLC после выставления данных внешним устройством.
Но всё же может быть интересно уточнить тайминги выставления сигнала RPLY процессором 1801ВМ1 при обращении по шине МПИ к его регистрам - это ведь тоже особенность процессора.
Да, видимо надо будет уточнить. Вообще документацию писать очень занудно, деталей очень много, приходится существенную часть несущественных :) подробностей отсекать, и нет общей цели подменить этой докой, например, тот же ГОСТ. Хотя сейчас я думаю что картинки из ГОСТ-а и описания QBUS было бы неплохо рядом вставить.
разработчиков ) обязательное требование выравнивания сигнала RPLY на входе процессора по фронту такта CLC при помощи внешней обвязки.
Я детектор этот анализировал долго, в-общем, не совсем понятно что там конкретно происходит, есть сильное подозрение на метастабильность триггера, который защелкивает RPLY по тактовому фронту. Также есть вероятность что глюк в процессоре от августа 1991-го, который мы сфотографировали, уже устранен, поэтому зря искали :). А в целом схема будет надежно работать если RPLY будет стабильным как на фронте так и на срезе тактовой. Поэтому синхронизировать можно любым фронтом, я встречал оба варианта в практических схемах на ВМ1. Внешнее защелкивание RPLY по срезу тактовой даст несколько большее быстродействие.
Также есть вероятность что глюк в процессоре от августа 1991-го, который мы сфотографировали, уже устранен, поэтому зря искали :)
А что за глюк такой?
А что за глюк такой?
Достаточно широкоизвестный, если RPLY поступает на процессор не синхронизированный с тактовой частотой (вероятно, изменяющийся в некоторый момент перед наступлением тактового события, синхронизация устраняет такое изменение - оно начинает происходить в предсказуемый момент), то происходит аппаратное зависание интерфейсного блока ВМ1. Об этом еще разработчики писали, им нужно было запустить плату с процессором 1801ВМ1 к какой-то показухе, а оно подвисало периодически, до утра ковырялись, устранили, а потом уже не следующий день нашли проблему в самом процессоре. Кстати, входы прерываний тоже требуют синхронизациии, а то возможны проблемы.
В-общем, добавил в документ еще диаграммки из ГОСТа, нагляднее стало, понятнее почему оно в процессоре сделано именно таким образом. А то я сокрушался что SYNC так поздно снимается, в PCI-то FRAME снимается достаточно рано :)
Значит были процы без этого глюка? Как их отличить?
Значит были процы без этого глюка? Как их отличить?
Никто не знает были или нет, я просто предположил что глюк пофиксили, раз явно его не видно. Посмотрел по топологии куда идет вход RPLY и попытался сообразить к чему приведет его изменение в "неподходящий" момент. На логическом уровне ни до чего такого фатального не додумался, возможно дело в физических характеристиках транзисторов, скорости у них небольшие, время метастабильности триггера в 100+ нс вполне может иметь заметную вероятность.
Как проверить ВМ1 на БК0010 на отсуствие глюка по RPLY - надо выв. 39 ВМ1 отрезать от его цепей и присобачить на линию RPLY от БИС платы - если в течении часа на Т4 ( тест непрерывный из МСТД ) не повиснет - значит, процессор хороший.
*
А что за глюк в ВМ1 - если сигнал RPLY приходит через менее чем 200 нс от сигнала DIN, то процессор виснет. Встречал такое на некотрых экз. процессора - но не во всех, единичные экз. отлично работали.
( Процессоры были Ангстремовские, а не самодельные, как некотрые подумали ).
Наконец-то получилась рабочая модель процессора ВМ1 сконвертированная из верилога в си.Попытка подключить Verilog-CPP модель 1801ВМ1 в эмулятор ДВК выявила различия в поведении с абстрактной моделью при чтении регистра SEL1 ( в обоих случаях в SEL1 находится значение 000000 ).
Абстрактная модель:
-------------------------------------------------
C AD B S D D W R B I I D D S I H E A D I S S
L S Y I O T P S R A M M A N A V C C R L L
C Y N N U B L 7 Q K R G C I L N L L 3 1 2
-------------------------------------------------
1 177716 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 177716 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
1 177716 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 177716 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
1 000000 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 000000 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
1 000000 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 000000 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
1 000000 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
0 000000 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0
1 000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
1 000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 000000 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
Модель Verilog-CPP ( далее V-модель ) :
clk ad bsy wtbt sync din dout rply init sel1
000019 177716 1 0 0 0 0 0 0 0
000020 177716 1 0 0 0 0 0 0 0
000020 177716 1 0 1 0 0 0 0 0
000021 177716 1 0 1 1 0 0 0 0
000021 000000 1 0 1 1 0 1 0 0
000022 000000 1 0 1 1 0 1 0 0
000022 000200 1 0 1 1 0 1 0 0
000023 000200 1 0 1 1 0 1 0 0
000023 000200 1 0 1 1 0 1 0 0
000024 000200 1 0 1 0 0 1 0 0
000024 000200 1 0 1 0 0 0 0 0
000025 000000 1 0 1 0 0 0 0 0
000025 000000 1 0 1 0 0 0 0 0
000026 000000 1 0 1 0 0 0 0 0
000026 000000 0 0 0 0 0 0 0 0
000027 000000 0 0 0 0 0 0 0 0
000027 000000 0 0 0 0 0 0 0 0
000028 000000 0 0 0 0 0 0 0 0
000028 000000 0 0 0 0 0 0 0 0
Отсюда вопросы:
1. Может ли ( и должен ли ) реальный процессор 1801ВМ1 считывать значение по адресу 177716 без ожидания и без анализа сигнала RPLY ?
2. Почему V-модель не выставляет SEL1 ( или не выводит в лог выставление SEL1 ) при чтении адреса 177716 ?
Разобрался почему V-модель не выставляет SEL1. Из-за оптимизации кода для однопроходного вычисления возникла ошибка - использование значения wire.sel177x до его вычисления. Для исправления ошибки нужно перенести вычиcление wire.sel177x в единственное место в коде, где это значение используется:
if( !m_pINOUTPins->pin_sync_in )
{
wire.sel177x = ((m_pINOUTPins->pin_ad_in & 0177700) == 0177700) && (((m_pINOUTPins->pin_ad_in >> 4) & 3) == (m_pINOUTPins->pin_pa & 3));
reg.sel_xx = wire.sel177x;
reg.sel_00 = wire.sel177x && (((m_pINOUTPins->pin_ad_in & 0xf) >> 1) == 0);
reg.sel_02 = wire.sel177x && (((m_pINOUTPins->pin_ad_in & 0xf) >> 1) == 1);
reg.sel_04 = wire.sel177x && (((m_pINOUTPins->pin_ad_in & 0xf) >> 1) == 2);
reg.sel_06 = wire.sel177x && (((m_pINOUTPins->pin_ad_in & 0xf) >> 1) == 3);
reg.sel_10 = wire.sel177x && (((m_pINOUTPins->pin_ad_in & 0xf) >> 1) == 4);
reg.sel_12 = wire.sel177x && (((m_pINOUTPins->pin_ad_in & 0xf) >> 1) == 5);
reg.sel_14 = wire.sel177x && (((m_pINOUTPins->pin_ad_in & 0xf) >> 1) == 6);
reg.sel_16 = wire.sel177x && (((m_pINOUTPins->pin_ad_in & 0xf) >> 1) == 7);
}
Спасибо. Однако я подозреваю, что там не одна такая ошибка.
Там теоретически всё должно было работать, механизм и порядок вычисления assignов и alwaysов был подсмотрен в коде, генерируемом verilatorом.
Идея в том, что wire.sel177x должен вычисляться в assign_all в конце вычисления eval_n, а код, где он используется, находится в eval_all_p.
Но в моём алгоритме есть логический изъян. Между этими действиями производится вывод текущих значений ног проца в МПИ, а затем чтение оттуда, и иногда бывает, что значения меняются, при этом надо бы вернуться назад и пересчитать заново все assign и always, что опять приведёт к изменению значений на ногах проца, в общем я не понял, как избежать либо вечного зацикливания, либо варианта, когда нужное состояние проскакивает мимо.
Я всё равно вернусь к дальнейшей отладке, когда у меня будет больше моделей между которыми можно будет организовать взаимодействие.
---------- Post added at 22:06 ---------- Previous post was at 21:59 ----------
Кстати, насчёт RPLY не знаю, оно там генерируется и выдаётся на шину для других устройств, в случае если данный процессор - пассивное устройство на шине. Но вроде бы когда он сам к своим регистрам обращается, то на своё же RPLY никак не реагирует.
Кстати, насчёт RPLY не знаю, оно там генерируется и выдаётся на шину для других устройств, в случае если данный процессор - пассивное устройство на шине. Но вроде бы когда он сам к своим регистрам обращается, то на своё же RPLY никак не реагирует.
Реагирует. Внутренние периферийные регистры надо рассматривать как полную аналогию внешнему устройству, подключенному к шине, только внутри микросхемы. Процессор - это два независимых (с точки зрения шины) блока -периферийный и процессорный. Внутренняя периферия берет сигналы прямо с выводов DIN/DOUT/SYNC, точно так же защелкивает адрес как внешняя, и формирует RPLY (очень быстро, и выход - открытый коллектор). Единственное отличие - корректно рулится направление выводов A/D. То есть при чтении внутренней периферии они работают как выходы в целом, и процессор считывает данные именно с выводов. При записи периферии - для периферии A/D работают входами, а в целом A/D управляет процессор - если пишет то же самое внутреннее ядро то A/D будуn выходами. Если шина захвачена другим агентом - то A/D будут входами. Ну и для 1777x4 и 1777x6 на напревление A/D - своя коррекция. Я как раз табличку пишу на тему.
Powered by vBulletin® Version 4.2.5 Copyright © 2025 vBulletin Solutions, Inc. All rights reserved. Перевод: zCarot