Программа с 9 слов кода растолстела аж до 16, но всё ещё умещается в 4-битовую адресацию :)
;Проверяем работу с байтами в QuatCore ;Подумав ещё немножко, решаем расширить на 2 бита не X, а SP (а может ещё подумаем - и всех расширим) %include "QuatCoreConsts.inc" %include "Win1251.inc" .rodata ;в кои-то веки строка должна оканчиваться нулём! (халява закончилась) hello db 'Hello, World! Live fast at 25 MHz',13,10,0 EthDisable db 2,3,0x22,0x54,0x00,0x01,3,0x22,0x66,0x00,0x18,2,0x22,0x6F,0x02 .code main proc SetClock proc ;конфигурирует Ethernet-контроллер на частоту 25 МГц и отключает собственно Ethernet ;SP=0 при включении питания, на ПЛИС с этим строго ;SP EthDisable ;байтовая адресация нужна (хотя если EthDisable занимает младшие байты, можно и без неё обойтись) 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 ;адрес возврата надо заносить в регистр C. print proc @@start: ZAcc RoundZero SUB [SP] JGE C OUT [SP++] JMP @@start print endp
Первым делом мы устанавливаем тактовую частоту, посылая набор сообщений Ethernet-контроллеру. Видна одна "закомментированная" строка,
SP EthDisable
после которой SP стал бы равен 0x80, что означает "адрес 0, на шину данных выдавать только младший байт".
Без неё у нас просто SP=0, что означает "адрес 0, выдавать полное слово". Но поскольку SPI-контроллер "реагирует" только на младшие 8 бит, ему пофиг, что "сверху" поступает строка Hello, World.
Очень уж не хотелось, чтоб код занимал 17 слов, неаккуратненько это как-то...
Вот основная "статистика":
Компиляция завершена успешно Ширина адреса сегмента кода (и регистра PC): 4 Ширина адреса сегмента данных (и регистров X,Y,Z,SP): 6 Ширина сумматора для относительных прыжков: 4 Количество инициализированных слов данных: 51 Количество инициализированных слов кода: 16 Количество адресов процедур: 1
Хм, ширина сумматора для относительных прыжков "живёт своей жизнью" - в прошлый раз вообще стоял ноль, а сейчас четвёрка. Сейчас мы вообще их не используем, так что влиять не должно.
Посмотрим листинг программы:
main proc SetClock proc 0 1028 SIO ETH 1 A1F3 j [SP++] ;количество посылок (счёт с нуля) 2 A2F3 @@EthWord: k [SP++] ;количество байт в посылке (счёт с нуля) 3 00F3 @@EthByte: OUT [SP++] 4 AA68 kLOOP @@EthByte 5 8990 NOP IN 6 A928 jLOOP @@EthWord SetClock endp 7 1008 SIO UART 8 FD08 SP hello 9 8AB0 C CALL(print) A B038 @@endless: JMP @@endless main endp print proc B 8868 @@start: ZAcc RoundZero C 83FC SUB [SP] D BC83 JGE C E 00F3 OUT [SP++] F B078 JMP @@start print endp
Посмотрим непосредственные значения:
2 000F SIO ETH/jLOOP SetClock::@EthWord 3 000F kLOOP SetClock::@EthByte/ZACC RoundZero 192 00FF SIO UART/SP hello 10 000F JMP main::@endless 11 000F JMP print::@start
Да, адресация абсолютная, всё нормально. Выглядит неплохо. И таблица непосредственных значений вообще без ЛЭ выходит, и задействует всего 3 бита из доступных 7.
С программой разобрались, теперь параметры надо выставить
В этот раз enableSPI=1, а в модуле UART ставим тактовую частоту 25 МГц, а скорость передачи 921 600, теперь можно :)
Попробуем в этот раз обойтись без симуляции.
Когда-то долго и упорно этот конкретный SetClock мы ковыряли, должен бы заработать.
Вся схема целиком синтезируется в 1314 ЛЭ, на 28 ЛЭ больше. Ну да, модуль SPI подключился, логично.
Максимальная частота 25,77 МГц. Да, Place&Route так себя ведёт, более сложная схема может работать шустрее, рандома хватает.
Осталось прошить. На терминале ставим скорость 921 600, запускаем, и...

