2011-10-18 11:47:32 +02:00

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