nabbla (nabbla1) wrote,
nabbla
nabbla1

Разгоняем QuatCore до 25 МГц, часть 6

Осталось модернизировать ещё два модуля, QuatCoreALU (арифметическое устройство) и QuatCoreMem (работа с памятью), чтобы они корректно вели себя в конвейере.

Начнём с АЛУ. Как следует из очень упрощённой схемы, его часть, отвечающая за SrcAddr, вообще в наших конвейерных делах не участвует. У неё нет "побочных эффектов" (наподобие [SP++] или CALLn, где помимо вывода информации на шину данных, совершаются дополнительные действия), нет задержки выдачи данных, поэтому можно игнорировать SrcStall и SrcDiscard. Так что остаётся лишь часть, отвечающая за DestAddr.




И как это ни странно, сейчас АЛУ переделывать не надо СОВСЕМ. В нём и так был реализован один вход stall, которым я хотел "подружить" АЛУ с устройствами ввода-вывода, но в тот момент не смог - они блокировали друг друга "до скончания времён". И выход busy, который работал идеально :)

На всякий случай, вот код модуля QuatCoreALUcontrol, на сегодняшний день:

//most complicated thing so far. Should support commands:
//NOP (smth else is active) 
//0x80: ACC
//0x81: PM
//0x82: ADD
//0x83: SUB

//0x84: ABS
//0x85: ???
//0x86: ???
//0x87: ???

//0x88: ZACC
//0x89: ???
//0x8A: C
//0x8B: ???

//0x8C: DIV2
//0x8D: ???
//0x8E: ???
//0x8F: ???

//0x90: MUL
//0x91: FMPM
//0x92: FMA
//0x93: FMS

//0x94: MULSU
//0x95: SUFMPM
//0x96: SUFMA
//0x97: SUFMS

//0x98: MULU
//0x99: UFMPM
//0x9A: UFMA
//0x9B: UFMS

//0x9C: SQRD2
//0x9D: SQRPMD2
//0x9E: SQRAD2
//0x9F: SQRSD2

//100xxxxx = ALU commands
//1000xxxx = 'short' commands
//1001xxxx = 'long' commands

//100xxx00 = 'clear ACC'
//100xxx10 = 'add to ACC'
//100xxx11 = 'sub from ACC'
//100xxx01 = 'plus-minus' (depends on PM input)

//100x00xx = 'signed commands'
//100x01xx = 'signed B / unsigned C commands / special command (ABS)'
//100x10xx = 'unsigned commands / special commands (ZAcc, C)'
//100x11xx = 'div2 commands (DIV2, SQRD2, SQRAD2 etc)'

//AccMode = 	000 - load
//		001 - FORBIDDEN
//      	010 - idle
//		011 - clear
//		100 - set -3/2
//		101 - set 2
//		110 - set 3/2
//		111 - set 1/2lsb

//Bmode   =	00 - shift right
//		01 - shift right and invert
//		10 - load 16 bit
//		11 - load AccWidth bit (bypass)

//Cmode   = 	0x - idle
//		10 - shift
//		11 - load
module QuatCoreALUcontrol (input Bypass, input clk, input [7:0] DestAddr, input Csenior, input PM, input [15:0] D,input stall,
			output [2:0] AccMode, output cin, output BSigned, output [1:0] Bmode, output [1:0] Cmode, output Sena, output Busy);
							
