262 lines
7.0 KiB
Verilog
262 lines
7.0 KiB
Verilog
`timescale 1ns / 1ps
|
|
//Praktikum TGDI WS 09/10
|
|
//In dieser Datei soll die FPU implementiert werden.
|
|
//Diese Datei bitte als Lsung einschicken.
|
|
|
|
module fpu(
|
|
input clk, //Takt
|
|
input reset, //Reset-Signal
|
|
input[31:0] instruction, //aktueller Befehl
|
|
input[31:0] mem_readdata, //Lesedaten vom Speicher zum Registersatz (fuer lwc1)
|
|
output[31:0] mem_writedata, //Schreibdaten vom Registersatz (Port 1) an Speicher (fuer swc1)
|
|
|
|
input regdst, //Gibt an, welcher Teil des Befehls als Adresse fuer den Write Port
|
|
//des FPU-Registersatzes verwendet wird
|
|
//0: Zielregister aus dem FP-Befehl
|
|
//1: instruction[20:16] (lwc1, swc1)
|
|
input fpuregwritemux, //Gibt an, woher die Daten am Write Port des Registersatzes stammen
|
|
//0: Daten ist Ergebnis aus FPU
|
|
//1: Daten aus Speicher
|
|
|
|
input fpu_regwrite //Write_enable des FPU-Registersatzes
|
|
);
|
|
|
|
wire [31:0] mem_data1, mem_data2;
|
|
wire [31:0] res_add, res_sub, res_mult;
|
|
wire [31:0] dummy_1;
|
|
|
|
wire notwrite;
|
|
|
|
assign notwrite = 0;
|
|
|
|
//lese daten aus fpu-memory
|
|
float_memory RAM( clk,
|
|
notwrite,
|
|
instruction[15:11], instruction[20:16], instruction[10:6], //2xquellregister,1xZiel
|
|
mem_readdata, //does not write!
|
|
mem_data1, mem_data2);
|
|
|
|
//calculate values
|
|
float_add adder(mem_data1,mem_data2,res_add);
|
|
float_sub subber(mem_data1,mem_data2,res_sub);
|
|
float_mult multer(mem_data1,mem_data2,res_mult);
|
|
|
|
reg [31:0] result;
|
|
wire [31:0] swc1_res;
|
|
|
|
always@(*)
|
|
begin
|
|
//float-berechnung
|
|
case(instruction[5:0]) //Funktion, wleche aufgerufen wird
|
|
6'b000000 : result = res_add; //addition
|
|
6'b000001 : result = res_sub; //subtraktion
|
|
6'b000010 : result = res_mult; //multiplikation
|
|
default: result = res_add; //default = addieren
|
|
endcase
|
|
|
|
if(regdst & fpuregwritemux & fpu_regwrite) //lwc1-befehl
|
|
result = mem_readdata; //ergebnis = eingegebene daten
|
|
end
|
|
|
|
//schreibe memory -> bei swc1-befehl oder wenn float-berechnung durchgeführt wurde!
|
|
float_memory write_mem( clk,
|
|
fpu_regwrite,
|
|
instruction[15:11], instruction[20:16], instruction[10:6],
|
|
result,
|
|
swc1_res,dummy_1); //lese daten aus speicher
|
|
|
|
//Output: entweder result bei float/lwc1 oder swc1_res bei swc1
|
|
assign mem_writedata = (regdst & !fpuregwritemux & !fpu_regwrite)/*swc1*/ ? swc1_res : result /*float / lwc1*/;
|
|
|
|
endmodule
|
|
|
|
//Registersatz
|
|
module float_memory(
|
|
input clk, //Clock
|
|
input write_enable, //schreibe ja/nein
|
|
input [4:0] read_addr1, read_addr2, write_addr, //lese und schreibaddressen
|
|
input [31:0] write_data, //daten zum schreiben
|
|
output[31:0] read_data1,read_data2 //ausgelesene daten
|
|
);
|
|
|
|
//Speicher
|
|
reg [31:0] RAM[31:0];
|
|
|
|
//schreiben
|
|
always@(posedge clk)
|
|
if(write_enable & write_addr != 0)
|
|
RAM[write_addr] <= write_data;
|
|
|
|
//lesen
|
|
assign read_data1 = RAM[read_addr1];
|
|
assign read_data2 = RAM[read_addr2];
|
|
|
|
endmodule
|
|
|
|
//Float-Multiplikator
|
|
module float_mult(
|
|
input [31:0] read_data1,read_data2, //Arbeitsdaten
|
|
output [31:0] result); //achtung, rundet die Zahl
|
|
|
|
wire vz1;
|
|
wire [10:0] exp1;
|
|
reg [20:0] mant1;
|
|
|
|
wire vz2;
|
|
wire [10:0] exp2;
|
|
reg [20:0] mant2;
|
|
|
|
reg [10:0] shiftamount;
|
|
|
|
reg [10:0] new_exp;
|
|
reg new_vz;
|
|
reg [41:0] new_mant;
|
|
|
|
//assigns
|
|
//teile read_data1 auf in vz1, exp1
|
|
assign vz1 = read_data1[31];
|
|
assign exp1 = read_data1[30:20];
|
|
//teile read_data2 auf in vz2, exp2
|
|
assign vz2 = read_data2[31];
|
|
assign exp2 = read_data2[30:20];
|
|
|
|
//always
|
|
always@(*)
|
|
begin
|
|
//mantisse um 1 erweitern
|
|
mant1[20] = 1;
|
|
mant1[19:0] = read_data1[19:0];
|
|
//mantisse um 1 erweitern
|
|
mant2[20] = 1;
|
|
mant2[19:0] = read_data2[19:0];
|
|
|
|
//exponent
|
|
new_exp = exp1 + (exp2 - 1023); //nur 1x bias! (11 bit deshalb bias=1023)
|
|
|
|
//vorzeichen
|
|
if(vz1 == vz2)
|
|
new_vz = 0; //+
|
|
else
|
|
new_vz = 1; //-
|
|
|
|
//mantisse
|
|
new_mant = mant1 * mant2; //auf 42 bit gemapt
|
|
|
|
//shift back -> jetzt steht die erste 1 in der 21 stelle = new_mant[20]
|
|
if(new_mant[41] == 1) //höchstes bit ist gesetzt?
|
|
begin
|
|
new_mant = new_mant >> 20; //schifte um 20
|
|
new_exp = new_exp + 20; //addiere 20 auf den exp
|
|
end else
|
|
begin
|
|
new_mant = new_mant >> 19; //oberstes bit ist nicht gesetzt = zweithöchstes bit ist gesetzt, shifte um 19
|
|
new_exp = new_exp + 19; //addiere 19 auf exp
|
|
end
|
|
end
|
|
|
|
assign result[31] = new_vz; //neues vorzeichen
|
|
assign result[30:20] = new_exp; //neuer exponent
|
|
assign result[19:0] = new_mant[19:0]; //neue mantisse
|
|
|
|
endmodule
|
|
|
|
//Addierer
|
|
module float_add(
|
|
input [31:0] read_data1, read_data2, //Arbeitsdaten
|
|
output [31:0] result); //Ergebniss
|
|
|
|
wire vz1;
|
|
wire [10:0] exp1;
|
|
reg [20:0] mant1;
|
|
|
|
wire vz2;
|
|
wire [10:0] exp2;
|
|
reg [20:0] mant2;
|
|
|
|
reg [10:0] shiftamount;
|
|
|
|
reg [10:0] new_exp;
|
|
reg new_vz;
|
|
reg [21:0] new_mant;
|
|
|
|
//assigns
|
|
//teile read_data1 auf in vz1, exp1
|
|
assign vz1 = read_data1[31];
|
|
assign exp1 = read_data1[30:20];
|
|
//teile read_data2 auf in vz2, exp2
|
|
assign vz2 = read_data2[31];
|
|
assign exp2 = read_data2[30:20];
|
|
|
|
//always
|
|
always@(*)
|
|
begin
|
|
//mantisse um 1 erweitern
|
|
mant1[20] = 1;
|
|
mant1[19:0] = read_data1[19:0];
|
|
//mantisse um 1 erweitern
|
|
mant2[20] = 1;
|
|
mant2[19:0] = read_data2[19:0];
|
|
|
|
//exponentenvergleich, new_exp = größerer exp, shifte kleinere Zahl
|
|
if(exp1 >= exp2)
|
|
begin
|
|
shiftamount = exp1 - exp2;
|
|
mant2 = mant2 >> shiftamount[10:0];
|
|
new_exp = exp1;
|
|
end else
|
|
begin
|
|
shiftamount[10:0] = exp2 - exp1;
|
|
mant1 = mant1 >> shiftamount[10:0];
|
|
new_exp = exp2;
|
|
end
|
|
|
|
//vorzeichen
|
|
if(mant1 >= mant2)
|
|
begin
|
|
new_vz = vz1;
|
|
end else
|
|
begin
|
|
new_vz = vz2;
|
|
end
|
|
|
|
if(vz1 == vz2) //vorzeichen sind gleich
|
|
begin
|
|
new_mant[21:0] = mant1 + mant2; //addiere
|
|
end else
|
|
begin
|
|
if(vz1 > vz2) //read_data1 ist negativ
|
|
begin
|
|
new_mant[21:0] = mant2 - mant1;
|
|
end else //read_data2 ist negativ
|
|
begin
|
|
new_mant[21:0] = mant1 - mant2;
|
|
end
|
|
end
|
|
|
|
if(new_mant[21] == 1) //übertrag -> zurückshiften
|
|
begin
|
|
new_mant = new_mant >> 1;
|
|
new_exp = new_exp + 1;
|
|
end
|
|
end
|
|
|
|
//zusammen bauen
|
|
assign result[31] = new_vz;
|
|
assign result[30:20] = new_exp;
|
|
assign result[19:0] = new_mant[19:0];
|
|
endmodule
|
|
|
|
//subtrahierer
|
|
module float_sub(
|
|
input[31:0] read_data1, read_data2, //Arbeitsdaten
|
|
output [31:0] result); //Ergebniss
|
|
|
|
wire [31:0] data2;
|
|
|
|
assign data2[30:0] = read_data2[30:0];
|
|
assign data2[31] = (read_data2[31] == 1) ? 0 : 1; //vertausche vorzeichen von read_data2
|
|
|
|
//Addiere read_data1 und read_data2(mit geändertem vorzeichen)
|
|
float_add add(read_data1, data2,result);
|
|
endmodule
|