Я в предыдущем письме добавил архив для ModeiSim, проверил, вроде работает. Но есть вероятность что ему все равно альтеровские библиотеки понадобяться (в Квартус у меня устновлен).
И эта вероятность равна 100%.
Но изменение в config.v значения CONFIG_VM1_CORE_REG_USES_RAM на 0 действительно решает проблему ( мне слабо переделать vm1_run.do, поэтому набор для симуляции: QSync_for_ModelSim.zip надо запускать через создание обычного родного проекта ModelSim ).
Но вот вопрос - как заставить ModelSim выводить значение линий AD в инверсном ( т.е. нормальном ) виде ?
Последний раз редактировалось Patron; 12.12.2015 в 18:03.
О, симуляция пошла, замечательно!
Тут простого ответа я не знаю, можно завести дополнительный сигнал, например i_ad[15:0], назначить ему в исходниках инверсию от pin_ad, и смотреть уже его. Или в подпапке <root>/Qbus есть диаграммы регистров адреса (areg) и данных (qreg), они инвертированы, можно сами значения из них брать. Они вполне мышкой перетаскиваются, можно копи-пастить, то есть легко расположить рядом с диаграммой ad.
Последний раз редактировалось Vslav; 12.12.2015 в 18:15.
.
В тактовом генераторе tbench.v есть ошибка из-за которой весь нулевой такт сигнал clk остаётся неизменным, а первый такт ( как и все последующие ) начинается с положительного полупериода :
Если изменить код так:
Код://_____________________________________________________________________________ // // Clock generator // initial begin ena = 1; clk = 0; nclk = 0; forever begin `ifdef CONFIG_SIM_DEBUG_MC $display("clk: %04d", nclk); `endif clk = 0; #(`CONFIG_SIM_CLOCK_HPERIOD); clk = 1; #(`CONFIG_SIM_CLOCK_HPERIOD); nclk = nclk + 1; end end
То работа тактового генератора приходит в норму и растактовка исправляется:
![]()
Это не ошибка, такая особенность первого клока в тестбенче не приводит ни к каким принципиальным последствиям - понятие такт очень условно, просто вот так нарисовалась сеточка на диаграмме, и все процессы сдвинулись на 5 нс. Можно банально вертикальную сеточку сделать 5 нс (оно автоматом, просто позумить в окошке), чтобы отметить и фронты и срезы и чуток проскроллить вправо - и разницы вообще глазом не увидеть. Отладочный nclk остался со времен отладки микроавтомата, тогда его фаза имела смысл, но сейчас его уже можно и вообще удалить.
Update: часто бывает что тактовая от PLL формируется, тогда в начальный момент симуляции тактовой частоты вообще нет, появляется со временем и в произвольной фазе, поэтому привязываться к "сеточке" бессмысленно.
Update2: сейчас LSI-11 разбираю, там 4 тактовых сигнала, все тоже должно быть инвариантно, независимо с какого C1-C4 начнется счет.
Update3: обновление 1.4D
- изменена структура каталогов (потихоньку приближаемся к требованиям OpenCores)
- теперь можно перейти в каталог симуляции и запустить в ModelSim (Altera Edition можно Started/Полный) файл run.do. Удалены все зависимости от путей, для проектов Async/Qsync Quartus при этом не нужен, сразу симулируется с полным пакетом диаграмм.
- проект Async не требует для симуляции альтеровских библиотек
- добавлен тест вычисления знаков числа Пи
Последний раз редактировалось Vslav; 13.12.2015 в 16:38.
С любовью к вам, Yandex.Direct
Размещение рекламы на форуме способствует его дальнейшему развитию
Сейчас как раз идёт отладка CPP-модели ( методом потактового сравнения содержимого переменных ), поэтому правильная фаза nclk играет важную роль.
- - - Добавлено - - -
Прояснилась причина, по которой CPP-модель не снимала SYNC при снятии RPLY между eval_p() и eval_n().
Сигнал oe_clr_fc использовался в eval_all_n() раньше, чем устанавливался в assign_all(), а поскольку этот сигнал активен только полтакта, то к следующему вызову eval_all_n() сигнал уже снова был сброшен и поэтому SYNC оставался установленным "вечно".
Вообще говоря - используемый в CPP-модели подход методически ложен:
Код:void eval_n() { eval_all_n(); // выполняем все always для переднего фронта assign_all(pMPI); // вычисляем все assignы }
Методически корректный подход выглядит так:
Код:void eval_n() { assign_all(pMPI); // вычисляем все assignы eval_all_n(); // выполняем все always для переднего фронта assign_all(pMPI); // вычисляем все assignы }
А практически правильный подход может быть таким:
- - - Добавлено - - -Код:void eval_n() { assign_in(pMPI); // вычисляем все входные зависимости eval_all_n(); // выполняем все always для переднего фронта assign_out(pMPI); // вычисляем все выходные зависимости }
В ходе тестирования модели QSync выяснился интересный момент.
При осуществлении записи сначала выставляются на шину прошлые данные записи и только затем - новые.
Например, при выполнении на модели процессора 1801ВМ1 следующего кода:
Процессор пишет в память начальное содержимое регистров:Код:.ASect . = 0 MOV R0, (PC)+ NOP MOV R1, (PC)+ NOP MOV R2, (PC)+ NOP MOV R3, (PC)+ NOP MOV R4, (PC)+ NOP MOV R5, (PC)+ NOP MOV SP, (PC)+ NOP HALT
И при выполнении команды MOV R1, (PC)+ - осциллограмма выглядит так:Код:gpr[0] = 0133000; gpr[1] = 0100514; gpr[2] = 0100410; gpr[3] = 0040000; gpr[4] = 0100057; gpr[5] = 0060000; gpr[6] = 0000011;
Лог при этом выглядит так:
Код:# [000085]-1- ad_ena: 0 ; pin_ad_out: 000000 ; dout_out: 0 ; qrd: 133000 # # [000085]-0- ad_ena: 0 ; pin_ad_out: 000000 ; dout_out: 0 ; qrd: 133000 # # [000086]-1- ad_ena: 0 ; pin_ad_out: 000000 ; dout_out: 0 ; qrd: 133000 # # [000086]-0- ad_ena: 1 ; pin_ad_out: 133000 ; dout_out: 1 ; qrd: 133000 # # [000087]-1- ad_ena: 1 ; pin_ad_out: 100514 ; dout_out: 1 ; qrd: 100514 # # [000087]-0- ad_ena: 1 ; pin_ad_out: 100514 ; dout_out: 1 ; qrd: 100514 # # [000088]-1- ad_ena: 1 ; pin_ad_out: 100514 ; dout_out: 1 ; qrd: 100514 # # [000088]-0- ad_ena: 1 ; pin_ad_out: 100514 ; dout_out: 1 ; qrd: 100514
Интересно, как выглядит аналогичная осциллограмма при выполнении записи реальным 1801ВМ1.
Последний раз редактировалось Patron; 13.12.2015 в 19:10.
Совсем так точно не выглядит - в реальной системе RPLY побыстрее ставится и снимается, а процессор ведет себя точно также - выставляет/снимает сигналы в той же фазе (ну еще задержку надо учитывать, примерно 10-15нс вносят буфера, остальное - родная задержка Tco микросхемы ВМ1). И достоверные данные появляются на шине одновременно со спадом DOUT: []
Update: в строке 1475 vm1_qbus.v можно сменить полярность pin_clk_n на pin_clk_p, тогда достоверные данные записи будут выставляться на шине за полтакта до DOUT. При этом граничная частота проекта на реальном Циклоне-3-С8 падает с 90МГц до 72МГц. Наверное, придется пожертвовать. Версии на Wishbone эта проблема не касается.
Update2: а вообще, не так плохо все, на speedgrade C6 частота падает до 95МГц всего, после тестирования изменение можно внести постоянно.
Последний раз редактировалось Vslav; 14.12.2015 в 02:01.
При этом получается, что assign_all(pMPI); перед обоими eval_all_x(); избыточен, поскольку eval_n() и eval_p() вызываются последовательно друг за другом. Развёрнутая цепочка вызовов функций внутри цикла вызовов последовательности eval_n() и eval_p() выглядит так.
Именно так выглядит цикл, генерируемый Верилатором.Код:{ assign_all(pMPI); eval_n(); eval_p(); assign_all(pMPI); } init eval_n(); assign_all(pMPI); eval_p(); assign_all(pMPI); eval_n(); assign_all(pMPI); eval_p(); assign_all(pMPI); .....
Я такой подход реализовать не смог. Т.к. у меня не получилось сделать обёртку vm1.v над vm1_qbus.v, такая модель не получалась работоспособной в принципе, пришлось разворачивать их в одну большую плоскую функцию. При этом получилось так, что входные зависимости (ноги, у которых тип inout), зависят от выходных в текущей итерации. Поэтому функция assign_all у меня минимум двухпроходная. И изредка выполняется за три прохода, из-за изменения данных в переменной pin_ad_n.
Это ошибочное впечатление, потому что на самом деле там ещё есть вызов eval(pMPI) ( который делается в tboard.cpp ) и полная последовательность вызовов выглядит так:
А должна выглядеть так:Код:eval_n(); assign_all(pMPI); eval(pMPI) eval_p(); assign_all(pMPI); eval(pMPI) eval_n(); assign_all(pMPI); eval(pMPI) eval_p();
- - - Добавлено - - -Код:eval_n(); assign_all(pMPI); eval(pMPI) assign_all(pMPI); eval_p(); assign_all(pMPI); eval(pMPI) assign_all(pMPI); eval_n(); assign_all(pMPI); eval(pMPI) assign_all(pMPI); eval_p();
Если добавить в проект Верилятора обработку tbench.v, то результат будет другим ( из-за учёта влияния памяти и устройств на состояние сигналов шины между вызовами ).
Можно сделать две копии assign_all(pMPI) и выкинуть из первой все зависимости от сигналов, которые не изменяются памятью и устройствами ( типа BSY, SYNC, DIN, DOUT ), а из второй - от сигналов, которые не изменяет процессор ( типа IRQ1, IRQ2, IRQ3, VIRQ, DMR ).
- - - Добавлено - - -
В реале данные выставляются на одном полутакте с DOUT, поэтому лучше задержать на полтакта активацию pin_ad_ena.
Последний раз редактировалось Patron; 14.12.2015 в 14:57.
Эту тему просматривают: 3 (пользователей: 0 , гостей: 3)