wire isOurAddr	   = (DestAddr[7:5] == 3'b100)&(~stall);
wire isLongCommand = DestAddr[4];
wire [1:0] PlusMinusMode = DestAddr[1:0];

wire OpSign = PlusMinusMode[0] & (PlusMinusMode[1] | PM); //0 is '+', 1 is '-'
wire isSignedC = (DestAddr[3] == DestAddr[2]);

reg SeniorDataBit = 1'b0;
always @(posedge clk) if (isIdle)
	SeniorDataBit <= D[15];

wire FirstDivSign = OpSign ^ ((isLongCommand & isSignedC & (isFirstDiv | MulFirst)) | (SeniorDataBit & (~isLongCommand) & (~DestAddr[3]) & DestAddr[2]));


//first of all, 'state machine'
localparam sIdle = 3'b000;
localparam sDiv2 = 3'b001;
localparam sFirstDiv = 3'b010;
localparam sAdd  = 3'b011;
localparam sMult = 3'b100;

reg [2:0] State = sIdle;


wire isIdle = (State == sIdle);
wire isFirstDiv = (State == sFirstDiv);
wire isDiv2 = (State == sDiv2);
wire isAdd = (State == sAdd);
wire isMult = (State == sMult);

reg MulFirst = 1'b0;
always @(posedge clk)
	MulFirst <= isFirstDiv;

	

//also, 4-bit counter for multiplier
wire finished; //when multiplication is all over
 lpm_counter	counter (
			.clock (clk),
//			.cnt_en (IsIdle),
			.sclr (~isMult),
			.cout (finished) );
  defparam
    counter.lpm_direction = "UP",
    counter.lpm_port_updown = "PORT_UNUSED",
    counter.lpm_type = "LPM_COUNTER",
    counter.lpm_width = 4;


//now combinatorial logic which depends on current state, DestAddr and other inputs
assign AccMode = 	isIdle? 				((isOurAddr & (PlusMinusMode == 2'b00))? {1'b1, D[1:0]} : 3'b010) : //либо команда ZAcc, либо храним текущее значение
					(isFirstDiv | isDiv2)?	{isLongCommand & (PlusMinusMode == 2'b00), 1'b1, (PlusMinusMode == 2'b00)}:
					isAdd?					3'b000:
											{1'b0, ~Csenior, 1'b0};
//in 'clear' commands we either don't care of contents at first step (will clear it anyway),
//or this is ZAcc command which sets one of consts. So we seek for this command
//otherwise AccMode is idle.

//so far this is only for idle
							
assign cin = FirstDivSign; //don't care at idle/FirstDiv/Div2, care at sAdd so far

assign BSigned = 	(DestAddr[3:2] != 2'b10) | (~isFirstDiv); //this row equals fully unsigned operation
//assign BSigned = (DestAddr[3:2] != 2'b10) & (isLongCommand | (~DestAddr[1])); //making one and only operation DIV2S into UDIV2S.
							
assign Bmode = 	isIdle? 	{1'b1, Bypass}:
		isFirstDiv?	{1'b0, FirstDivSign} :
				{1'b0, isSignedC & MulFirst};
					
					
assign Cmode[1] = isIdle? isOurAddr & DestAddr[3] & ((~DestAddr[4]&DestAddr[1])|(DestAddr[4]&DestAddr[2])) : isMult; //1 LE shorter still
//assign Cmode[1] = isIdle? isOurAddr & DestAddr[3] & DestAddr[2] & (DestAddr[1]|isLongCommand) : isMult;
									
assign Cmode[0] = ~isMult;

assign Sena    = 	isOurAddr & DestAddr[0] | isMult;

assign Busy = isOurAddr & (DestAddr[4:2] != 3'b0_10)&(~isAdd)&(~finished);

always @(posedge clk)
	State <= 	isIdle? 		(Busy? ((DestAddr[3:2] == 2'b11)? sDiv2 : sFirstDiv) : sIdle):
			isDiv2?			sFirstDiv:
			isFirstDiv? 		(isLongCommand? sMult : sAdd):
			(isAdd | finished)?	sIdle : State;

endmodule


Модуль QuatCoreMem надо немножко подправить:


На него идёт аж ТРИ разных блокировочных сигнала. Но встраиваются они довольно легко, вот код QuatCoreFastMemDecoder:

//приготовление эффективного адреса и получение данных по этому адресу - процесс небыстрый,
//поэтому разбиваем его на 2 такта. Т.е если у нас запрашивают ЧТЕНИЕ из памяти, то включаем busy на 1 такт

//на удивление сложное поведение внутри конвейера, нужно различать 3 разных управляющих сигнала:
//DestStall запрещает запись в регистры, память и инкременты/декременты SP, по части DestAddr
//SrcStall запрещает инкременты/декременты SP, но как ни в чём не бывало формирует правильное значение в DataBus. Когда будет SrcStall=0, мы тут же выдаём ответ
//SrcDiscard отличается тем, что после сброса в 0 мы выжидаем ещё 1 такт, прежде чем выдать ответ, т.к не уверены, что адрес команды остался тем же. 

module QuatCoreFastMemDecoder (input clk, input [7:0] DestAddr, input [7:0] SrcAddr, input SrcStall, input DestStall, input SrcDiscard,
				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, output busy	);

reg fetching = 1'b0;
							
wire isDest = (~DestStall)&(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 = (~SrcDiscard)&(~SrcStall)&(SrcAddr[7:6] == 2'b11);
//assign CountSP = (~stall) & (isDest | isSource) & (BaseAddr == 2'b11) & (FirstIndex == 2'b11); таило в себе ошибку!
assign CountSP = ((isDest & DestAddr[5] & DestAddr[4] & DestAddr[1] & DestAddr[0]) | (isSource & SrcAddr[5] & SrcAddr[4] & SrcAddr[1] & SrcAddr[0]));
assign SPup = ~SecondIndex[0];

assign busy = isSource & SrcSquareBrac & (~fetching);

//осталось с ним сообразить. Когда приходит SrcAddr[7:6] == 2'b11 и SrcDiscard = 0, то невзирая на SrcStall, мы уже начали выборку! 
//и значит, когда нам "позволят" работать, уже получим результат!
//но ещё, когда SrcStall=0 и fetching=1, он должен сброситься опять в ноль,
//чтобы идущая сразу следом команда не могла ошибочно выдать результат СРАЗУ
always @(posedge clk)
	fetching <= (~SrcDiscard)&(SrcAddr[7:6] == 2'b11)&((~fetching)|SrcStall);
													
endmodule


Последняя строка, с fetching - наименее понятная :) Много граничных условий. Когда SrcDiscard=1, для нас команда вообще как бы "не существует", считаем что она не наша. А вот если SrcDiscard=0, но SrcStall=1, то мы отчасти её признаём - выставляем к следующему такту fetching=1, что означает - на выходе к шине данных уже лежит правильное значение. Причём когда SrcStall=0 (никто нас не тормозит) и fetching=1, мы к следующему такту снова устанавливаем fetching=0, потому что такие условия означают, что к следующему такту нам могут уже дать другой адрес, а возможно и тот же самый адрес, но регистр уже поменялся, поэтому данные на выходе станут потенциально некорректными.

Вроде так...

Теперь посмотрим на схему всего QuatCore в целом:



По крайней мере, синтезируется, и то радость. И максимально допустимую частоту указывает 26,11 МГц. Осталось посмотреть - а работает ли он вообще? Самое время делать ставки!
Poll #2099886 FastQuatCore

Этот вариант с конвейером заработает?

Да
0(0.0%)
После внесения незначительных доработок
0(0.0%)
После коренной переделки
0(0.0%)
Не заработает вообще
0(0.0%)
Tags: ПЛИС, программки, работа, странные девайсы
Subscribe

  • Хотел выпендриться

    Одно из замечаний к моему протоколу информационного обмена: ДОБАВЬ 16-битные заголовки к каждому сообщению! Нам могут прислать командное слово с…

  • Моделирование стыковки с помощью ВидеоИзмерителя

    "Нарисовал"-таки видеоролик, где мы начинаем с весьма неблагоприятных условий: вместо того, чтобы нас поставить ровно "напротив" стыковочного узла,…

  • Более тяжёлые тела падают быстрее!

    Увидел не так давно видео от Flammable Maths с таким заголовком, и подумал поначалу - он опять нас троллит. Это немецкий препод математики…

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments