nabbla (nabbla1) wrote,
nabbla
nabbla1

Categories:

QuatCore: очередной глюк со стеком

Сижу, дописываю инициализацию SD-карточки. В прошлый раз остановился на CMD8 и проверке первого байта ответа. Ввёл проверку ещё 4 байт "эха" - работает. Сделал, чтобы всего было 3 попытки на инициализацию карты, после чего сдаёмся (чтобы не спамить терминал) - работает.

Ввёл посылку CMD55 - работает, ответ корректный. Но в конце строки какой-то мусор, и мы входим в бесконечный цикл:

(скриншот сделан чуть позже, когда я попытался локализовать проблему и выкинул львиную долю информационного обмена)

После не столь долгих разбирательств оказалось, что ошибка опять "аппаратная", в смысле, что не в ассемблере, а в верилоге. Очень обидная ошибка - она появилась, когда я делал модуль QuatCoreMem более "сговорчивым" - одно исправил, а другое поломал, причём ошибка затаилась на довольно длительный срок, никак не выявляя себя...


С самого начала мне очень подозрительным показалось следующее в ассемблерном коде:

OKCMD55	dw	'CMD55 success',13,0x800A
.data
	Stack		dw	?,?,?,?,?


То есть, запоролись ПОСЛЕДНИЕ СИМВОЛЫ строки, вплотную прилегающей к стеку!

Собственно, поэтому и случился бесконечный цикл - потёрлась единичка в старшем разряде, которая должна нас останавливать, поэтому так и крутимся по ближайшим 32 словам.

Следующий вопрос - а почему стек уполз не в ту сторону? Первым подозреваемым была процедура SendSD, где я опять играюсь со стеком, но потом, когда я вообще перестал её вызывать, проблема сохранилась. Та же история с "обработкой исключений" - до них выполнение попросту не доходило!

Симулятор позволил пролить свет на проблему: из указателя стека ВНЕЗАПНО вычиталась единичка на команде
X   CMD0strAdr

причём CMD0strAdr транслируется в литерал 0x77.
То есть мы просим поместить вполне конкретное значение в регистр X, а "попутно" вдруг из стека вычитается единица.

Вот код нашего модуля QuatCoreMemDecoder:

module QuatCoreMemDecoder (input [7:0] DestAddr, input [7:0] SrcAddr, input stall,
			output MemWrite, output SrcSquareBrac, output WriteX, output WriteY, output WriteZ, output WriteSP, output CountSP, output SPup,
			output [1:0] BaseAddr, output [1:0] FirstIndex, output [1:0] SecondIndex	);
							
wire isDest = (DestAddr[7:6] == 2'b11);
wire DestSquareBrac = (DestAddr[3:0] != 4'b1101);
assign MemWrite = isDest & DestSquareBrac;

assign BaseAddr = 	MemWrite? DestAddr[5:4] : SrcAddr[5:4];
assign FirstIndex = 	MemWrite? DestAddr[1:0] : SrcAddr[1:0];
assign SecondIndex = 	MemWrite? DestAddr[3:2] : SrcAddr[3:2];

assign SrcSquareBrac = (SrcAddr[3:0] != 4'b1101);

assign WriteX = isDest & (~DestSquareBrac) & (DestAddr[5:4] == 2'b00);
assign WriteY = isDest & (~DestSquareBrac) & (DestAddr[5:4] == 2'b01);
assign WriteZ = isDest & (~DestSquareBrac) & (DestAddr[5:4] == 2'b10);
assign WriteSP = isDest & (~DestSquareBrac) & (DestAddr[5:4] == 2'b11);

wire isSource = (SrcAddr[7:6] == 2'b11);
assign CountSP = (~stall) & (isDest | isSource) & (BaseAddr == 2'b11) & (FirstIndex == 2'b11);
assign SPup = ~SecondIndex[0];
													
endmodule


И ошибка в выражении для CountSP - если здесь появляется единичка, то реверсивный счётчик, в котором хранится SP, начинает счёт вверх или вниз, в зависимости от выражения SPup (нужно ли считать ВВЕРХ?).

Для команды, которая вызвала ошибку, DestAddr = 0xCD = 1100_1101, а SrcAddr = 0x77 = 0111_0111.

Смотрим, как идёт "декодирование". isDest = 1 (проверяются старшие два бита DestAddr), то есть назначение данных - модуль памяти.
DestSquareBrac = 0, т.е в мнемоническом коде команды нет квадратных скобок, т.е запись надо делать не в память, а в регистр.
MemWrite = 0, т.е запись в память не делается.

И вот дальше возникает проблема. Провода BaseAddr / FirstIndex / SecondIndex управляют формирователем эффективного адреса. Раньше там стояли такие выражения:
assign BaseAddr = 	isDest? DestAddr[5:4] : SrcAddr[5:4];
assign FirstIndex = 	isDest? DestAddr[1:0] : SrcAddr[1:0];
assign SecondIndex = 	isDest? DestAddr[3:2] : SrcAddr[3:2];


И хотя это сильно ограничивало наши возможности - нельзя было поместить в регистр значение из памяти, но по крайней мере стек работал правильно :)

А сейчас, раз мы не ведём запись в память, то от щедрот своих передаём формирователь эффективного адреса для чтения из памяти. Причём мы не проверяем, нужно ли действительно читать из памяти, если не нужно, то мультиплексор QuatCoreSrcMux попросту проигнорирует наши данные.

Вот и получается, что BaseAddr = 11, что соответствует SP;
FirstIndex = 11, что в случае стека соответствует вычитанию единички для SP;
SecondIndex = 01, что в случае стека соответствует прибавлению нуля.

И теперь, при нахождении выражения CountSP, мы имеем:
(~stall)=1 (процессор никого не ждёт!),
(isDest | isSrc) = 1 (в нашем случае isDest=1),
BaseAddr == 2'b11 и
FirstIndex = 2'b11.

То есть, выполнены все условия для счёта SP.

Раньше это не срабатывало, поскольку мы не забирались в "отрицательную" область памяти, где сплошные единички в адресах. Но вот залезли наконец - и нарвались.

Исправление довольно простое, в одну строку:
assign CountSP = (~stall) & ((isDest & DestAddr[5] & DestAddr[4] & DestAddr[1] & DestAddr[0]) | (isSource & SrcAddr[5] & SrcAddr[4] & SrcAddr[1] & SrcAddr[0]));


С ним декодер с 17 ЛЭ разбухает аж до 18 ЛЭ, не нравится мне, когда так получается, но это запоздалое следствие расширения функциональности, когда мы разрешили перемещать данные из памяти в базовые регистры и наоборот.


Кажись, исправили:

Осталось ещё 2 команды выполнить, одна запросит High Capacity, вторая узнает, эта карточка High Capacity (свыше 2 Гб) или обычная. Если ещё багов в процессоре не вылезет - сделаем наконец-то...

UPD. Ага, и с размаху налетел на ещё одну "особенность". Хотел сделать, чтобы входные данные можно было сразу в аккумулятор запихивать или даже какие-то действия с ними делать, для чего добавил вход stall в АЛУ, и думал, этого хватит.
А вот добавить вход stall в устройства ввода-вывода забыл! В итоге, АЛУ честно дожидалось, когда мы примем очередной байт, а когда приняли его - начало упихивать в аккумулятор, при этом SrcAddr оставался тот же самый, и мы напрочь зависли, ожидая, когда же будет принят ещё один байт, а он не будет принят, потому что мы не запустили передачу! Лаадно, ща подправим...
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