Z80 Finite State Machine

Микропроцессоры и микроконтроллеры от фирмы Zilog, а также компьютеры на них построенные

Moderator: Shaos

User avatar
HardWareMan
Banned
Posts: 2139
Joined: 20 Mar 2005 13:41
Location: От туда

Post by HardWareMan »

pfgx wrote:Выходные буферы #MREQ, #IORQ, #RD, #WR управляются через RS-триггеры, так что иголок быть не должно, но имеется интересная особенность: при переводе в Z-состояние у выходных буферов отключается только верхний транзистор, то есть RS-триггер при этом должен находиться в состоянии 1. Находится ли он в нём?
Т.е., при активации BUSR процессор должен встать в пассивной позиции иначе мы получаем ОК на шинах управления? И, соответственно, захват шины обрабатывается в строго определенный момент времени (предполагаю непосредственно перед М1)?
pfgx
Senior
Posts: 137
Joined: 20 Mar 2013 03:36
Location: Ростов-на-Дону

Post by pfgx »

HardWareMan wrote:Т.е., при активации BUSR процессор должен встать в пассивной позиции иначе мы получаем ОК на шинах управления?
Да, именно так. Надо будет в Verilog учесть этот ОК, спасибо за замечание.
HardWareMan wrote:И, соответственно, захват шины обрабатывается в строго определенный момент времени (предполагаю непосредственно перед М1)?
Выходной драйвер #BUSACK и отключение верхнего транзистора управляются одним и тем же сигналом, то есть если #BUSACK=0 - транзистор отключен. Сигнал активируется в какой-то сложный момент времени, но на эти же триггеры кроме #RESET, #BUSRQ и #CLK заведён сигнал 609 - завершение M-циклов текущей операции и переход к новой (M1).
pfgx
Senior
Posts: 137
Joined: 20 Mar 2013 03:36
Location: Ростов-на-Дону

Post by pfgx »

Понял, что Z80 слишком сложен для "посмотреть как устроен процессор", да и в целом уже стало понятно как это делается, но постараюсь завершить начатое.

Пробую писать на Verilog. Из черновика (код нерабочий, в нём точно есть ошибки :) ):

Code: Select all

module mod_state (
	input wire [200:131] w,
	input wire w127,
	input wire clk,
	input wire w_reset,
	output reg [6:1] Tstate,
	output reg [5:1] Mstate
);

	wire w249;

	wire w609; // w609=1 - начать новый M-цикл
	wire w609a, w609b, w609c;
	assign w609a = Tstate[3] & Mstate[2]; // по 3 такта только M2
	assign w609b = Tstate[4] & Mstate[1] & w[155]; // по 4 такта только M1 и только для w155
	assign w609c = Tstate[5] & Mstate[5] & w249; // по 5 тактов только M5 для w249
	assign w609 = w609a | w609b | w609c | Tstate[6] | w_reset; // M-цикл всегда заканчивается при T=6 или при reset=1

	always @(negedge clk)
	begin
		if (w609) Tstate='b000001;
		else Tstate[6:1]={Tstate[5:1],1'b0}; // сдвиг на 1 разряд
	end


	wire w627; // w627=1 - перейти на M1 // приоритет у перехода на M1
	wire w626; // w626=1 - перейти на M4

	// перейти на M1 (то есть начать новую команду)
	wire w627a,w627b,w627c;
	assign w627a = Mstate[1] & w[171] & w[172] & w[173] & w[177]; // при M1 только для этих команд
	assign w627b = Mstate[2] & (~w[172]) & w[173] & w[199] & (0|w[177]); // при M2 только для этих
	assign w627c = Mstate[3] & (w127 | (w[158] & (w[175] | (~w[156]))) ); // при M3 для этих
	assign w627 = w627a | w627b | w627c | Mstate[5] | w_reset ; // переход на M1 также всегда при M5 и при reset
	// команд, завершающихся на M4, не бывает
	// M3 это (для определённых команд) чтение байта смещения и расчёт адреса (5 тактов) для IX/IY+offset и относительных переходов

	// перейти на М4 
	// т.е. пропуск M3 (чтения байта смещения и вычисления адреса) и иногда M2 (если w[172]=1)
	wire w626a,w626b;
	assign w626a = (Mstate[1] & w[172]) | Mstate[2]; // такие пропуски бывают на M1 для w172 и на M2
	assign w626b = w[173]&(~w[177]) | ((0)&w[177]); // дополнительное условие такого пропуска
	assign w626 = w626a & w626b;


	always @(negedge clk)
	begin
		if (w627) Mstate='b00001; // перейти на M1
		else
		if (w626) Mstate='b01000; // перейти на M4
		else 
			Mstate[5:1]={Mstate[4:1],1'b0}; // сдвиг на 1 разряд
	end

endmodule
User avatar
Lavr
Supreme God
Posts: 16689
Joined: 21 Oct 2009 08:08
Location: Россия

Post by Lavr »

pfgx wrote:Понял, что Z80 слишком сложен для "посмотреть как устроен процессор", да и в целом уже стало понятно как это делается, но постараюсь завершить начатое.
Да ты огромнейшую работу проделал! Я даже не думал, что это лишь для того,
чтобы "посмотреть как устроен процессор"! :o

Ты материалы всё же где-нибуть выложи свои - чтобы пройденной тобой дорогой
никому не пришлось бы пройти сначала...
iLavr
pfgx
Senior
Posts: 137
Joined: 20 Mar 2013 03:36
Location: Ростов-на-Дону

Post by pfgx »

Да, я не понимал как сделать вот такую большую схему, в которой одновременно работает несколько блоков, и как их между собой согласовать. Решил посмотреть как оно в реальном процессоре, заодно разобраться с недокументированными командами. Теперь пытаюсь переложить это на Verilog.

Материалы выложу обязательно. А идущих этой дорогой много, я смотрю народ основательно взялся за NES, PlayStation и прочее, что можно рассмотреть в микроскоп :)
pfgx
Senior
Posts: 137
Joined: 20 Mar 2013 03:36
Location: Ростов-на-Дону

Post by pfgx »

Поработал, отдохнул, продолжаю :)

Вот есть эти счётчики (которые на самом деле сдвиговые регистры) тактов и циклов. Строб записи инструкции в буфер сделан просто: M1&T3. То есть в момент наступления 3-го такта цикла M1 инструкция с шины защёлкивается в буфер. Допустим, последний цикл предыдущей инструкции был длиной 3 такта, тогда при очередном перепаде тактового сигнала сбросится T3, установится T1, сбросится Mn и установится M1, но произойдёт это практически одновременно, неизвестно в каком порядке, и возможна ситуация, когда T3 ещё не сбросился, а M1 уже установлен, что вызовет "иголку" в стробе записи. Весь вечер пересматривал схему, но то ли я чего-то не замечаю, то ли эта иголка не влияет на работу, то ли случайно (или не совсем случайно) на кристалле получилось так, что T3 сбрасывается раньше. В любом случае в Verilog это надо учесть. Например, сделать так:

Code: Select all

	reg cmdclock,cmdclock2;
	always @(posedge clock) cmdclock2 = (Mstate[1]) & (Tstate[2]);
	always @(negedge clock) cmdclock = cmdclock2;
pfgx
Senior
Posts: 137
Joined: 20 Mar 2013 03:36
Location: Ростов-на-Дону

Post by pfgx »

Счётчики для беспрефиксных опкодов уже почти работают, основные проблемы с опкодами С0h...FFh, так как там есть исключения и много условных команд, длительность которых может изменяться.

Попробую перевести следующую часть схемы:

Code: Select all

	// декодер для управления блоком регистров
	wire [399:301] w3;
	assign w3[331] = ~((w[51] & w211 & (w205 | w204)) | (w2[240] & ((w204 & w209) | (w205 & w210))));
	assign w3[342] = ~(w211 | (w209 & w206));
	assign w3[346] = ~(w3[331] & w3[332]);
	assign w3[347] = ~(w3[337] & w3[338]);
	assign w3[348] = ~(w3[339] & w3[340]);
	...
pfgx
Senior
Posts: 137
Joined: 20 Mar 2013 03:36
Location: Ростов-на-Дону

Post by pfgx »

Вся дальнейшая логика сильно привязана к выходам счётчика тактов-циклов, но в этой "шине" счётчиков кроме 10 уже известных проводов есть ещё провод под условным номером 201 и расположенный как бы отдельно от них провод под номером 212. После этой логики ещё один "слой", который и формирует сигналы выборки-чтения-записи регистров.

Ещё в процессоре есть провод 612, который часто используется, но пока совершенно непонятно откуда он идёт и зачем он нужен.

Code: Select all

	// декодер для управления блоком регистров
	wire [399:301] w3;
	assign w3[301] = ~((w1[199] & w209 & w209) | (w211 & ((w202 & 0) | (w203 & (0 | 0))))); // TODO проверить 209-209
	assign w3[302] = ~((w[51] & w205 & (w209 | w211)) | (M1 & w208 & w2[237]));
	assign w3[303] = ~((w205 & w210) | (w211 & ((w203 & w2[252]) | (w204 & w[51]))));
	assign w3[304] = ~((M1 & w208 & w2[252]) | (w202 & w211 & ~w2[244]));
	assign w3[305] = ~(M1 & w211 & w[80]); // 80 - ex AF,AF'
	assign w3[306] = ~((M1 & w210 & w1[183]) | (w1[199] & w204 & w207) | (w203 & w209 & w2[236]));
	assign w3[307] = ~((w209 & (w202 | (w204 & w2[251]))) | (w205 & w211 & (w2[296] | w2[297])));
	assign w3[308] = ~((w210 & w204 & (w2[246] | w[51])) | (w209 & ((w205 & ~w[51] & w2[251]) | (w203 & w2[261]))));
	assign w3[309] = ~(w210 & M1 & w[130]);
	assign w3[310] = ~(M1 & w208 & w[87]); // какая-то ED
	assign w3[311] = ~((M1 & w208 & w2[260]) | (w203 & w211 & (~w1[174] | w[94]))); // 94 - какая-то ED
	assign w3[312] = ~((M1 & w210 & w1[183]) | (M1 & w208 & w[97]) | (w203 & w209 & w1[176] & w2[236]) | (w203 & w211 & w2[236])); // 97 - сдвиги rlc, rrc, rlnc, rrnc
	assign w3[313] = ~((w211 | w209) & (w203 | w202) & w[99]); // 99 - push/pop
	assign w3[314] = ~((w209 & ((w205 & w[103]) | (w204 & w1[196]))) | (w211 & w205 & w2[298]) | (w207 & w206 & w[101]) | (w208 & ((w206 & w2[298]) | (w204 & w1[196]))));
	assign w3[315] = ~(w[105] & ((w204 | w210) | (w205 & w209)));
	assign w3[316] = ~((w205 & w209 & (w[106] | w[101])) | (w204 & w210 & w[101]) | (M1 & w208 & (w2[255] | w1[198])) | (M1 & w207 & w[103])); // 106 - ld R,byte
	assign w3[317] = ~((w1[198] & w209 & (w202 | w203)) | (w208 & ((w1[198] & w203) | (w[109] & w204))) | (w2[299] & w205 & w210)); // 109 - какая-то ED
	assign w3[318] = ~(w2[259] & ((w205 & w210) | (w204 & w209)));
	assign w3[319] = ~((w3[318] & w2[257] & (w202 | w203)) | (w211 & (w202 | w203) & (w2[250] | w[115] | w1[198])));
	assign w3[320] = ~(((w207 | w208) & (w202 | (M1 & (w[117] | w[118])))) | (w209 & w[119] & (w205 | w204))); // 117 - inc/dec RR; 118 - ???; 119 - ld RR,word
	assign w3[321] = ~((w205 & w210 & w[51]) | ((w[51] | w2[243]) & (w204 | M1) & (w207 | w208)));
	// 322...330 ???
	assign w3[331] = ~((w[51] & w211 & (w205 | w204)) | (w2[240] & ((w204 & w209) | (w205 & w210))));
	assign w3[332] = ~((w203 & w208 & w1[196]) | (w211 & (w202 | w203) & (w[50] | w2[254])));
	assign w3[333] = w204 | w2[256];
	assign w3[334] = ~((w210 & (M1 | (w204 & w2[243]))) | (M1 & w208) | (w209 & (w202 | (w204 & w1[196]))));
	assign w3[335] = ~(w204 | M1 | (w205 & w2[262]));
	assign w3[336] = ~w3[335];
	assign w3[337] = ~((w210 & ((w202 & w1[176] & w2[243]) | (w1[196] & w204))) | (M1 & w207 & (w[51] | ~w1[176])) | (M1 & w209));
	assign w3[338] = ~((w208 & (w202 | ((w204 | w203) & (w1[196] | w2[243])))) | (w210 & ((w205 & w2[263]) | (w203 & w2[264]))));
	assign w3[339] = ~(w211 & ((w205 & (w[51] | 0)) | (w203 & w2[243] & 0) | (w204 & 0))); // TODO
	assign w3[340] = ~((w204 & w209 & 0) | (w1[196] & w203 & (w209 | w211)) | (M1 & w208 & w2[267])); // "0" завязан на 612 // TODO
	assign w3[341] = ~((T4 & w[121]) | (w[115] & M5 & T3)); // 121 - ld SP,HL; 115 - ex [SP],HL
	assign w3[342] = ~(w211 | (w209 & M1));
	assign w3[343] = (w204 & w2[268]) | (~w2[269] & (w202 | w203));
	assign w3[344] = ~((w202 & (w1[199] | ~w1[176])) | (~w1[176] & w203) | (w[51] & w205) | (w204 & (w[51] | w1[196])));
	assign w3[345] = ~(w3[343] | w3[344]);
	assign w3[346] = ~(w3[331] & w3[332]);
	assign w3[347] = ~(w3[337] & w3[338]);
	assign w3[348] = ~(w3[339] & w3[340]);
//	assign w3[349] = 
//	assign w3[350] = 
	assign w3[351] = w1[185] ? ~w[56] : w[59]; // 56 - rst, 59 - ???
	assign w3[354] = ~(w3[311] & (w3[310] | w3[357]));
	assign w3[356] = ~(w3[309] & (w3[310] | ~w3[357]));
	assign w3[357] = w1[185] ? w[65] : ~w[63]; // 65 - 5-й бит регистра опкода; 63 - 4-й бит
//	assign w3[358] = 
//	assign w3[359] = 
	assign w3[360] = ~(w3[363] | ~w3[313] | (w3[309] & w3[311]));
	assign w3[361] = w1[185] ? w[66] : w[65]; // 65 - 5-й бит регистра опкода; 66 - 2-й бит
	assign w3[362] = ~(w3[316] & w3[317]);
	assign w3[363] = ~(w3[319] & w3[320]);
	assign w3[364] = ~(w3[362] | ((~w3[360] | w3[363]) & (~w3[357] | w3[361])));
	assign w3[365] = ~(w3[321] & w3[322]);
	assign w3[366] = ~(~w3[312] | ~w3[314] | ~w3[315] | w3[352] | w3[362] | ~w3[318] | w3[365]);
pfgx
Senior
Posts: 137
Joined: 20 Mar 2013 03:36
Location: Ростов-на-Дону

Post by pfgx »

pfgx wrote:Строб записи инструкции в буфер сделан просто: M1&T3
Ой, а там за этим AND ещё RS-триггер есть :)
User avatar
Stan
Banned
Posts: 397
Joined: 04 Jan 2013 10:09
Location: 95.24.178.158

Post by Stan »

А из Ваших разработок не видно пока, как выполняется команда DAD RP?
Судя по тому, что АЛУ 4-битное, там должно быть 4 прохода через АЛУ, нет?
pfgx
Senior
Posts: 137
Joined: 20 Mar 2013 03:36
Location: Ростов-на-Дону

Post by pfgx »

Пока не видно, но теоретически да, должно быть 4 прохода.
Выполняется она за 11 тактов, состоит из трёх M-циклов: M1 и M4 по 4 такта и M5 3 такта. На M4T3 и M5T3 есть строб записи в младший и старший байты.

Для желающих порассматривать черновики с кучей недоделок:
http://yadi.sk/d/Wg0bpuKAKzeu6
http://yadi.sk/d/I65stOaiKzehQ
pfgx
Senior
Posts: 137
Joined: 20 Mar 2013 03:36
Location: Ростов-на-Дону

Post by pfgx »

Вопрос. После PUSH AF, POP BC куда попадёт A? В регистр B или C? Может кто-нибудь проверить на реальном Z80?
User avatar
Stan
Banned
Posts: 397
Joined: 04 Jan 2013 10:09
Location: 95.24.178.158

Post by Stan »

В реальном К580ВМ80А - вроде как в B, a в С - флаги.
По крайней мере, так видно под отладчиком.

А что, у Z80 есть отличие в исполнении этих команд?

Нет, всё верно - Особенности процессора Z80:
1.11. Процессор Z-80 допускает проведение операций со стеком. На стек может помещаться только содержимое регистровых пар или индексных регистров. Для этих операций можно рассматривать регистры A и F как отдельную регистровую пару AF, состоящую из аккумулятора (старший байт) и набора флагов (младший байт) .
Коли аккумулятора А (старший байт), то он и попадет при РОР в В - тоже старший байт.
bigmax
Fanat
Posts: 79
Joined: 10 Feb 2014 03:37

Post by bigmax »

Я думаю, что не зря команду назвали PUSH AF а не PUSH FA. Но лучше конечно проверить.
pfgx
Senior
Posts: 137
Joined: 20 Mar 2013 03:36
Location: Ростов-на-Дону

Post by pfgx »

Оно вроде всё элементарно, но путаница небольшая есть, как и с RLC/RLNC, например.

Да, похоже что действительно AF, а не FA. Но тогда зачем происходит "refresh" регистра флагов на каждом M1T3? А может это у меня криво формируются сигналы чтения-записи регистров. Там же как: при рисовании одну стравленную перемычку где-то пропустил - всё поплыло, а кое-что даже инвертировалось.