Нда, что-то не то. Намекает на неправильную частоту передачи. Медленно-медленно мигающий светодиод на плате, который должен вспыхивать раз в секунду, тоже говорит, что частота настроена так и не была...
Сделаем ещё один грязный трюк: на компьютере настроим скорость UART в 921 600 * 4 / 25 = 147 456 бод. По счастью, "виртуальный COM-порт" на микросхеме CP2102 не ограничивается стандартными скоростями. Получаем уже нечто более осмысленное:

В начале наш традиционный нолик.
В конце получаем свой Hello, world.
А вот между ними происходит какая-то хрень. Впрочем, не такая уж хрень. Взглянем ещё раз на нашу строку EthDisable:
EthDisable db 2,3,0x22,0x54,0x00,0x01,3,0x22,0x66,0x00,0x18,2,0x22,0x6F,0x02
Тут мы попытались самим себе напомнить, как эта строка устроена: передаваемые значения указаны в Hex, т.е начинаются с 0x, а всё остальное - это количество байт и количество "посылок".
Первая двойка - количество посылок минус 1, т.е посылок реально 3.
Второе число, 3 - количество байт первой посылки минус 1, т.е передаётся 4 байта. Когда они закончатся - будет следующее число - и так далее.
Итого, мы должны передать:
22 54 00 01 22 66 00 18 22 6F 02
Всё это и передаётся, только почему-то по UART
Но мало того, похоже у нас передаются и команды SIO. Вторым же символом идёт C2, а непосредственно перед Hello, world: уже знакомая нам C0.
Всё выглядит так, будто у селектора ввода-вывода так и не обновился параметр EnableSPI=1. Почему-то у меня такое случалось уже не раз, когда параметры, выставленные "сверху", каким-то образом не проходят "вниз" при синтезе, остаются старые значения. Чтобы проверить, в этом ли дело, можно в окне Compilation report в дереве слева выбрать Analysis&Synthesis / Parameter settings by Entity Instance, и там отыскать наш злополучный IO Selector:

Нет, всё правильно стоит. Да и добавление 28 ЛЭ намекает, что ранее "отрезавшийся" модуль SPI теперь всё-таки доступен. И результаты оптимизации это подтверждают:

Здесь фигурируют регистр модуля LCD (в конечном итоге уходят ВСЕ), а вот SPI здесь нет!
Возможно, что умудрился "заблудиться в трёх соснах" в этих трёх параметрах. Вот этот злополучный модуль:
//DestAddr==000x_xxxx : 'OUT' //DestAddr==001x_xxxx : select output device //xxxx_xxxx_xxxx_xx00 - UART //xxxx_xxxx_xxxx_xx01 - LCD //xxxx_xxxx_xxxx_xx1x - SPI //also selecting SPI device //xxxx_xxxx_xxxx_0xxx - Ethernet //xxxx_xxxx_xxxx_10xx - SD //xxxx_xxxx_xxxx_11xx - ADC //IN command is //SrcAddr == 1001_0xxx //(ALU Src was 100x_xxxx, now it is 1000_xxxx) module QuatCoreIOselector ( input clk, input [7:0] SrcAddr, input [7:0] DestAddr, input [15:0] DataBus, input DestStall, input SrcStall, input SrcDiscard, input SPI_busy, output UARTtxEN, output LCD_EN, output SPItxEN, output UARTrxEN, output SPIrxEN, output reg [1:0] SPIdevice = 2'b0); parameter enableLCD = 1'b0; parameter enableUART = 1'b1; parameter enableSPI = 1'b0; localparam HasChoice = enableSPI | ((enableLCD + enableUART + enableSPI) > 1); wire isSelection = (~DestAddr[7]) & (~DestAddr[6]) & DestAddr[5] & (~DestStall) & HasChoice; wire isIO_out = (~DestAddr[7])&(~DestAddr[6])&((~DestAddr[5]) | (~HasChoice)) & (~DestStall); wire isIO_in = (SrcAddr[7:3] == 5'b1001_0)&(~SrcStall)&(~SrcDiscard); reg [1:0] sel = enableUART? 2'b00: enableLCD? 2'b01: 2'b10; reg [1:0] shadowSPI = 2'b0; always @(posedge clk) if (isSelection) begin sel <= DataBus[1:0]; shadowSPI <= DataBus[3:2]; end always @(posedge clk) if (~SPI_busy) SPIdevice <= shadowSPI; assign UARTtxEN = (sel==2'b00) & isIO_out & enableUART; assign LCD_EN = (sel==2'b01) & isIO_out & enableLCD; assign SPItxEN = sel[1] & isIO_out & enableSPI; assign UARTrxEN = (sel==2'b00) & isIO_in & enableUART; assign SPIrxEN = sel[1] & isIO_in & enableSPI; endmodule
И я знаю ответ!
Этот модуль не был изменён с появлением GPU. У нас на вывод информации были доступны адреса DestAddr = 0x00..0x7F, целых 128 штук.
Поначалу всё это было в единоличном распоряжении IOselector, поэтому по 7-му биту мы определяли, что команда адресована "нам", по 6-му биту - что это за команда, OUT (0x00..0x3F) или SIO (0x40..0x7F).
Потом у нас появилась статическая память, и половину адресного пространства мы широким жестом отдали ей, на команды ERL (External memory Register Low), ERH (External memory Register High), задающие 20-битный адресный регистр, и [ER++], записывающий в память по текущему адресу, с инкрементом. Так что OUT ужался до 0x00..0x1F, а SIO: до 0x20..0x3F.
Но теперь у нас возник ещё и GPU, и ему тоже нужны адреса! Поэтому мы откусили ещё половинку, и теперь команде OUT даны адреса 0x00..0x0F, а команде SIO: 0x10..0x1F.
Действительно, на симуляции и в листингах мы видели 0x10 для SIO. А в комментариях к модулю IOselector чёрным по белому указаны старые адреса.
Дурацкая ошибка, сразу видно что молод я ещё, не застал, когда устройства на PCI шине надо было ручками конфигурировать, привык к Plug&Play...
К счастью, с командой IN проблем нет: мы "обобрали" АЛУ в этот раз.
Что ж, добавляем ещё один бит и не забываем привести комментарии в соответствие с кодом:
//DestAddr==0000_xxxx : 'OUT' //DestAddr==0001_xxxx : 'SIO' (select input/output device) //xxxx_xxxx_xxxx_xx00 - UART //xxxx_xxxx_xxxx_xx01 - LCD //xxxx_xxxx_xxxx_xx1x - SPI //also selecting SPI device //xxxx_xxxx_xxxx_0xxx - Ethernet //xxxx_xxxx_xxxx_10xx - SD //xxxx_xxxx_xxxx_11xx - ADC //IN command is //SrcAddr == 1001_0xxx //(ALU Src was 100x_xxxx, now it is 1000_xxxx) module QuatCoreIOselector ( input clk, input [7:0] SrcAddr, input [7:0] DestAddr, input [15:0] DataBus, input DestStall, input SrcStall, input SrcDiscard, input SPI_busy, output UARTtxEN, output LCD_EN, output SPItxEN, output UARTrxEN, output SPIrxEN, output reg [1:0] SPIdevice = 2'b0); parameter enableLCD = 1'b0; parameter enableUART = 1'b1; parameter enableSPI = 1'b0; localparam HasChoice = enableSPI | ((enableLCD + enableUART + enableSPI) > 1); wire isSelection = (DestAddr[7:4] == 4'b0001) & (~DestStall) & HasChoice; wire isIO_out = (DestAddr[7:5] == 3'b000) & ((~DestAddr[4]) | (~HasChoice)) & (~DestStall); wire isIO_in = (SrcAddr[7:3] == 5'b1001_0)&(~SrcStall)&(~SrcDiscard); reg [1:0] sel = enableUART? 2'b00: enableLCD? 2'b01: 2'b10; reg [1:0] shadowSPI = 2'b0; always @(posedge clk) if (isSelection) begin sel <= DataBus[1:0]; shadowSPI <= DataBus[3:2]; end always @(posedge clk) if (~SPI_busy) SPIdevice <= shadowSPI; assign UARTtxEN = (sel==2'b00) & isIO_out & enableUART; assign LCD_EN = (sel==2'b01) & isIO_out & enableLCD; assign SPItxEN = sel[1] & isIO_out & enableSPI; assign UARTrxEN = (sel==2'b00) & isIO_in & enableUART; assign SPIrxEN = sel[1] & isIO_in & enableSPI; endmodule
Вся система целиком по-прежнему синтезируется в 1314 ЛЭ, что не может не радовать! Вообще, эти вот декодеры адресов на удивление "дешёвые". И максимальная частота по-прежнему 25,77 МГц.
Поехали!

Да! И светодиод мигает раз в секунду, намекая, что частота действительно настроена в 25 МГц.
Самое время тестировать "в железе" обработку изображения - не прошло и полгода...