nabbla (nabbla1) wrote,
nabbla
nabbla1

Category:

QuatCore: перебираем компилятор

Все подготовительные работы окончены, теперь придётся ВСЁ СЛОМАТЬ. Одно утешает, "последнюю стабильную версию" я закоммитил, будет к чему откатиться, если сейчас напортачу.

Сейчас очередные "мысли вслух", в надежде привести их в порядок...


Во время компиляции ассемблерного кода, составляется 2 основных списка:

Labels, т.е МЕТКИ. Метка попадает в этот список там, где она ОПРЕДЕЛЯЕТСЯ, а не там, где ИСПОЛЬЗУЕТСЯ. Если она определяется ещё один раз, выскакивает ошибка - так быть не должно. К меткам, начинающимся со знака @, добавляется имя процедуры, где они были определены, например, @j_loop:, заданный в процедуре main, заменяется на main::j_loop. Это так называемые "локальные метки".

Метки бывают трёх разных типов: кода, данных и литералов. Первые указывают на адрес в "сегменте кода" .code, они используются для организации циклов, ветвлений и вызова процедур. Собственно, синтаксис

Foo proc 
...
...
Foo endp


это всего лишь метка Foo, и ограничение области видимости локальных меток - и ничего более того.

Метки данных (ВНЕЗАПНО) определяются в сегменте данных, а литералы задаются директивой EQU, например, в файле QuatCoreConsts:

;константы для аргументов QuatCore
;можно было бы их в сам компилятор "зашить", но пущай так
.data ;костыль нашего компилятора - он ищет EQU только в сегменте данных

;устройства ввода-вывода. Выбор производится командой SIO
UART	EQU	0
LCD	EQU	1
ETH	EQU	2
SD	EQU	10
ADC	EQU	14

;константы, которыми можно инициализировать аккумулятор, командой ZAcc
MinusThreeHalf	EQU	0
MinusOne	EQU	1
ThreeHalf	EQU	2
RoundZero	EQU	3


Важно, что здесь пока не вводится никаких ограничений: мы же не знаем, как потом мы обратимся к этой метке. Если это будет вызов процедуры - любой адрес будет допустим. Или если мы делаем относительный прыжок откуда-то неподалёку :)

По всему выходит, что список меток мы оставляем без изменений.

Чего нельзя сказать о списке Fixups. В нём как будто бы содержатся всё те же метки, но сюда метка попадает, когда она ИСПОЛЬЗУЕТСЯ, но НЕ БЫЛА ОПРЕДЕЛЕНА, либо пока нет уверенности, как её применить.

То есть, мы натыкаемся на прыжок, где указана метка, которой мы ещё не занесли в список Labels. Скорее всего это означает, что производится прыжок "вперёд", и эта метка в своё время будет указана. А пока мы заносим в список Fixups имя этой метки и адрес в коде, где она применена. То есть, хоть у нас компилятор и должен сработать в один проход, но он оставляет отдельные поля пустыми. И когда разбор всего исходного кода закончен, мы принимаемся за список Fixups и вставляем недостающие значения, либо ругаемся, если какая-то из меток так и не появилась.

Что важно: в списке Fixups одна и та же метка может встречаться множество раз - столько же раз, как она встречается в самом коде. И каждый раз она "разрешается" по-новой, и это правильно: если у нас есть несколько относительных прыжков из разных мест на одну и ту же метку, очевидно, что результирующий код получится разный. Или в одном месте прыгнуть удастся, а в другом уже нет. Мало того, на одну и ту же метку мы можем прыгать по-разному.

Именно список Fixups нужно доработать. Как ни странно, сам способ его организации - в виде пары "имя-адрес" - нас вполне устраивает. Но работа с ним усложнится, добавится несколько этапов.

Теперь сюда надо будет заносить вообще любые ИСПОЛЬЗУЕМЫЕ метки, даже если они уже были заданы, и не только метки, но и числа.

Когда весь код будет проанализирован, компилятор узнаёт размер сегмента данных и кода - а значит и ширину адресных регистров и регистра PC. Теперь нужно будет пройти по всему Fixups, найти там все относительные прыжки - и узнать, сколько бит нужно выделить для них.

Теперь мы можем пройтись по Fixups ещё раз - и "разрешить" все значения, но пока не заносить их напрямую в код, а составить таблицу непосредственных значений, причём вместе с каждым значением указывается также маска - какие биты действительно важны, а какие могут быть произвольными. Причём при добавлении каждой новой записи нужно проверить - нет ли повторов, с учётом маски? Скажем, в одном месте у нас литерал 719 = 0x02CF, с маской 0xFFFF (заносим его в 16-битный регистр), а в другом месте: литерал 15 = 0x000F, с маской 0x001F (заносим его в 5-битный регистр). Мы можем с чистой совестью воспринять их как одну константу. Похоже, что в 99% случаев у нас младшие биты будут задействованы, а старшие - как повезёт. В такой ситуации, мне кажется, можно делать жадный выбор - как видим, что две константы можно сделать одной - сразу так и делаем. Так, по крайней мере, можно сразу же на эту таблицу выделить 128 ячеек, и если они все забьются - грязно выругаться.

И только когда таблица будет построена, её нужно будет переупорядочить неким хитрым способом, и превратить её в верилоговский модуль, и только теперь пройтись по Fixups последний раз - и наконец-то занести Immediate значения в код, в соответствии с этой таблицей.

Для начала попробуем составить эту таблицу...
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

  • Нахождение двух самых отдалённых точек

    Пока компьютер долго и упорно мучал симуляцию, я пытался написать на ассемблере алгоритм захвата на ближней дистанции. А сейчас на этом коде можно…

  • Слишком общительный счётчик

    Вчера я чуть поторопился отсинтезировать проект,параметры не поменял: RomWidth = 8 вместо 7, RamWidth = 9 вместо 8, и ещё EnableByteAccess=1, чтобы…

  • Балансируем конвейер QuatCore

    В пятницу у нас всё замечательно сработало на симуляции, первые 16 миллисекунд полёт нормальный. А вот прошить весь проект на ПЛИС и попробовать "в…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments