nabbla (nabbla1) wrote,
nabbla
nabbla1

Category:

SPI на быстром QuatCore

Продолжаем "наращивать периферию". Думал, что передачу посылок на Ethernet-контроллер "одолею" очень быстро, благо и UART, и ЖК-экранчик "на передачу" замечательно сработали, и здесь ничего шибко нового нет.

Оказалось, всё не так просто: я, чтобы выдержать паузу между отдельными сообщениями (когда надо установить nCS в единицу, а затем снова в ноль и тем самым начать новую "посылку"), использовал также команду IN ("приём"), которая выжидает окончание двустороннего обмена (мы отправляем данные по MOSI, а получаем по MISO), заносит результат в шину данных, и только после этого возобновляет работу.

Так что налаживать придётся сразу "дуплекс", иначе ничего не заработает.

И сразу воспользуемся "разветвителем" на 3 устройства, который понадобился из-за того, что все наши SPI-устройства выведены на разные ножки ПЛИС, а соединить их между собой "аналогово" (непосредственной коммутацией) ПЛИС не способна, ей нужно давать указания, какая ножка в каждый момент времени на вход, а какая на выход.


Для нашего модуля SPI нужно подключать внешний делитель частоты, с коэффициентом деления как минимум два, иначе этот модуль может работать некорректно (хотя я уже забыл, в чём там дело и насколько легко оно исправляется). Это пока что снижает возможные скорости обмена: с SD-карточкой мы могли бы общаться на 25 МГц, но при тактовой частоте 25 МГц и делении пополам, частота SCK составит всего лишь 6,25 МГц. С другой стороны, даже умей мы работать с SD на всех 25 МГц, мы не смогли бы записывать несжатый видеопоток "высокой чёткости", в 8 раз не дотягиваем до требуемого битрейта! (нужно 25 МБайт/с, а по SPI получается лишь 25 МБит/с, в лучшем случае, не считая "накладных расходов"). Поэтому в какой-то момент мы приняли волевое решение поставить лучше SRAM (статическую память) и записывать кадр туда.

Ethernet-контроллер поддерживает частоту SPI от нуля вплоть до 14 МГц, так что с "делением пополам" попадаем в этот интервал нормально. Наконец, наша "медленная" (500 килосэмплов в секунду) АЦП ADC124SO51 лучше всего работает (выдаёт заявленные параметры) на частотах от 3,2 МГц до 8 МГц, так что наши 6,25 МГц очень даже подходят.

Поэтому решаем пока что сделать делитель частоты вдвое.

Вот код модуля SPI:
//дуплексный контроллер SPI
//наиболее универсальный, но требует частоты ce хотя бы вдвое меньше clk, в противном случае между переданными словами может появляться прореха с nCS=1
//для запуска используем исключительно TXen (т.е команду OUT), а за ней - RXen (т.е команду IN), она дождётся окончания приёма-передачи 16 бит - и выдаст результат
//после этого, если мы успеем подать TXen до того, как nCS=1, то начнём передачу ещё одного слова.

//главная хитрость - правильно сформировать сигнал busy.
//по TXen мы ждём определённого импульса NEG_E по последнему биту или во время ожидания.
//по RXen мы должны сбросить busy сразу после импульса POS_E по последнему биту.
//ЛИБО сформировать выход как SR[15:1] и MISO. Очень смешно, но должно заработать...

`include "math.v"
module QuatCore8bitDuplexSPI (	input clk, input ce, input [15:0] D, input TXen, input RXen,
				input MISO,
				output [15:0] Q,
				output reg SCK=1'b0, output MOSI, output nCS, output busy, output ceo);

//	assign MISO = nCS? 1'b1 : 1'bz; //"неактивным" здесь считается лог. "1", на манер UART
	
	//нужно более 9 состояний
	localparam sIdle =	4'h0;
	localparam sB0 =	4'h8;
	localparam sB1 =	4'h9;
	localparam sB2 =	4'hA;
	localparam sB3 =	4'hB;
	localparam sB4 =	4'hC;
	localparam sB5 =	4'hD;
	localparam sB6 =	4'hE; 
	localparam sB7 =	4'hF;
	
	wire [3:0] State; //вставим его отдельным модулем, чтобы задействовать режим счётчика ЛЭ

	reg [8:0] SR = 9'h1FF; //нужно одним битом больше, потому что пока защёлкиваем по MISO, не успеваем сдвинуть младший и затираем его нахрен
	//могли бы, учитывая полудуплекс, иногда защёлкивать, а иногда нет, но нужен регистр для определения - приём или передача - то на то и выходит.
	
	assign nCS = ~State[3]; //по сути, "isIdle"

	wire NEG_E = ce & SCK; //спад SCK, когда мы переключаем состояния и сдвигаем данные в регистре
	wire POS_E = ce & (~SCK); //фронт SCK, когда мы защёлкиваем входной бит

	wire IsFinalBit; //генерируется логикой переноса (cout)
	
	wire TXbusy = TXen & (~(NEG_E & (nCS | IsFinalBit))); //т.е ждём, пока не будет спада SCK в режиме Idle или на передаче последнего бита
	wire RXbusy = RXen & (~(POS_E & (nCS | IsFinalBit))); //т.е ждём, пока не будет фронта SCK в режиме Idle или на передаче последнего бита
	assign busy = TXbusy | RXbusy;
	wire start = TXen & NEG_E & (nCS | IsFinalBit); //то есть, старт всегда по TXen, а по RXen можно получить ответ, что же там было!
	//assign busy = ((TXen & (~NEG_E))|(RXen & (~POS_E))) & (nCS | IsFinalBit);
	//правда, и такой вариант не очень, т.к возникнет задержка до следующего слова. 
				
	lpm_counter StateCounter (.clock (clk),
				  .cnt_en (NEG_E & (~nCS)),
				  .sset (start), 
				  .Q (State),														
				  .cout (IsFinalBit));
	defparam
		StateCounter.lpm_direction = "UP",
		StateCounter.lpm_port_updown = "PORT_UNUSED",
		StateCounter.lpm_type = "LPM_COUNTER",
		StateCounter.lpm_width = 4,
		StateCounter.lpm_svalue = sB0;			                    
	

	assign MOSI = SR[8];
	assign Q = {{8{SR[7]}}, SR[7:1], MISO}; //старший (15) бит дублирует 7-й, т.к удобно проверять - как "знак". 
				
	always @(posedge clk) begin
		SCK <= ce? ~SCK : SCK;
		if (NEG_E)
			SR[8:1] <= start? D[7:0]: SR[7:0];
		if (POS_E)
			SR[0] <= MISO;
	end
	
	assign ceo = ce & SCK; //просто сэкономили 1 бит в делителях частоты, поскольку мы ОЧЕНЬ жадные.
endmodule


По обилию непонятных комментариев, ясно, что давался он тяжело...

Выход ceo просто даёт единичные импульсы с частотой 6,25 МГц, это мы для контроля частоты заставляем один из светодиодов мигать с частотой 1 Гц, ну и из жадности начинаем делить с этих 6,25 МГц, а чего добру пропадать?

Уже вижу, что сигнал busy надо будет переделать, сделать аж 3 разных. Один - для QuatCoreIOselector, чтобы он "из-под ног не вырывал устройство", на ходу меняя, к какому из устройств обращаться. Второй - к DestStallReq, третий - к SrcStallReq. А пока нас и так устроит...

А это код мультиплексора на 3 устройства:
//нужен, когда входы MISO для разных девайсов сидят на РАЗНЫХ НОЖКАХ ПЛИС
//просто соединить их друг с другом нельзя - компилятор не поймёт, какая из них должна активироваться
//так что сделаем свой собственный мультиплексор

//Device 	=	0x: ETH
//		=	10: SD
//		=	11: ADC
module SPI_Mux3 (input clk, input nCS, input [1:0] Device, output MISO_MUX, output SD_nCS, output ADC_nCS, output ETH_nCS,
		inout SD_MISO, inout ADC_MISO, inout ETH_MISO);

assign SD_nCS = nCS | ~(Device == 2'b10);
assign ADC_nCS = nCS | ~(Device == 2'b11);

reg rETH_nCS = 1'b1;
assign ETH_nCS = rETH_nCS;
always @(posedge clk)
	rETH_nCS <= nCS | Device[1];
//assign ETH_nCS = nCS | Device[1];
//Нужно обеспечить 50 нс между спадом и nCS и фронтом SCK (это у нас и так есть)
//а также 50 нс между спадом SCK и фронтом nCS (этого не было)
//этим регистром мы из 500 нс / 0 нс получаем 250 нс / 250 нс. 

assign SD_MISO = SD_nCS? 1'b1 : 1'bz;
assign ADC_MISO = ADC_nCS? 1'b1 : 1'bz;
assign ETH_MISO = ETH_nCS? 1'b1 : 1'bz;

assign MISO_MUX = 	~Device[1]?	ETH_MISO :
			Device[0]?	ADC_MISO :
					SD_MISO;

endmodule


По крайней мере, всё это безобразие успешно синтезируется - и fitter срабатывает весьма шустро, и в тайминги укладываемся до сих пор, с предельной частотой аж 25,19 МГц. И то радость, а то был момент, когда после любого изменения в схему приходилось долго и упорно "танцевать с бубном", пока оно наконец не сделается как надо. Либо Can't find fit, либо Critical Warning о несоблюдении таймингов.

Теперь нужно стряхнуть пыль с нашей программы, описанной здесь:
;посылаем в Ethernet-контроллер набор команд,
;чтобы максимально выключить все его устройства
;и главное, сменить тактовую частоту
%include "QuatCoreConsts.inc"
.rodata
	EthDisable	dw	2,3,0x22,0x54,0x00,0x01,3,0x22,0x66,0x00,0x18,2,0x22,0x6F,0x02
;2-количество посылок-1, т.е 3 посылки
;3-количество байт в первой посылке-1, т.е 4 байта
;затем сама посылка, затем количество байт в следующей посылке - и так далее
;0x22,0x54,0x00,0x01 - поместить в MIREGADR адрес регистра PHCON1
;0x22,0x66,0x00,0x18 - поместить в MIWR новое содержимое для PHCON1: PSLEEP=1 среди прочего
;0x22,0x6F, 0x01 - установить тактовую частоту 33 МГц и отключить Ethernet-контроллер
;взамен 0x22,0x6F,0x02 - установить тактовую частоту 25 МГц и отключить Ethernet-контроллер
;0x22,0x6F, 0x0B - оставить тактовую частоту 4 МГц, чтобы в контр. условиях понять, чего мы наворотили...
;0x22,0x6F, 0x0F - 50 кГц, чтобы вообще в замедленной съёмке!
;частота SPI (SCK) должна быть не более 1 МГц, иначе Phys не отключится (нужно давать ему время!)
;упорно не работает, хотя вроде бы все условия выполнили...

.data

.code
	main proc
		@@yetagain:	SP		EthDisable
				;SIO		SD		;для отладки, посмотреть осциллограммы
				SIO		ETH
				
				j		[SP++]	;количество посылок (счёт с нуля)
		@@EthWord:	k		[SP++]	;количество байт в посылке (счёт с нуля)
		@@EthByte:	OUT		[SP++]
				kLOOP		@@EthByte
			;нужно, чтобы nCS сбросилось в единицу
				k		IN		;лишние регистры не "засоряем", хотя пофиг скорее всего
				jLOOP		@@EthWord

				;пережидаем 100 мкс, это 400 тактов. Если простое вычитание и проверка знака - это 4 такта, т.е до 100 надо сосчитать
				;если вычитать значение делённое на 2, это 5 тактов, т.е до 80. От 40 вниз до 0, по 1/2 - ага.
;				Acc		40
;		@@wait:	DIV2S		1
;				JGE		@@wait
				
		@@endless:	JMP		@@endless			
	main endp


В этот раз включим сразу два модуля: и SPI, и UART. Последний хотим потом протестить на повышенной тактовой частоте, аж на 921600 бод. Хотя бы можно не переживать за нолик, посланный в SPI. Он будет послан в UART, а только потом мы переключимся на SPI, с помощью команды SIO (Select I/O).

И глядя на программу, понимаем: одной передачей мы здесь не обойдёмся! Используется команда IN, которая введена, чтобы дождаться передачи всех данных, после чего сделать ещё некоторую паузу (пока будем прыгать в начало цикла), и только потом отправлять следующий "пакет" байтов. Ладно, переписываем сразу модуль SPI:

//дуплексный контроллер SPI
//наиболее универсальный, но требует частоты ce хотя бы вдвое меньше clk, в противном случае между переданными словами может появляться прореха с nCS=1
//для запуска используем исключительно TXen (т.е команду OUT), а за ней - RXen (т.е команду IN), она дождётся окончания приёма-передачи 16 бит - и выдаст результат
//после этого, если мы успеем подать TXen до того, как nCS=1, то начнём передачу ещё одного слова.

//главная хитрость - правильно сформировать сигнал busy.
//по TXen мы ждём определённого импульса NEG_E по последнему биту или во время ожидания.
//по RXen мы должны сбросить busy сразу после импульса POS_E по последнему биту.
//ЛИБО сформировать выход как SR[15:1] и MISO. Очень смешно, но должно заработать...

`include "math.v"
module QuatCore8bitDuplexSPI (	input clk, input ce, input [15:0] D, input TXen, input RXen,
				input MISO,
				output [15:0] Q,
				output reg SCK=1'b0, output MOSI, output nCS, output busy, output TXbusy, output RXbusy, output ceo);

//	assign MISO = nCS? 1'b1 : 1'bz; //"неактивным" здесь считается лог. "1", на манер UART
	
	//нужно более 9 состояний
	localparam sIdle =  4'h0;
	localparam sB0 =    4'h8;
	localparam sB1 =    4'h9;
	localparam sB2 =    4'hA;
	localparam sB3 = 	4'hB;
	localparam sB4 =    4'hC;
	localparam sB5 =    4'hD;
	localparam sB6 =    4'hE; 
	localparam sB7 =	4'hF;
	
	wire [3:0] State; //вставим его отдельным модулем, чтобы задействовать режим счётчика ЛЭ
//	assign DState = State;

	reg [8:0] SR = 9'h1FF; //нужно одним битом больше, потому что пока защёлкиваем по MISO, не успеваем сдвинуть младший и затираем его нахрен
	//могли бы, учитывая полудуплекс, иногда защёлкивать, а иногда нет, но нужен регистр для определения - приём или передача - то на то и выходит.
	
	assign nCS = ~State[3]; //по сути, "isIdle"

	wire NEG_E = ce & SCK; //спад SCK, когда мы переключаем состояния и сдвигаем данные в регистре
	wire POS_E = ce & (~SCK); //фронт SCK, когда мы защёлкиваем входной бит

	wire IsFinalBit; //генерируется логикой переноса (cout)
	
	assign TXbusy = TXen & (~(NEG_E & (nCS | IsFinalBit))); //т.е ждём, пока не будет спада SCK в режиме Idle или на передаче последнего бита
	assign RXbusy = RXen & (~(POS_E & (nCS | IsFinalBit))); //т.е ждём, пока не будет фронта SCK в режиме Idle или на передаче последнего бита
	assign busy = ~nCS;	//для защёлкивания "теневого регистра" выбора устройства SPI
	wire start = TXen & NEG_E & (nCS | IsFinalBit); //то есть, старт всегда по TXen, а по RXen можно получить ответ, что же там было!
	//assign busy = ((TXen & (~NEG_E))|(RXen & (~POS_E))) & (nCS | IsFinalBit);
	//правда, и такой вариант не очень, т.к возникнет задержка до следующего слова. 
				
	lpm_counter StateCounter (.clock (clk),
				  .cnt_en (NEG_E & (~nCS)),
				  .sset (start), 
				  .Q (State),																		                               
                                  .cout (IsFinalBit));
	defparam
		StateCounter.lpm_direction = "UP",
		StateCounter.lpm_port_updown = "PORT_UNUSED",
		StateCounter.lpm_type = "LPM_COUNTER",
		StateCounter.lpm_width = 4,
		StateCounter.lpm_svalue = sB0;			                    
	

	assign MOSI = SR[8];
	assign Q = {{8{SR[7]}}, SR[7:1], MISO}; //старший (15) бит дублирует 7-й, т.к удобно проверять - как "знак". 
				
	always @(posedge clk) begin
		SCK <= ce? ~SCK : SCK;
		if (NEG_E)
			SR[8:1] <= start? D[7:0]: SR[7:0];
		if (POS_E)
			SR[0] <= MISO;
	end
	
	assign ceo = ce & SCK; //просто сэкономили 1 бит в делителях частоты, поскольку мы ОЧЕНЬ жадные.
endmodule


Как ни странно, там уже практически было два "независимых" сигнала busy, один для передачи, другой для приёма, соединённые по "ИЛИ".

И доделываем полную схему:


Теперь компилируем программу:
Загружаем файл конфигурации транслятора
Файл конфигурации прочитан, готовы к работе
Обрабатываем файл HelloEthernet.asm
Пытаемся оптимизировать таблицу вызова процедур

Бит 3 адреса
Всегда ноль...
Бит 2 адреса
Всегда ноль...
Бит 1 адреса
Всегда ноль...
Бит 0 адреса
Всегда ноль...
4 входных бит оказались не сопоставленными битам адреса
Извините, данный код ещё не готов...
Компиляция завершена успешно

Ширина адреса сегмента кода (и регистра PC):           4  
Ширина адреса сегмента данных (и регистров X,Y,Z,SP):  4  
Количество инициализированных слов данных:             15 
Количество инициализированных слов кода:               9  
Количество адресов процедур:                           0  

Адреса процедур:


Тут, ВНЕЗАПНО, вообще процедуры не вызываются, мы предполагаем, что этот код будет идти даже перед инициализацией стека. Как бы, настройка тактовой частоты - ещё более срочная вещь :)

Настраиваем параметры QuatCore: RamWidth=4, RomWidth=4, и синтезируем повторно. Успешно: 506 ЛЭ на всю схему (ядро+UART+SPI), тайминги соблюдаются, 25,32 МГц. Теперь запускаем симуляцию...

И там начинает творится что-то очень странное: мы только-только отправили на передачу первый байт (а их в первой посылке должно быть 4), как вдруг у нас "зажигалось" DestStall=1, и мы замирали на длительное время, непонятно зачем, в общем, чудеса, да и только. Потом "со свежей головой" сообразил, в чём было дело. Команда "IN" уже попала "в конвейер", несмотря на прыжок, а мы забыли добавить в QuatCoreIOselector входы SrcDiscard и SrcStall! Поэтому мы не поняли, что эту команду IN исполнять не надо, не настало ещё её время!

Пора это исправить. Если SrcDiscard=1, то мы игнорируем команду IN, здесь всё просто. Для SrcStall=1 пока что надёжнее всего будет поступить так же. Пример: мы опрашиваем АЦП, для чего сначала посылаем байт, где содержится номер канала, и тут же должны получить "назад" отсчёт АЦП, нечто такое:
OUT		[Z+k]	
i		IN	
OUT		0	
Y		IN


И поскольку команда "на запись" (DestAddr) выполняется с запозданием на 1 такт, то выйдет, что OUT и IN выполняются ОДНОВРЕМЕННО! Но OUT "застрянет" в ожидании, когда будет передан ПРЕДЫДУЩИЙ байт, либо выполнится МГНОВЕННО, если такового нет. А команда IN должна дожидаться, пока завершится именно ТЕКУЩИЙ обмен, поэтому, чтобы не получилось "фальстарта", она действительно должна "сидеть и не рыпаться", пока ожидается передача предыдущего байта.

Так что не будем пока умничать и сделаем, что SrcDiscard и SrcStall работают одинаково. Буквально одну строчку QuatCoreIOselector нужно доработать:

wire isIO_in = (SrcAddr[7:3] == 5'b1001_0)&(~SrcStall)&(~SrcDiscard);


На "общей схеме QuatCore" (ядро+периферия) нужно подключить провода SrcStall и SrcDiscard:



Всё это нормально синтезируется в 493 ЛЭ, тайминги выдержаны (27,86 МГц, ВАУ), сейчас для проверки приводим листинг программы:
    main proc
0  FD00          @@yetagain: SP      EthDisable
1  2002                  SIO     ETH
2  A1F3                  j       [SP++]  ;количество посылок (счёт с нуля)
3  A2F3          @@EthWord:  k       [SP++]  ;количество байт в посылке (счёт с нуля)
4  00F3          @@EthByte:  OUT     [SP++]
5  AA7D                  kLOOP       @@EthByte
6  A290                  k       IN      ;лишние регистры не "засоряем", хотя пофиг скорее всего
7  A97A                  jLOOP       @@EthWord
8  B008          @@endless:  JMP     @@endless           
    main endp

(какая прелесть, 9 строк, 18 байт)

и запускаем симуляцию:


Очень даже неплохо: указатель стека инициализируется нулём (мы его пока не для стека используем, а чтобы по всей строке пройтись), настраиваем ввод-вывод конкретно на Ethernet-контроллер, и это у нас получается, как видно, ведь именно ETH_nCS переключился в ноль, а остальные (ADC_nCS и SD_nCS) остались единичными.

Задали j=2 (то есть, у нас 3 посылки) и k=3 (в первой посылке 4 байта), загрузили первый байт к отправке: 0x22. Командой OUT мы запускаем SPI. Застреваем на этом аж на 3 такта, так уж наш модуль SPI устроен, что дожидается спада SCK (а он "тикает" сам по себе, чтобы выполнить требования для SD-карточки, которой тут пока и в помине нет), и только после этого "отпускает" процессор, позволяет ему дальше выполнять команды. Ничего шибко страшного.

И мы видим, что затем ETH_nCS устанавливается в ноль, и если посмотреть на значения MOSI на каждом фронте SCK, то там и будет бит за битом передано 0x22, как мы и просили. Причём значения устанавливаются "заблаговременно", по спадам SCK, в общем, всё как надо.

А процессор тем временем выполняет прыжок в начало цикла "передачи байтов", при этом у нас "мелькают" команды IN, k, jLOOP (всё это - после выхода из внутреннего цикла), но все они оказываются проигнорированы - СЕЙЧАС их исполнять не нужно!

Наконец, [SP++] срабатывает - мы загружаем на шину данных новое значение к передаче, 0x54, и затем на OUT мы надолго застреваем - ведь предыдущая посылка ещё передаётся. И как только передача одного байта, 0x22 завершилась, ТУТ ЖЕ, БЕСШОВНО начинается передача следующего, 0x54, а процессор "сдвигается с мёртвой точки". И снова прыжок, опять множество отменённых команд, которые попали "по инерции", [SP++] подгружает очередной байт, 0x00, и мы снова ждём окончания посылки предыдущего байта.

Следующий слайд!


Видим, что байт 0x54 тоже передался успешно, за ним началась передача байта 0x00, а тем временем мы подгрузили следующий байт, 0x01. Внутренний цикл работает правильно, уже радость. Ещё на этой осциллограмме видно, как UART закончил передавать нулевой байт, так вот криво у нас конвейер инициализируется при подаче питания, что запускается команда на передачу нулевого байта. Ну и пущай, когда-нибудь поправим...

Отмотаем чуть дальше, где мы начали передачу 0x01 и вышли из внутреннего цикла:


Передача началась, kLOOP определил, что переход уже не нужен, т.к k=0, и он "дал слово" команде IN. Она "остановилась" в ожидании, когда же мы передадим байт 0x01 и тем временем примем один байт "взамен". По 8-му фронту SCK, когда на MISO уже должен был появиться последний бит, ожидание закончилось. На шину данных ушли "все нули" (к MISO ничего не было подключено), и мы пошли дальше.

Зачем-то мы этот "ответ" записали в регистр k, хотя могли бы поставить NOP - разницы никакой. Тем временем на шине данных приготовили адрес для продолжения внешнего цикла, по j, и команда jLOOP осуществила этот прыжок, уменьшив j на единицу, теперь j=1. На шинах SrcAddr и DestAddr проскочили команды, идущие уже после цикла по j, в том числе нули из неиницализированной области памяти, но ничего из этого не исполнилось, поскольку в нужные моменты "зажигались" SrcDiscard=1 и DestStall=1.

Наконец, мы перешли в начало внешнего цикла, записали количество байт в нашей текущей посылке (опять k=3, что означает: 4 байта!). И теперь мы передаём эту посылку, начиная с первого байта, это опять 0x22.

Если мы понаблюдаем за выходом SPI, то увидим: ETH_nCS успел на короткое время переключиться в единицу, что корректно завершило первую 4-байтную посылку. Первый байт, 0x22 - это код команды "запись в выбранный регистр", 0x54 - адрес этого регистра, MIREGADR, а вот затем идёт запись байта за байтом с автоинкрементом, то есть в адрес 0x54 запишется 0x00, в 0x55 запишется 0x01, а не прерви мы эту посылку, т.е не выставь nCS=1, то и следующая посылка не воспринималась бы как очередная запись, а записала бы 0x22 по адресу 0x56, затем 0x66 по адресу 0x57 и т.д. Весь смысл двух вложенных циклов и команды IN был в том, чтобы разделить эти байты на отдельные посылки, между которыми есть небольшие паузы.

Ну а дальше идёт передача уже следующей посылки, байт за байтом. Похоже, что всё хорошо.

Давайте ещё посмотрим, чем всё это заканчивается:


Всё в порядке: передали последнюю посылку, после чего ETH_nCS установился в единицу, а процессор вошёл в "бесконечный цикл", которым эта программа и заканчивается.

Осталось проверить на железе...
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

Recent Posts from This Journal

  • Ремонт лыжных мостиков

    Вернулся с сегодняшнего субботника. Очень продуктивно: отремонтировали все ТРИ мостика! Правда, для этого надо было разделиться, благо народу…

  • Гетто-байк

    В субботу во время Великой Октябрьской резни бензопилой умудрился петуха сломать в велосипеде. По счастью, уже на следующий день удалось купить…

  • А всё-таки есть польза от ковариаций

    Вчера опробовал "сценарий", когда варьируем дальность от 1 метра до 11 метров. Получилось, что грамотное усреднение - это взять с огромными весами…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments