nabbla (nabbla1) wrote,
nabbla
nabbla1

Category:

Быстродействующий байтовый Hello,World :)

В смысле, пора с 4 МГц в очередной раз перейти на 25 МГц, и воочию убедиться, что вся наша весёлая связка QuatCore+GPU+периферия действительно на 25 МГц может работать.

Программа с 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 МГц.

Самое время тестировать "в железе" обработку изображения - не прошло и полгода...
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