Ещё у меня дурацкая мысля возникла, что на ПЛИС, для этого "софтового" процессора не очень-то нужен вход reset, ведь он всё равно сбрасывает не всё. Если мы уже успели "затереть" что-нибудь в своей оперативной памяти, то reset это на место не вернёт, тут уж лучше всю ПЛИС сбросить, тогда она отконфигурируется по-новой, в том числе инициализирует содержимое блоков внутренней памяти. По счастью, достаточно лишь "заземлить" вход reset - и при синтезе всё ненужное уберётся.
И если рассчитывать, что с нулевого адреса программа запустится именно после инициализации ПЛИС, то все регистры и вся память имеют известное состояние. Иногда даже не просто "известное", но и заданное нами, чтобы не нужно было в коде программы их инициализировать. Не всегда, правда, вот счётчики lpm_counter всегда нулём инициализируются. Доходит до того, что в "чистом верилоге" мы регистр инициализировали, а синтезатор заметил, что здесь можно счётчик поставить - и всё, накрылась медным тазом наша инициализация. Не очень это правильно, но какая-то это "серая" тема с присвоением начальных значений...
Написали вот такую программу:
;проверяем UART на разогнанном до 25 МГц QuatCore ;сначала "повышаем частоту", затем выводим %include "QuatCoreConsts.inc" %include "Win1251.inc" .rodata EthDisable dw 2,3,0x22,0x54,0x00,0x01,3,0x22,0x66,0x00,0x18,2,0x22,0x6F,0x02 hello dw 'Hello, World! Live fast at 25 MHz',13,0x800A .code main proc SetClock proc ;конфигурирует Ethernet-контроллер на частоту 25 МГц и отключает собственно Ethernet ;SP=0 при включении питания, на ПЛИС с этим строго SIO ETH j [SP++] ;количество посылок (счёт с нуля) @@EthWord: k [SP++] ;количество байт в посылке (счёт с нуля) @@EthByte: OUT [SP++] kLOOP @@EthByte ;нужно, чтобы nCS сбросилось в единицу NOP IN jLOOP @@EthWord SetClock endp SIO UART SP hello C Call(print) @@endless: JMP @@endless main endp ;посылает строку куда-нибудь. Пока модуль ввода-вывода всего один, SIO не нужен (нужный модуль по умолчанию будет активен) ;X указывает на начало строки текста ;конец обозначается отрицательным числом (у нас НЕТ ФЛАГА НУЛЯ!!!) ;меняет значение регистра Acc и SP print proc @@start: OUT [SP] Acc [SP++] SUB 0 JGE @@start JMP C print endp
Вот её листинг:
main proc SetClock proc 0 2002 SIO ETH 1 A1F3 j [SP++] ;количество посылок (счёт с нуля) 2 A2F3 @@EthWord: k [SP++] ;количество байт в посылке (счёт с нуля) 3 00F3 @@EthByte: OUT [SP++] 4 AA7D kLOOP @@EthByte 5 8990 NOP IN 6 A97A jLOOP @@EthWord SetClock endp 7 2000 SIO UART 8 FD0F SP hello 9 8AB0 C Call(print) A B00A @@endless: JMP @@endless main endp print proc B 00FC @@start: OUT [SP] C 80F3 Acc [SP++] D 8300 SUB 0 E BC7B JGE @@start F B083 JMP C print endp
Влезает в 16 слов кода. Не стал инициализировать SP нулём, поскольку знаю и так, что он будет нулевым при начале работы, это как раз реверсивный счётчик, в явном виде выписанный как lpm_counter.
Да, удобная штука адресный регистр с автоинкрементом, жаль он пока всего один у нас, SP. Ну, пока хватает. Вызов функции пока сделали не через стек: адрес возврата заносится в регистр C, там он и лежит до лучших времён. Просто чтобы не забывать синтаксис своего транслятора и разные низкоуровневые штучки, которые здесь разрешены, ибо кто ж мне их запретит!
На удивление всё легко: код откомпилился с первого раза, без каких-либо Hazard'ов, затем я в QuatCore задал ширину ROM, 4 бита (!), ширину RAM, 5 бит, скопировал файлы QuatCoreCode.mif, QuatCoreData.mif и QuatCoreCallTable.v, запустил синтез, прошил - и вуаля:

Фурычит. И на 25 МГц мы наконец можем поставить частоту UART в 921600 бод, это деление частоты в 27 раз, получается с точностью 0,5%, это допустимо. С 4 МГц оно делиться правильно не хотело, разве что "дробный" делитель сделать, но всё равно же каждый бит тогда будет "дёргаться" из стороны в сторону, какой-то будет занимать 4 такта, какие-то 5 тактов, джиттер, так сказать.