Initial load of files
This commit is contained in:
218
src_Core/BSV_Additional_Libs/ByteLane.bsv
Normal file
218
src_Core/BSV_Additional_Libs/ByteLane.bsv
Normal file
@@ -0,0 +1,218 @@
|
||||
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package ByteLane;
|
||||
|
||||
// ================================================================
|
||||
// Library for 'adjusting/unadjusting' bytes on a wider bus
|
||||
|
||||
// Example: a 64b bit bus can be regarded as 8 "byte lanes"
|
||||
// with each lane selected by the lower 3 bits of an address.
|
||||
// Such a bus typically carries data of 1, 2, 4 or 8 bytes.
|
||||
// For 1,2, and 4, different apps use one of two representations:
|
||||
// - carry the data on contiguous lower-order bytes, with a separate
|
||||
// indication of how many bytes are being carried (1,2,4)
|
||||
// - carry the data bytes on the lanes corresponding to the byte addresses,
|
||||
// along with a 'mask' indicating which bytes are valid:
|
||||
// Lane: 7 6 5 4 3 2 1 0
|
||||
// x x x x D D D D 4-byte data, address lsbs = 3'b000
|
||||
// D D D D x x x x 4-byte data, address lsbs = 3'b100
|
||||
//
|
||||
// x x x x x x D D 2-byte data, address lsbs = 3'b000
|
||||
// x x x x D D x x 2-byte data, address lsbs = 3'b010
|
||||
// ...
|
||||
// D D x x x x x x 2-byte data, address lsbs = 3'b110
|
||||
//
|
||||
// x x x x x x x D 1-byte data, address lsbs = 3'b000
|
||||
// x x x x x x D x 1-byte data, address lsbs = 3'b001
|
||||
// ...
|
||||
// D x x x x x x x 1-byte data, address lsbs = 3'b111
|
||||
// Only 1 bit/lane is needed for the 'mask' (a.k.a. 'byte-enable' or 'strobe')
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Vector :: *;
|
||||
|
||||
// ================================================================
|
||||
// Expand each bit in 'strobe' to a byte, creating a full-width mask.
|
||||
|
||||
function Bit #(TMul #(n,8)) fn_strobe_to_mask (Bit #(n) strobe);
|
||||
function Bit #(8) fn_bit_j_to_byte_j (Integer j);
|
||||
return signExtend (strobe [j]);
|
||||
endfunction
|
||||
|
||||
Vector #(n, Bit #(8)) v = genWith (fn_bit_j_to_byte_j);
|
||||
return pack (v);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Update an n-byte word taking into account an n-bit strobe
|
||||
|
||||
function Bit# (TMul #(n,8)) fn_update_strobed_bytes (Bit# (TMul #(n,8)) old_data,
|
||||
Bit# (TMul #(n,8)) new_data,
|
||||
Bit #(n) strobe);
|
||||
Bit# (TMul #(n,8)) mask = fn_strobe_to_mask (strobe);
|
||||
return ((old_data & (~ mask)) | (new_data & mask));
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Lane-adjust a data word according to the byte-address and data width
|
||||
// Args: addr: only bottom few bits are relevant
|
||||
// width: # of relevant bytes: 1, 2, 4, 8
|
||||
// data_in: lsb-justified data
|
||||
// Results: Bool err: misaligned, or bad data_width
|
||||
// strobe: bit vector showing which lanes are active
|
||||
// data_out: data shifted to be in-lane
|
||||
|
||||
// ----------------
|
||||
// 32b version
|
||||
|
||||
function Tuple3 #(Bool, //
|
||||
Bit #(4), // strobe
|
||||
Bit #(32)) // lane-adjusted data
|
||||
fn_lane_adjust_32b (Bit #(32) addr, Bit #(3) dw, Bit #(32) data);
|
||||
|
||||
Bit #(4) strobe = 0;
|
||||
Bool err = False;
|
||||
|
||||
case (dw)
|
||||
1: case (addr [1:0])
|
||||
2'b00: begin strobe = 'b_0001; end
|
||||
2'b01: begin strobe = 'b_0010; data = (data << 8); end
|
||||
2'b10: begin strobe = 'b_0100; data = (data << 16); end
|
||||
2'b11: begin strobe = 'b_1000; data = (data << 24); end
|
||||
endcase
|
||||
2: case (addr [1:0])
|
||||
2'b00: begin strobe = 'b_0011; end
|
||||
2'b10: begin strobe = 'b_1100; data = (data << 16); end
|
||||
default: err = True;
|
||||
endcase
|
||||
4: case (addr [1:0])
|
||||
2'b00: strobe = 'b_1111;
|
||||
default: err = True;
|
||||
endcase
|
||||
default: err = True;
|
||||
endcase
|
||||
return tuple3 (err, strobe, data);
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// 64b version
|
||||
|
||||
function Tuple3 #(Bool, // err: misaligned, or bad data_width
|
||||
Bit #(8), // strobe
|
||||
Bit #(64)) // lane-adjusted data
|
||||
fn_lane_adjust_64b (Bit #(64) addr, Bit #(4) dw, Bit #(64) data);
|
||||
|
||||
Bit #(8) strobe = 0;
|
||||
Bool err = False;
|
||||
|
||||
case (dw)
|
||||
1: case (addr [2:0])
|
||||
3'b000: begin strobe = 'b_0000_0001; end
|
||||
3'b001: begin strobe = 'b_0000_0010; data = (data << 8); end
|
||||
3'b010: begin strobe = 'b_0000_0100; data = (data << 16); end
|
||||
3'b011: begin strobe = 'b_0000_1000; data = (data << 24); end
|
||||
3'b100: begin strobe = 'b_0001_0000; data = (data << 32); end
|
||||
3'b101: begin strobe = 'b_0010_0000; data = (data << 40); end
|
||||
3'b110: begin strobe = 'b_0100_0000; data = (data << 48); end
|
||||
3'b111: begin strobe = 'b_1000_0000; data = (data << 56); end
|
||||
endcase
|
||||
2: case (addr [2:0])
|
||||
3'b000: begin strobe = 'b_0000_0011; end
|
||||
3'b010: begin strobe = 'b_0000_1100; data = (data << 16); end
|
||||
3'b100: begin strobe = 'b_0011_0000; data = (data << 32); end
|
||||
3'b110: begin strobe = 'b_1100_0000; data = (data << 48); end
|
||||
default: err = True;
|
||||
endcase
|
||||
3: case (addr [2:0])
|
||||
3'b000: begin strobe = 'b_0000_1111; end
|
||||
3'b100: begin strobe = 'b_1111_0000; data = (data << 32); end
|
||||
default: err = True;
|
||||
endcase
|
||||
4: case (addr [2:0])
|
||||
3'b000: begin strobe = 'b_1111_1111; end
|
||||
default: err = True;
|
||||
endcase
|
||||
default: err = True;
|
||||
endcase
|
||||
return tuple3 (err, strobe, data);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Lane-unadjust a data word according to the byte-address and data width
|
||||
|
||||
// ----------------
|
||||
// 32b version
|
||||
|
||||
function Tuple2 #(Bool, // err: misaligned, or bad data_width
|
||||
Bit #(32)) // lane-unadjusted data
|
||||
fn_lane_unadjust_32b (Bit #(32) addr, Bit #(3) dw, Bit #(32) data);
|
||||
|
||||
Bool err = False;
|
||||
|
||||
case (dw)
|
||||
1: case (addr [1:0])
|
||||
2'b00: data = (data >> 0);
|
||||
2'b01: data = (data >> 8);
|
||||
2'b10: data = (data >> 16);
|
||||
2'b11: data = (data >> 24);
|
||||
endcase
|
||||
2: case (addr [1:0])
|
||||
2'b00: data = (data >> 0);
|
||||
2'b10: data = (data >> 16);
|
||||
default: err = True;
|
||||
endcase
|
||||
4: case (addr [1:0])
|
||||
2'b00: data = (data >> 0);
|
||||
default: err = True;
|
||||
endcase
|
||||
default: err = True;
|
||||
endcase
|
||||
return tuple2 (err, data);
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// 64b version
|
||||
|
||||
function Tuple2 #(Bool, // err: misaligned, or bad data_width
|
||||
Bit #(64)) // lane-unadjusted data
|
||||
fn_lane_unadjust_64b (Bit #(64) addr, Bit #(4) dw, Bit #(64) data);
|
||||
|
||||
Bool err = False;
|
||||
|
||||
case (dw)
|
||||
1: case (addr [2:0])
|
||||
3'b000: data = (data >> 0);
|
||||
3'b001: data = (data >> 8);
|
||||
3'b010: data = (data >> 16);
|
||||
3'b011: data = (data >> 24);
|
||||
3'b100: data = (data >> 32);
|
||||
3'b101: data = (data >> 40);
|
||||
3'b110: data = (data >> 48);
|
||||
3'b111: data = (data >> 56);
|
||||
endcase
|
||||
2: case (addr [2:0])
|
||||
3'b000: data = (data >> 0);
|
||||
3'b010: data = (data >> 16);
|
||||
3'b100: data = (data >> 32);
|
||||
3'b110: data = (data >> 48);
|
||||
default: err = True;
|
||||
endcase
|
||||
4: case (addr [2:0])
|
||||
3'b000: data = (data >> 0);
|
||||
3'b100: data = (data >> 32);
|
||||
default: err = True;
|
||||
endcase
|
||||
8: case (addr [2:0])
|
||||
3'b000: data = (data >> 0);
|
||||
default: err = True;
|
||||
endcase
|
||||
default: err = True;
|
||||
endcase
|
||||
return tuple2 (err, data);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
63
src_Core/BSV_Additional_Libs/CreditCounter.bsv
Normal file
63
src_Core/BSV_Additional_Libs/CreditCounter.bsv
Normal file
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) 2015-2019 Bluespec, Inc., All Rights Reserved
|
||||
// Author: Rishiyur S. Nikhil
|
||||
|
||||
// A "credit counter" which can be incremented and decremented concurrently.
|
||||
|
||||
package CreditCounter;
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
// ================================================================
|
||||
// Interface
|
||||
|
||||
interface CreditCounter_IFC #(numeric type w);
|
||||
// Current value of internal count
|
||||
method UInt #(w) value;
|
||||
|
||||
// Increment internal count
|
||||
method Action incr;
|
||||
|
||||
// Decrement internal count
|
||||
method Action decr;
|
||||
|
||||
// Clear internal count to 0
|
||||
method Action clear;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
// Module implementation
|
||||
// Scheduling: value < incr < decr < clear
|
||||
|
||||
module mkCreditCounter (CreditCounter_IFC #(w));
|
||||
|
||||
Reg #(UInt #(w)) crg [3] <- mkCReg (3, 0);
|
||||
|
||||
method UInt #(w) value = crg [1];
|
||||
|
||||
method Action incr;
|
||||
if (crg [0] == maxBound) begin
|
||||
$display ("%0d: ERROR: CreditCounter: overflow", cur_cycle);
|
||||
$finish (1); // Assertion failure
|
||||
end
|
||||
crg [0] <= crg [0] + 1;
|
||||
endmethod
|
||||
|
||||
method Action decr () if (crg [1] != 0);
|
||||
if (crg [1] == 0) begin
|
||||
$display ("%0d: ERROR: CreditCounter: underflow", cur_cycle);
|
||||
$finish (1); // Assertion failure
|
||||
end
|
||||
crg [1] <= crg [1] - 1;
|
||||
endmethod
|
||||
|
||||
method Action clear;
|
||||
crg [2] <= 0;
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
15
src_Core/BSV_Additional_Libs/Cur_Cycle.bsv
Normal file
15
src_Core/BSV_Additional_Libs/Cur_Cycle.bsv
Normal file
@@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package Cur_Cycle;
|
||||
|
||||
// ================================================================
|
||||
// A convenience function to return the current cycle number during BSV simulations
|
||||
|
||||
ActionValue #(Bit #(32)) cur_cycle = actionvalue
|
||||
Bit #(32) t <- $stime;
|
||||
return t / 10;
|
||||
endactionvalue;
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
119
src_Core/BSV_Additional_Libs/EdgeFIFOFs.bsv
Normal file
119
src_Core/BSV_Additional_Libs/EdgeFIFOFs.bsv
Normal file
@@ -0,0 +1,119 @@
|
||||
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
// EdgeFIFOFs are 1-element FIFOFs used as generic interfaces by IPs
|
||||
// where they connect to interconnect fabrics. The overall interface
|
||||
// is a standard FIFOF interface. The IP-side is guarded. The
|
||||
// Fabric-side is unguarded, so that it's easy to attach transactors
|
||||
// for some particular bus interface such as AXI4-Lite, AHB-Lite, etc.
|
||||
|
||||
|
||||
package EdgeFIFOFs;
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import FIFOF :: *;
|
||||
|
||||
// ================================================================
|
||||
// FIFOFs for Master IPs.
|
||||
// enq (IP-side) is guarded, deq (Fabric-side) is not.
|
||||
|
||||
module mkMaster_EdgeFIFOF (FIFOF #(t))
|
||||
provisos (Bits #(t, tsz));
|
||||
|
||||
Integer port_deq = 0;
|
||||
Integer port_enq = 1;
|
||||
Integer port_clear = 2;
|
||||
|
||||
Array #(Reg #(Bool)) crg_full <- mkCReg (3, False);
|
||||
Reg #(t) rg_payload <- mkRegU;
|
||||
|
||||
// ----------------
|
||||
// Clear
|
||||
|
||||
method Action clear;
|
||||
crg_full [port_clear] <= False;
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Enq side (IP-side; guarded)
|
||||
|
||||
method Bool notFull ();
|
||||
return (! crg_full [port_enq]);
|
||||
endmethod
|
||||
|
||||
method Action enq (t x) if (! crg_full [port_enq]);
|
||||
crg_full [port_enq] <= True;
|
||||
rg_payload <= x;
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Deq side (Fabric-side; unguarded)
|
||||
|
||||
method Bool notEmpty ();
|
||||
return crg_full [port_deq];
|
||||
endmethod
|
||||
|
||||
method t first (); // unguarded
|
||||
return rg_payload;
|
||||
endmethod
|
||||
|
||||
method Action deq (); // unguarded
|
||||
crg_full [port_deq] <= False;
|
||||
endmethod
|
||||
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
// For Slave IPs.
|
||||
// enq (Fabric-side) is unguarded, deq (IP-side) is guarded.
|
||||
|
||||
module mkSlave_EdgeFIFOF (FIFOF #(t))
|
||||
provisos (Bits #(t, tsz));
|
||||
|
||||
Integer port_deq = 0;
|
||||
Integer port_enq = 1;
|
||||
Integer port_clear = 2;
|
||||
|
||||
Array #(Reg #(Bool)) crg_full <- mkCReg (3, False);
|
||||
Reg #(t) rg_payload <- mkRegU;
|
||||
|
||||
// ----------------
|
||||
// Clear
|
||||
|
||||
method Action clear;
|
||||
crg_full [port_clear] <= False;
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Enq side (IP-side; unguarded)
|
||||
|
||||
method Bool notFull ();
|
||||
return (! crg_full [port_enq]);
|
||||
endmethod
|
||||
|
||||
method Action enq (t x); // unguarded
|
||||
crg_full [port_enq] <= True;
|
||||
rg_payload <= x;
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Deq side (Fabric-side; guarded)
|
||||
|
||||
method Bool notEmpty ();
|
||||
return crg_full [port_deq];
|
||||
endmethod
|
||||
|
||||
method t first () if (crg_full [port_deq]);
|
||||
return rg_payload;
|
||||
endmethod
|
||||
|
||||
method Action deq () if (crg_full [port_deq]);
|
||||
crg_full [port_deq] <= False;
|
||||
endmethod
|
||||
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
126
src_Core/BSV_Additional_Libs/GetPut_Aux.bsv
Normal file
126
src_Core/BSV_Additional_Libs/GetPut_Aux.bsv
Normal file
@@ -0,0 +1,126 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package GetPut_Aux;
|
||||
|
||||
// ================================================================
|
||||
// Misc. additional useful definitions on FIFOs, Gets and Puts
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
|
||||
// ================================================================
|
||||
// A convenience function to 'pop' a value from a FIFO, FIFOF, ...
|
||||
|
||||
function ActionValue #(t) pop (ifc f)
|
||||
provisos (ToGet #(ifc, t));
|
||||
return toGet (f).get;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// No-op stubs for Get, Put, Client and Server interfaces.
|
||||
// 'get' is never enabled.
|
||||
// 'put' is always enabled and just discards its argument.
|
||||
|
||||
Get #(t) getstub = interface Get;
|
||||
method ActionValue #(t) get () if (False);
|
||||
return ?;
|
||||
endmethod
|
||||
endinterface;
|
||||
|
||||
Put #(t) putstub = interface Put;
|
||||
method Action put (t x) if (True);
|
||||
noAction;
|
||||
endmethod
|
||||
endinterface;
|
||||
|
||||
Client #(t1,t2) client_stub = interface Client;
|
||||
interface request = getstub;
|
||||
interface response = putstub;
|
||||
endinterface;
|
||||
|
||||
Server #(t1,t2) server_stub = interface Server;
|
||||
interface request = putstub;
|
||||
interface response = getstub;
|
||||
endinterface;
|
||||
|
||||
// ================================================================
|
||||
// For debugging, a convenience function to display full/empty status of a FIFO
|
||||
|
||||
function Action fa_show_FIFOF_state (String s, FIFOF #(t) f);
|
||||
action
|
||||
$write ("%s:", s);
|
||||
if (! f.notEmpty) $display (" Empty");
|
||||
else if (! f.notFull) $display (" Full");
|
||||
else $display (" neither empty nor full");
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// DiscardFIFOF
|
||||
// enqueue side: always ready, and discards everything
|
||||
// dequeue side: always empty, never returns anything
|
||||
|
||||
module mkDiscardFIFOF (FIFOF #(t));
|
||||
method Action enq (t x);
|
||||
noAction;
|
||||
endmethod
|
||||
|
||||
method Bool notFull;
|
||||
return True;
|
||||
endmethod
|
||||
|
||||
method t first () if (False);
|
||||
return ?;
|
||||
endmethod
|
||||
|
||||
method Action deq () if (False);
|
||||
noAction;
|
||||
endmethod
|
||||
|
||||
method Bool notEmpty;
|
||||
return False;
|
||||
endmethod
|
||||
|
||||
method Action clear;
|
||||
noAction;
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
// dummy_FIFO interface
|
||||
// enqueue side: never ready;
|
||||
// dequeue side: never ready
|
||||
|
||||
FIFOF #(t) dummy_FIFOF = interface FIFOF;
|
||||
method Action enq (x) if (False);
|
||||
noAction;
|
||||
endmethod
|
||||
|
||||
method notFull;
|
||||
return False;
|
||||
endmethod
|
||||
|
||||
method first () if (False);
|
||||
return ?;
|
||||
endmethod
|
||||
|
||||
method Action deq () if (False);
|
||||
noAction;
|
||||
endmethod
|
||||
|
||||
method notEmpty;
|
||||
return False;
|
||||
endmethod
|
||||
|
||||
method Action clear;
|
||||
noAction;
|
||||
endmethod
|
||||
endinterface;
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
137
src_Core/BSV_Additional_Libs/Semi_FIFOF.bsv
Normal file
137
src_Core/BSV_Additional_Libs/Semi_FIFOF.bsv
Normal file
@@ -0,0 +1,137 @@
|
||||
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package Semi_FIFOF;
|
||||
|
||||
// ================================================================
|
||||
// Separate interfaces for input-side and output-side of FIFOF.
|
||||
// Conversion functions to these, from FIFOF interfaces.
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import FIFOF :: *;
|
||||
import Connectable :: *;
|
||||
|
||||
// ================================================================
|
||||
// Semi-FIFOF interfaces
|
||||
|
||||
interface FIFOF_I #(type t);
|
||||
method Action enq (t x);
|
||||
method Bool notFull ();
|
||||
endinterface
|
||||
|
||||
interface FIFOF_O #(type t);
|
||||
method t first ();
|
||||
method Action deq ();
|
||||
method Bool notEmpty ();
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
// Converters from FIFOF
|
||||
|
||||
function FIFOF_I #(t) to_FIFOF_I (FIFOF #(t) f);
|
||||
return interface FIFOF_I;
|
||||
method enq (x) = f.enq (x);
|
||||
method notFull = f.notFull;
|
||||
endinterface;
|
||||
endfunction
|
||||
|
||||
function FIFOF_O #(t) to_FIFOF_O (FIFOF #(t) f);
|
||||
return interface FIFOF_O;
|
||||
method first = f.first;
|
||||
method deq = f.deq;
|
||||
method notEmpty = f.notEmpty;
|
||||
endinterface;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Connections
|
||||
|
||||
// ----------------
|
||||
// FIFOF_O to FIFOF_I
|
||||
|
||||
instance Connectable #(FIFOF_O #(t), FIFOF_I #(t));
|
||||
module mkConnection #(FIFOF_O #(t) fo, FIFOF_I #(t) fi) (Empty);
|
||||
rule rl_connect;
|
||||
fi.enq (fo.first);
|
||||
fo.deq;
|
||||
endrule
|
||||
endmodule
|
||||
endinstance
|
||||
|
||||
// ----------------
|
||||
// FIFOF_I to FIFOF_O
|
||||
|
||||
instance Connectable #(FIFOF_I #(t), FIFOF_O #(t));
|
||||
module mkConnection #(FIFOF_I #(t) fi, FIFOF_O #(t) fo) (Empty);
|
||||
mkConnection (fo, fi);
|
||||
endmodule
|
||||
endinstance
|
||||
|
||||
// ----------------
|
||||
// FIFOF_O to FIFOF
|
||||
|
||||
instance Connectable #(FIFOF_O #(t), FIFOF #(t));
|
||||
module mkConnection #(FIFOF_O #(t) fo, FIFOF #(t) fi) (Empty);
|
||||
rule rl_connect;
|
||||
fi.enq (fo.first);
|
||||
fo.deq;
|
||||
endrule
|
||||
endmodule
|
||||
endinstance
|
||||
|
||||
// ----------------
|
||||
// FIFOF to FIFOF_I
|
||||
|
||||
instance Connectable #(FIFOF #(t), FIFOF_I #(t));
|
||||
module mkConnection #(FIFOF #(t) fo, FIFOF_I #(t) fi) (Empty);
|
||||
rule rl_connect;
|
||||
fi.enq (fo.first);
|
||||
fo.deq;
|
||||
endrule
|
||||
endmodule
|
||||
endinstance
|
||||
|
||||
// ================================================================
|
||||
// Convenience function combining first/enq
|
||||
|
||||
function ActionValue #(t) pop_o (FIFOF_O #(t) f);
|
||||
actionvalue
|
||||
f.deq;
|
||||
return f.first;
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Dummy tie-off interfaces
|
||||
|
||||
// dummy_FIFO_I that never accepts anything (always "full")
|
||||
|
||||
FIFOF_I #(t) dummy_FIFOF_I = interface FIFOF_I;
|
||||
method Action enq (x) if (False);
|
||||
noAction;
|
||||
endmethod
|
||||
method notFull;
|
||||
return False;
|
||||
endmethod
|
||||
endinterface;
|
||||
|
||||
// Dummy FIFO_O that never yields anything (always "empty")
|
||||
|
||||
FIFOF_O #(t) dummy_FIFOF_O = interface FIFOF_O;
|
||||
method first () if (False);
|
||||
return ?;
|
||||
endmethod
|
||||
|
||||
method Action deq () if (False);
|
||||
noAction;
|
||||
endmethod
|
||||
|
||||
method notEmpty;
|
||||
return False;
|
||||
endmethod
|
||||
endinterface;
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
977
src_Core/CPU/Core.bsv
Normal file
977
src_Core/CPU/Core.bsv
Normal file
@@ -0,0 +1,977 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// Portions Copyright (c) Bluespec, Inc.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
|
||||
import Vector::*;
|
||||
import BuildVector::*;
|
||||
import DefaultValue::*;
|
||||
import ClientServer::*;
|
||||
import GetPut::*;
|
||||
import Assert::*;
|
||||
import Cntrs::*;
|
||||
import ConfigReg::*;
|
||||
import FIFO::*;
|
||||
import Fifo::*;
|
||||
import Ehr::*;
|
||||
import Connectable::*;
|
||||
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import CacheUtils::*;
|
||||
import TlbTypes::*;
|
||||
import SynthParam::*;
|
||||
import VerificationPacket::*;
|
||||
import Performance::*;
|
||||
import HasSpecBits::*;
|
||||
import Exec::*;
|
||||
import FetchStage::*;
|
||||
import ITlb::*;
|
||||
import DTlb::*;
|
||||
import L2Tlb::*;
|
||||
import TlbConnect::*;
|
||||
import EpochManager::*;
|
||||
import PhysRFile::*;
|
||||
import RFileSynth::*;
|
||||
import RenamingTable::*;
|
||||
import ReorderBuffer::*;
|
||||
import ReorderBufferSynth::*;
|
||||
import Scoreboard::*;
|
||||
import ScoreboardSynth::*;
|
||||
import SpecTagManager::*;
|
||||
import Fpu::*;
|
||||
import MulDiv::*;
|
||||
import ReservationStationEhr::*;
|
||||
import ReservationStationAlu::*;
|
||||
import ReservationStationMem::*;
|
||||
import ReservationStationFpuMulDiv::*;
|
||||
import AluExePipeline::*;
|
||||
import FpuMulDivExePipeline::*;
|
||||
import MemExePipeline::*;
|
||||
import SplitLSQ::*;
|
||||
import StoreBuffer::*;
|
||||
import GlobalSpecUpdate::*;
|
||||
import CCTypes::*;
|
||||
import L1CoCache::*;
|
||||
import L1Bank::*;
|
||||
import IBank::*;
|
||||
import MMIOCore::*;
|
||||
import RenameStage::*;
|
||||
import CommitStage::*;
|
||||
import Bypass::*;
|
||||
|
||||
import CsrFile :: *;
|
||||
|
||||
interface CoreReq;
|
||||
method Action start(
|
||||
Addr startpc,
|
||||
Addr toHostAddr, Addr fromHostAddr
|
||||
);
|
||||
method Action perfReq(PerfLocation loc, PerfType t);
|
||||
endinterface
|
||||
|
||||
interface CoreIndInv;
|
||||
method ActionValue#(ProcPerfResp) perfResp;
|
||||
method ActionValue#(void) terminate;
|
||||
endinterface
|
||||
|
||||
interface CoreDeadlock;
|
||||
interface Get#(L1DCRqStuck) dCacheCRqStuck;
|
||||
interface Get#(L1DPRqStuck) dCachePRqStuck;
|
||||
interface Get#(L1ICRqStuck) iCacheCRqStuck;
|
||||
interface Get#(L1IPRqStuck) iCachePRqStuck;
|
||||
interface Get#(RenameStuck) renameInstStuck;
|
||||
interface Get#(RenameStuck) renameCorrectPathStuck;
|
||||
interface Get#(CommitStuck) commitInstStuck;
|
||||
interface Get#(CommitStuck) commitUserInstStuck;
|
||||
interface Get#(void) checkStarted;
|
||||
endinterface
|
||||
|
||||
interface CoreRenameDebug;
|
||||
interface Get#(RenameErrInfo) renameErr;
|
||||
endinterface
|
||||
|
||||
interface Core;
|
||||
// core request & indication
|
||||
interface CoreReq coreReq;
|
||||
interface CoreIndInv coreIndInv;
|
||||
// coherent caches to LLC
|
||||
interface ChildCacheToParent#(L1Way, void) dCacheToParent;
|
||||
interface ChildCacheToParent#(L1Way, void) iCacheToParent;
|
||||
// DMA to LLC
|
||||
interface TlbMemClient tlbToMem;
|
||||
// MMIO
|
||||
interface MMIOCoreToPlatform mmioToPlatform;
|
||||
// stats enable
|
||||
method ActionValue#(Bool) sendDoStats;
|
||||
method Action recvDoStats(Bool x);
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface CoreDeadlock deadlock;
|
||||
// debug rename
|
||||
interface CoreRenameDebug renameDebug;
|
||||
|
||||
// Bluespec: external interrupt requests targeting Machine and Supervisor modes
|
||||
method Action setMEIP (Bit #(1) v);
|
||||
method Action setSEIP (Bit #(1) v);
|
||||
endinterface
|
||||
|
||||
// fixpoint to instantiate modules
|
||||
interface CoreFixPoint;
|
||||
interface Vector#(AluExeNum, AluExePipeline) aluExeIfc;
|
||||
interface Vector#(FpuMulDivExeNum, FpuMulDivExePipeline) fpuMulDivExeIfc;
|
||||
interface MemExePipeline memExeIfc;
|
||||
method Action killAll; // kill everything: used by commit stage
|
||||
interface Reg#(Bool) doStatsIfc;
|
||||
endinterface
|
||||
|
||||
(* synthesize *)
|
||||
module mkCore#(CoreId coreId)(Core);
|
||||
let verbose = True;
|
||||
Reg#(Bool) outOfReset <- mkReg(False);
|
||||
rule rl_outOfReset if (!outOfReset);
|
||||
$fwrite(stderr, "mkProc came out of reset\n");
|
||||
outOfReset <= True;
|
||||
endrule
|
||||
|
||||
Reg#(Bool) started <- mkReg(False);
|
||||
|
||||
// front end
|
||||
FetchStage fetchStage <- mkFetchStage;
|
||||
ITlb iTlb = fetchStage.iTlbIfc;
|
||||
ICoCache iMem = fetchStage.iMemIfc;
|
||||
|
||||
// back end
|
||||
RFileSynth rf <- mkRFileSynth;
|
||||
|
||||
// Bluespec: CsrFile including external interrupt request methods
|
||||
CsrFile csrf <- mkCsrFile(zeroExtend(coreId)); // hartid in CSRF should be core id
|
||||
|
||||
RegRenamingTable regRenamingTable <- mkRegRenamingTable;
|
||||
EpochManager epochManager <- mkEpochManager;
|
||||
SpecTagManager specTagManager <- mkSpecTagManager;
|
||||
ReorderBufferSynth rob <- mkReorderBufferSynth;
|
||||
|
||||
// We have two scoreboards: one conservative and other aggressive
|
||||
// - Aggressive sb is checked at rename stage, so inst after rename may be issued early
|
||||
// - Conservative sb is checked at reg read stage, to ensure correctness
|
||||
// Every pipeline should set both sb if it needs to write reg
|
||||
// - Conservative sb is set when data is written into rf
|
||||
// - Aggressive sb is set when pipeline sends out wakeup for reservation staion
|
||||
// Note that wakeup can be sent early if it knows when the data will be produced
|
||||
ScoreboardCons sbCons <- mkScoreboardCons; // conservative sb
|
||||
ScoreboardAggr sbAggr <- mkScoreboardAggr; // aggressive sb
|
||||
|
||||
// MMIO: need to track in flight CSR inst or interrupt; note we can at most
|
||||
// 1 CSR inst or 1 interrupt in ROB, so just use 1 bit track it. Commit
|
||||
// stage use port 0 to reset this, and Rename stage use port 1 to set this.
|
||||
Ehr#(2, Bool) csrInstOrInterruptInflight <- mkEhr(False);
|
||||
Reg#(Bool) csrInstOrInterruptInflight_commit = csrInstOrInterruptInflight[0];
|
||||
Reg#(Bool) csrInstOrInterruptInflight_rename = csrInstOrInterruptInflight[1];
|
||||
MMIOCoreInput mmioInIfc = (interface MMIOCoreInput;
|
||||
interface fetch = fetchStage.mmioIfc;
|
||||
method getMSIP = csrf.getMSIP;
|
||||
method setMSIP = csrf.setMSIP;
|
||||
method setMTIP = csrf.setMTIP;
|
||||
method noInflightCSRInstOrInterrupt = !csrInstOrInterruptInflight[0];
|
||||
method setTime = csrf.setTime;
|
||||
endinterface);
|
||||
MMIOCore mmio <- mkMMIOCore(mmioInIfc);
|
||||
|
||||
// fix point module to instantiate other function units
|
||||
module mkCoreFixPoint#(CoreFixPoint fix)(CoreFixPoint);
|
||||
// spec update
|
||||
Vector#(AluExeNum, SpeculationUpdate) aluSpecUpdate;
|
||||
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
|
||||
aluSpecUpdate[i] = fix.aluExeIfc[i].specUpdate;
|
||||
end
|
||||
Vector#(FpuMulDivExeNum, SpeculationUpdate) fpuMulDivSpecUpdate;
|
||||
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
|
||||
fpuMulDivSpecUpdate[i] = fix.fpuMulDivExeIfc[i].specUpdate;
|
||||
end
|
||||
GlobalSpecUpdate#(CorrectSpecPortNum, ConflictWrongSpecPortNum) globalSpecUpdate <- mkGlobalSpecUpdate(
|
||||
joinSpeculationUpdate(
|
||||
append(append(vec(regRenamingTable.specUpdate,
|
||||
specTagManager.specUpdate,
|
||||
fix.memExeIfc.specUpdate), aluSpecUpdate), fpuMulDivSpecUpdate)
|
||||
),
|
||||
rob.specUpdate
|
||||
);
|
||||
|
||||
// whether perf data is collected
|
||||
Reg#(Bool) doStatsReg <- mkConfigReg(False);
|
||||
|
||||
// redirect func
|
||||
//function Action redirectFunc(Addr trap_pc, Maybe#(SpecTag) spec_tag, InstTag inst_tag );
|
||||
//action
|
||||
// if (verbose) $fdisplay(stdout, "[redirect_action] new pc = 0x%8x, spec_tag = ", trap_pc, fshow(spec_tag));
|
||||
// epochManager.redirect;
|
||||
// fetchStage.redirect(trap_pc);
|
||||
// if (spec_tag matches tagged Valid .valid_spec_tag) begin
|
||||
// globalSpecUpdate.incorrectSpec(valid_spec_tag, inst_tag);
|
||||
// end
|
||||
//endaction
|
||||
//endfunction
|
||||
|
||||
// write aggressive elements + wakupe reservation stations
|
||||
function Action writeAggr(Integer wrAggrPort, PhyRIndx dst);
|
||||
action
|
||||
sbAggr.setReady[wrAggrPort].put(dst);
|
||||
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
|
||||
fix.aluExeIfc[i].rsAluIfc.setRegReady[wrAggrPort].put(Valid (dst));
|
||||
end
|
||||
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
|
||||
fix.fpuMulDivExeIfc[i].rsFpuMulDivIfc.setRegReady[wrAggrPort].put(Valid (dst));
|
||||
end
|
||||
fix.memExeIfc.rsMemIfc.setRegReady[wrAggrPort].put(Valid (dst));
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// write conservative elements
|
||||
function Action writeCons(Integer wrConsPort, PhyRIndx dst, Data data);
|
||||
action
|
||||
rf.write[wrConsPort].wr(dst, data);
|
||||
sbCons.setReady[wrConsPort].put(dst);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
Vector#(AluExeNum, FIFO#(FetchTrainBP)) trainBPQ <- replicateM(mkFIFO);
|
||||
Vector#(AluExeNum, AluExePipeline) aluExe;
|
||||
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
|
||||
Vector#(2, SendBypass) sendBypassIfc; // exe and finish
|
||||
for(Integer sendPort = 0; sendPort < 2; sendPort = sendPort + 1) begin
|
||||
sendBypassIfc[sendPort] = (interface SendBypass;
|
||||
method Action send(PhyRIndx dst, Data data);
|
||||
// broadcast bypass
|
||||
Integer recvPort = valueof(AluExeNum) * sendPort + i;
|
||||
for(Integer j = 0; j < valueof(FpuMulDivExeNum); j = j+1) begin
|
||||
fix.fpuMulDivExeIfc[j].recvBypass[recvPort].recv(dst, data);
|
||||
end
|
||||
fix.memExeIfc.recvBypass[recvPort].recv(dst, data);
|
||||
for(Integer j = 0; j < valueof(AluExeNum); j = j+1) begin
|
||||
fix.aluExeIfc[j].recvBypass[recvPort].recv(dst, data);
|
||||
end
|
||||
endmethod
|
||||
endinterface);
|
||||
end
|
||||
let aluExeInput = (interface AluExeInput;
|
||||
method sbCons_lazyLookup = sbCons.lazyLookup[aluRdPort(i)].get;
|
||||
method rf_rd1 = rf.read[aluRdPort(i)].rd1;
|
||||
method rf_rd2 = rf.read[aluRdPort(i)].rd2;
|
||||
method csrf_rd = csrf.rd;
|
||||
method rob_getPC = rob.getOrigPC[i].get;
|
||||
method rob_getPredPC = rob.getOrigPredPC[i].get;
|
||||
method rob_setExecuted = rob.setExecuted_doFinishAlu[i].set;
|
||||
method fetch_train_predictors = toPut(trainBPQ[i]).put;
|
||||
method setRegReadyAggr = writeAggr(aluWrAggrPort(i));
|
||||
interface sendBypass = sendBypassIfc;
|
||||
method writeRegFile = writeCons(aluWrConsPort(i));
|
||||
method Action redirect(Addr new_pc, SpecTag spec_tag, InstTag inst_tag);
|
||||
if (verbose) begin
|
||||
$display("[ALU redirect - %d] ", i, fshow(new_pc),
|
||||
"; ", fshow(spec_tag), "; ", fshow(inst_tag));
|
||||
end
|
||||
epochManager.incrementEpoch;
|
||||
fetchStage.redirect(new_pc);
|
||||
globalSpecUpdate.incorrectSpec(False, spec_tag, inst_tag);
|
||||
endmethod
|
||||
method correctSpec = globalSpecUpdate.correctSpec[finishAluCorrectSpecPort(i)].put;
|
||||
method doStats = doStatsReg._read;
|
||||
endinterface);
|
||||
aluExe[i] <- mkAluExePipeline(aluExeInput);
|
||||
// truly call fetch method to train branch predictor
|
||||
rule doFetchTrainBP;
|
||||
let train <- toGet(trainBPQ[i]).get;
|
||||
fetchStage.train_predictors(
|
||||
train.pc, train.nextPc, train.iType, train.taken,
|
||||
train.dpTrain, train.mispred
|
||||
);
|
||||
endrule
|
||||
end
|
||||
|
||||
Vector#(FpuMulDivExeNum, FpuMulDivExePipeline) fpuMulDivExe;
|
||||
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
|
||||
let fpuMulDivExeInput = (interface FpuMulDivExeInput;
|
||||
method sbCons_lazyLookup = sbCons.lazyLookup[fpuMulDivRdPort(i)].get;
|
||||
method rf_rd1 = rf.read[fpuMulDivRdPort(i)].rd1;
|
||||
method rf_rd2 = rf.read[fpuMulDivRdPort(i)].rd2;
|
||||
method rf_rd3 = rf.read[fpuMulDivRdPort(i)].rd3;
|
||||
method csrf_rd = csrf.rd;
|
||||
method rob_setExecuted = rob.setExecuted_doFinishFpuMulDiv[i].set;
|
||||
method Action writeRegFile(PhyRIndx dst, Data data);
|
||||
writeAggr(fpuMulDivWrAggrPort(i), dst);
|
||||
writeCons(fpuMulDivWrConsPort(i), dst, data);
|
||||
endmethod
|
||||
method conflictWrongSpec = globalSpecUpdate.conflictWrongSpec[finishFpuMulDivConflictWrongSpecPort(i)].put(?);
|
||||
method doStats = doStatsReg._read;
|
||||
endinterface);
|
||||
fpuMulDivExe[i] <- mkFpuMulDivExePipeline(fpuMulDivExeInput);
|
||||
end
|
||||
|
||||
let memExeInput = (interface MemExeInput;
|
||||
method sbCons_lazyLookup = sbCons.lazyLookup[memRdPort].get;
|
||||
method rf_rd1 = rf.read[memRdPort].rd1;
|
||||
method rf_rd2 = rf.read[memRdPort].rd2;
|
||||
method csrf_rd = csrf.rd;
|
||||
method rob_getPC = rob.getOrigPC[valueof(AluExeNum)].get; // last getPC port
|
||||
method rob_setExecuted_doFinishMem = rob.setExecuted_doFinishMem;
|
||||
method rob_setExecuted_deqLSQ = rob.setExecuted_deqLSQ;
|
||||
method isMMIOAddr = mmio.isMMIOAddr;
|
||||
method mmioReq = mmio.dataReq;
|
||||
method mmioRespVal = mmio.dataRespVal;
|
||||
method mmioRespDeq = mmio.dataRespDeq;
|
||||
method setRegReadyAggr_mem = writeAggr(memWrAggrPort);
|
||||
method setRegReadyAggr_forward = writeAggr(forwardWrAggrPort);
|
||||
method writeRegFile = writeCons(memWrConsPort);
|
||||
method doStats = doStatsReg._read;
|
||||
endinterface);
|
||||
let memExe <- mkMemExePipeline(memExeInput);
|
||||
|
||||
interface aluExeIfc = aluExe;
|
||||
interface fpuMulDivExeIfc = fpuMulDivExe;
|
||||
interface memExeIfc = memExe;
|
||||
method Action killAll;
|
||||
globalSpecUpdate.incorrectSpec(True, ?, ?);
|
||||
endmethod
|
||||
interface doStatsIfc = doStatsReg;
|
||||
endmodule
|
||||
CoreFixPoint coreFix <- moduleFix(mkCoreFixPoint);
|
||||
|
||||
Vector#(AluExeNum, ReservationStationAlu) reservationStationAlu;
|
||||
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
|
||||
reservationStationAlu[i] = coreFix.aluExeIfc[i].rsAluIfc;
|
||||
end
|
||||
Vector#(FpuMulDivExeNum, ReservationStationFpuMulDiv) reservationStationFpuMulDiv;
|
||||
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
|
||||
reservationStationFpuMulDiv[i] = coreFix.fpuMulDivExeIfc[i].rsFpuMulDivIfc;
|
||||
end
|
||||
ReservationStationMem reservationStationMem = coreFix.memExeIfc.rsMemIfc;
|
||||
DTlbSynth dTlb = coreFix.memExeIfc.dTlbIfc;
|
||||
SplitLSQ lsq = coreFix.memExeIfc.lsqIfc;
|
||||
StoreBuffer stb = coreFix.memExeIfc.stbIfc;
|
||||
DCoCache dMem = coreFix.memExeIfc.dMemIfc;
|
||||
|
||||
// L2 TLB
|
||||
L2Tlb l2Tlb <- mkL2Tlb;
|
||||
mkTlbConnect(iTlb.toParent, dTlb.toParent, l2Tlb.toChildren);
|
||||
|
||||
// flags to flush
|
||||
Reg#(Bool) flush_tlbs <- mkReg(False);
|
||||
Reg#(Bool) update_vm_info <- mkReg(False);
|
||||
Reg#(Bool) flush_reservation <- mkReg(False);
|
||||
`ifdef SECURITY
|
||||
Reg#(Bool) flush_caches <- mkReg(False);
|
||||
Reg#(Bool) flush_brpred <- mkReg(False);
|
||||
`else
|
||||
Reg#(Bool) flush_caches <- mkReadOnlyReg(False);
|
||||
Reg#(Bool) flush_brpred <- mkReadOnlyReg(False);
|
||||
`endif
|
||||
`ifdef SELF_INV_CACHE
|
||||
Reg#(Bool) reconcile_i <- mkReg(False);
|
||||
`else
|
||||
Reg#(Bool) reconcile_i <- mkReadOnlyReg(False);
|
||||
`endif
|
||||
`ifdef SELF_INV_CACHE
|
||||
`ifdef SYSTEM_SELF_INV_L1D
|
||||
Reg#(Bool) reconcile_d <- mkReg(False);
|
||||
`else // !SYSTEM_SELF_INV_L1D
|
||||
Reg#(Bool) reconcile_d <- mkReadOnlyReg(False);
|
||||
`endif // SYSTEM_SELF_INV_L1D
|
||||
`else // !SELF_INV_CACHE
|
||||
Reg#(Bool) reconcile_d <- mkReadOnlyReg(False);
|
||||
`endif // SELF_INV_CACHE
|
||||
|
||||
// performance counters
|
||||
Reg#(Bool) doStats = coreFix.doStatsIfc; // whether data is collected
|
||||
`ifdef PERF_COUNT
|
||||
// OOO execute stag (in AluExePipeline and MemExePipeline)
|
||||
|
||||
// commit stage (many in CommitStage.bsv)
|
||||
// cycle
|
||||
Count#(Data) cycleCnt <- mkCount(0);
|
||||
|
||||
// buffer/tags size
|
||||
Count#(Data) ldqFullCycles <- mkCount(0);
|
||||
Count#(Data) stqFullCycles <- mkCount(0);
|
||||
Count#(Data) robFullCycles <- mkCount(0);
|
||||
Count#(Data) aluRS0FullCycles <- mkCount(0);
|
||||
Count#(Data) aluRS1FullCycles <- mkCount(0);
|
||||
Count#(Data) fpuMulDivRSFullCycles <- mkCount(0);
|
||||
Count#(Data) memRSFullCycles <- mkCount(0);
|
||||
Count#(Data) epochFullCycles <- mkCount(0);
|
||||
Count#(Data) specTagFullCycles <- mkCount(0);
|
||||
|
||||
// FIFOs to connect performance counters
|
||||
FIFO#(ExeStagePerfType) exePerfReqQ <- mkFIFO1;
|
||||
FIFO#(ComStagePerfType) comPerfReqQ <- mkFIFO1;
|
||||
FIFO#(CoreSizePerfType) sizePerfReqQ <- mkFIFO1;
|
||||
Fifo#(1, PerfResp#(ExeStagePerfType)) exePerfRespQ <- mkCFFifo;
|
||||
Fifo#(1, PerfResp#(ComStagePerfType)) comPerfRespQ <- mkCFFifo;
|
||||
Fifo#(1, PerfResp#(CoreSizePerfType)) sizePerfRespQ <- mkCFFifo;
|
||||
|
||||
// FIFO of perf resp
|
||||
FIFO#(ProcPerfResp) perfRespQ <- mkFIFO1;
|
||||
`endif
|
||||
// FIFO of perf req
|
||||
FIFO#(ProcPerfReq) perfReqQ <- mkFIFO1;
|
||||
|
||||
// -- End of performance counters
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
// when to start deadlock checking
|
||||
Reg#(Bool) startDeadlockCheck <- mkReg(False);
|
||||
FIFO#(void) deadlockCheckStartedQ <- mkFIFO;
|
||||
|
||||
rule doStartDeadlockCheck(!startDeadlockCheck && started);
|
||||
startDeadlockCheck <= True;
|
||||
deadlockCheckStartedQ.enq(?);
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// Rename stage
|
||||
let renameInput = (interface RenameInput;
|
||||
interface fetchIfc = fetchStage;
|
||||
interface robIfc = rob;
|
||||
interface rtIfc = regRenamingTable;
|
||||
interface sbConsIfc = sbCons;
|
||||
interface sbAggrIfc = sbAggr;
|
||||
interface csrfIfc = csrf;
|
||||
interface emIfc = epochManager;
|
||||
interface smIfc = specTagManager;
|
||||
interface rsAluIfc = reservationStationAlu;
|
||||
interface rsFpuMulDivIfc = reservationStationFpuMulDiv;
|
||||
interface rsMemIfc = reservationStationMem;
|
||||
interface lsqIfc = lsq;
|
||||
method pendingMMIOPRq = mmio.hasPendingPRq;
|
||||
method issueCsrInstOrInterrupt = csrInstOrInterruptInflight_rename._write(True);
|
||||
method Bool checkDeadlock;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
return startDeadlockCheck;
|
||||
`else
|
||||
return False;
|
||||
`endif
|
||||
endmethod
|
||||
method doStats = coreFix.doStatsIfc._read;
|
||||
endinterface);
|
||||
RenameStage renameStage <- mkRenameStage(renameInput);
|
||||
|
||||
// commit stage
|
||||
let commitInput = (interface CommitInput;
|
||||
interface robIfc = rob;
|
||||
interface rtIfc = regRenamingTable;
|
||||
interface csrfIfc = csrf;
|
||||
method stbEmpty = stb.isEmpty;
|
||||
method stqEmpty = lsq.stqEmpty;
|
||||
method lsqSetAtCommit = lsq.setAtCommit;
|
||||
method tlbNoPendingReq = iTlb.noPendingReq && dTlb.noPendingReq;
|
||||
method setFlushTlbs = flush_tlbs._write(True);
|
||||
method setUpdateVMInfo = update_vm_info._write(True);
|
||||
method setFlushReservation = flush_reservation._write(True);
|
||||
method setFlushBrPred = flush_brpred._write(True);
|
||||
method setFlushCaches = flush_caches._write(True);
|
||||
method setReconcileI = reconcile_i._write(True);
|
||||
method setReconcileD = reconcile_d._write(True);
|
||||
method killAll = coreFix.killAll;
|
||||
method redirectPc = fetchStage.redirect;
|
||||
method setFetchWaitRedirect = fetchStage.setWaitRedirect;
|
||||
method incrementEpoch = epochManager.incrementEpoch;
|
||||
method commitCsrInstOrInterrupt = csrInstOrInterruptInflight_commit._write(False);
|
||||
method doStats = coreFix.doStatsIfc._read;
|
||||
method Bool checkDeadlock;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
return startDeadlockCheck;
|
||||
`else
|
||||
return False;
|
||||
`endif
|
||||
endmethod
|
||||
endinterface);
|
||||
CommitStage commitStage <- mkCommitStage(commitInput);
|
||||
|
||||
// send rob enq time to reservation stations
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule sendRobEnqTime;
|
||||
InstTime t = rob.getEnqTime;
|
||||
reservationStationMem.setRobEnqTime(t);
|
||||
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
|
||||
reservationStationFpuMulDiv[i].setRobEnqTime(t);
|
||||
end
|
||||
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
|
||||
reservationStationAlu[i].setRobEnqTime(t);
|
||||
end
|
||||
endrule
|
||||
|
||||
// preempt has 2 functions here
|
||||
// 1. break scheduling cycles
|
||||
// 2. XXX since csrf is configReg now, we should not let this rule fire together with doCommit
|
||||
// because we read csrf here and write csrf in doCommit
|
||||
|
||||
// TODO We can use wires to catch flush / updateVM enable sigals, because
|
||||
// there cannot be any instruction in pipeline (there can be poisoned inst
|
||||
// which cannot change CSR or link reg in D$), so doCommit cannot fire.
|
||||
// MMIO manager may change pending interrupt bits, but will not affect VM
|
||||
// info.
|
||||
(* preempts = "prepareCachesAndTlbs, commitStage.doCommitTrap_handle" *)
|
||||
(* preempts = "prepareCachesAndTlbs, commitStage.doCommitSystemInst" *)
|
||||
rule prepareCachesAndTlbs(flush_reservation || flush_tlbs || update_vm_info);
|
||||
if (flush_reservation) begin
|
||||
flush_reservation <= False;
|
||||
dMem.resetLinkAddr;
|
||||
end
|
||||
if (flush_tlbs) begin
|
||||
flush_tlbs <= False;
|
||||
iTlb.flush;
|
||||
dTlb.flush;
|
||||
end
|
||||
if (update_vm_info) begin
|
||||
update_vm_info <= False;
|
||||
let vmI = csrf.vmI;
|
||||
let vmD = csrf.vmD;
|
||||
iTlb.updateVMInfo(vmI);
|
||||
dTlb.updateVMInfo(vmD);
|
||||
l2Tlb.updateVMInfo(vmI, vmD);
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef SECURITY
|
||||
// Use wires to capture flush regs and empty signals. This is ok because
|
||||
// there cannot be any activity to make empty -> not-empty or need-flush ->
|
||||
// no-need-flush when we are trying to flush.
|
||||
PulseWire doFlushCaches <- mkPulseWire;
|
||||
PulseWire doFlushBrPred <- mkPulseWire;
|
||||
|
||||
rule setDoFlushCaches(flush_caches && fetchStage.emptyForFlush && lsq.noWrongPathLoads);
|
||||
doFlushCaches.send;
|
||||
endrule
|
||||
|
||||
rule setDoFlushBrPred(flush_brpred && fetchStage.emptyForFlush);
|
||||
doFlushBrPred.send;
|
||||
endrule
|
||||
|
||||
// security flush cache: need to wait for wrong path loads or inst fetches
|
||||
// to finish
|
||||
rule flushCaches(doFlushCaches);
|
||||
flush_caches <= False;
|
||||
iMem.flush;
|
||||
dMem.flush;
|
||||
endrule
|
||||
|
||||
// security flush branch predictors: wait for wrong path inst fetches to
|
||||
// finish
|
||||
rule flushBrPred(doFlushBrPred);
|
||||
flush_brpred <= False;
|
||||
fetchStage.flush_predictors;
|
||||
endrule
|
||||
`endif
|
||||
|
||||
`ifdef SELF_INV_CACHE
|
||||
// Use wires to capture flush regs and empty signals. This is ok because
|
||||
// there cannot be any activity to make empty -> not-empty or need-flush ->
|
||||
// no-need-flush when we are trying to flush.
|
||||
PulseWire doReconcileI <- mkPulseWire;
|
||||
|
||||
// We don't really need to wait for fetch to be empty, but just in case we
|
||||
// back pressure I TLB because I$ is reconciling.
|
||||
rule setDoReconcileI(reconcile_i && fetchStage.emptyForFlush);
|
||||
doReconcileI.send;
|
||||
endrule
|
||||
|
||||
rule reconcileI(doReconcileI);
|
||||
reconcile_i <= False;
|
||||
iMem.reconcile;
|
||||
endrule
|
||||
|
||||
`ifdef SYSTEM_SELF_INV_L1D
|
||||
PulseWire doReconcileD <- mkPulseWire;
|
||||
|
||||
Reg#(Bool) waitReconcileD <- mkReg(False);
|
||||
|
||||
// We don't really need to wait for lsq empty, but just in case
|
||||
rule setDoReconcileD(reconcile_d && lsq.noWrongPathLoads);
|
||||
doReconcileD.send;
|
||||
endrule
|
||||
|
||||
rule startReconcileD(doReconcileD && !waitReconcileD);
|
||||
coreFix.memExeIfc.reconcile.request.put(?);
|
||||
waitReconcileD <= True;
|
||||
endrule
|
||||
|
||||
rule completeReconcileD(waitReconcileD);
|
||||
let unused <- coreFix.memExeIfc.reconcile.response.get;
|
||||
waitReconcileD <= False;
|
||||
reconcile_d <= False;
|
||||
endrule
|
||||
`endif // SYSTEM_SELF_INV_L1D
|
||||
`endif // SELF_INV_CACHE
|
||||
|
||||
rule readyToFetch(
|
||||
!flush_reservation && !flush_tlbs && !update_vm_info
|
||||
&& iTlb.flush_done && dTlb.flush_done
|
||||
`ifdef SECURITY
|
||||
&& !flush_caches && !flush_brpred
|
||||
&& iMem.flush_done && dMem.flush_done
|
||||
&& fetchStage.flush_predictors_done
|
||||
`endif
|
||||
`ifdef SELF_INV_CACHE
|
||||
&& !reconcile_i && iMem.reconcile_done
|
||||
`ifdef SYSTEM_SELF_INV_L1D
|
||||
&& !reconcile_d
|
||||
`endif
|
||||
`endif
|
||||
);
|
||||
fetchStage.done_flushing();
|
||||
endrule
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
// incr cycle count
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incCycleCnt(doStats);
|
||||
cycleCnt.incr(1);
|
||||
endrule
|
||||
|
||||
// incr buffer full cycles
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incLdQFull(doStats && lsq.ldqFull_ehrPort0);
|
||||
ldqFullCycles.incr(1);
|
||||
endrule
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incStQFull(doStats && lsq.stqFull_ehrPort0);
|
||||
stqFullCycles.incr(1);
|
||||
endrule
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incROBFull(doStats && rob.isFull_ehrPort0);
|
||||
robFullCycles.incr(1);
|
||||
endrule
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incAluRS0Full(doStats && reservationStationAlu[0].isFull_ehrPort0);
|
||||
aluRS0FullCycles.incr(1);
|
||||
endrule
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incAluRS1Full(doStats && reservationStationAlu[1].isFull_ehrPort0);
|
||||
aluRS1FullCycles.incr(1);
|
||||
endrule
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incFpuMulDivRSFull(doStats && reservationStationFpuMulDiv[0].isFull_ehrPort0);
|
||||
fpuMulDivRSFullCycles.incr(1);
|
||||
endrule
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incMemRSFull(doStats && reservationStationMem.isFull_ehrPort0);
|
||||
memRSFullCycles.incr(1);
|
||||
endrule
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incEpochFull(doStats && epochManager.isFull_ehrPort0);
|
||||
epochFullCycles.incr(1);
|
||||
endrule
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incSpecTagFull(doStats && specTagManager.isFull_ehrPort0);
|
||||
specTagFullCycles.incr(1);
|
||||
endrule
|
||||
|
||||
// broadcast whether we should collect data
|
||||
rule broadcastDoStats;
|
||||
let stats = csrf.doPerfStats;
|
||||
doStats <= stats;
|
||||
iMem.perf.setStatus(stats);
|
||||
dMem.perf.setStatus(stats);
|
||||
iTlb.perf.setStatus(stats);
|
||||
dTlb.perf.setStatus(stats);
|
||||
l2Tlb.perf.setStatus(stats);
|
||||
fetchStage.perf.setStatus(stats);
|
||||
|
||||
if(stats && !doStats) begin
|
||||
$display("[stats] enabled");
|
||||
end
|
||||
else if(!stats && doStats) begin
|
||||
$display("[stats] disabled");
|
||||
end
|
||||
endrule
|
||||
|
||||
// dispatch perf req
|
||||
rule dispathPerfReq;
|
||||
perfReqQ.deq;
|
||||
let r = perfReqQ.first;
|
||||
case(r.loc)
|
||||
ICache: begin
|
||||
iMem.perf.req(unpack(truncate(r.pType)));
|
||||
end
|
||||
DCache: begin
|
||||
dMem.perf.req(unpack(truncate(r.pType)));
|
||||
end
|
||||
ITlb: begin
|
||||
iTlb.perf.req(unpack(truncate(r.pType)));
|
||||
end
|
||||
DTlb: begin
|
||||
dTlb.perf.req(unpack(truncate(r.pType)));
|
||||
end
|
||||
L2Tlb: begin
|
||||
l2Tlb.perf.req(unpack(truncate(r.pType)));
|
||||
end
|
||||
DecStage: begin
|
||||
fetchStage.perf.req(unpack(truncate(r.pType)));
|
||||
end
|
||||
ExeStage: begin
|
||||
exePerfReqQ.enq(unpack(truncate(r.pType)));
|
||||
end
|
||||
ComStage: begin
|
||||
comPerfReqQ.enq(unpack(truncate(r.pType)));
|
||||
end
|
||||
CoreSize: begin
|
||||
sizePerfReqQ.enq(unpack(truncate(r.pType)));
|
||||
end
|
||||
default: begin
|
||||
$fwrite(stderr, "[WARNING] unrecognzied perf req location ", fshow(r.loc), "\n");
|
||||
doAssert(False, "unknown perf location");
|
||||
end
|
||||
endcase
|
||||
endrule
|
||||
|
||||
// handle perf req: exe stage
|
||||
rule readPerfCnt_Exe;
|
||||
function Data getAluCnt(ExeStagePerfType pType);
|
||||
Data cnt = 0;
|
||||
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
|
||||
cnt = cnt + coreFix.aluExeIfc[i].getPerf(pType);
|
||||
end
|
||||
return cnt;
|
||||
endfunction
|
||||
|
||||
function Data getFpuMulDivCnt(ExeStagePerfType pType);
|
||||
Data cnt = 0;
|
||||
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
|
||||
cnt = cnt + coreFix.fpuMulDivExeIfc[i].getPerf(pType);
|
||||
end
|
||||
return cnt;
|
||||
endfunction
|
||||
|
||||
let pType <- toGet(exePerfReqQ).get;
|
||||
Data data = (case(pType)
|
||||
SupRenameCnt, SpecNoneCycles, SpecNonMemCycles: renameStage.getPerf(pType);
|
||||
ExeRedirectBr, ExeRedirectJr, ExeRedirectOther: getAluCnt(pType);
|
||||
ExeTlbExcep, ExeScSuccessCnt,
|
||||
ExeLrScAmoAcqCnt, ExeLrScAmoRelCnt,
|
||||
ExeFenceAcqCnt, ExeFenceRelCnt, ExeFenceCnt,
|
||||
ExeLdStallByLd, ExeLdStallBySt, ExeLdStallBySB,
|
||||
ExeLdForward, ExeLdMemLat, ExeStMemLat,
|
||||
ExeLdToUseLat, ExeLdToUseCnt: coreFix.memExeIfc.getPerf(pType);
|
||||
ExeIntMulCnt, ExeIntDivCnt,
|
||||
ExeFpFmaCnt, ExeFpDivCnt, ExeFpSqrtCnt: getFpuMulDivCnt(pType);
|
||||
default: 0;
|
||||
endcase);
|
||||
exePerfRespQ.enq(PerfResp {
|
||||
pType: pType,
|
||||
data: data
|
||||
});
|
||||
endrule
|
||||
|
||||
// handle perf req: com stage
|
||||
rule readPerfCnt_Com;
|
||||
let pType <- toGet(comPerfReqQ).get;
|
||||
Data data = (case(pType)
|
||||
CycleCnt: cycleCnt;
|
||||
default: commitStage.getPerf(pType);
|
||||
endcase);
|
||||
comPerfRespQ.enq(PerfResp {
|
||||
pType: pType,
|
||||
data: data
|
||||
});
|
||||
endrule
|
||||
|
||||
// handle perf req: core size
|
||||
rule readPerfCnt_Size;
|
||||
let pType <- toGet(sizePerfReqQ).get;
|
||||
Data data = (case(pType)
|
||||
LdQFullCycles: ldqFullCycles;
|
||||
StQFullCycles: stqFullCycles;
|
||||
ROBFullCycles: robFullCycles;
|
||||
AluRS0FullCycles: aluRS0FullCycles;
|
||||
AluRS1FullCycles: aluRS1FullCycles;
|
||||
FpuMulDivRSFullCycles: fpuMulDivRSFullCycles;
|
||||
MemRSFullCycles: memRSFullCycles;
|
||||
EpochFullCycles: epochFullCycles;
|
||||
SpecTagFullCycles: specTagFullCycles;
|
||||
default: 0;
|
||||
endcase);
|
||||
sizePerfRespQ.enq(PerfResp {
|
||||
pType: pType,
|
||||
data: data
|
||||
});
|
||||
endrule
|
||||
|
||||
// gather perf resp
|
||||
rule gatherPerfResp;
|
||||
Maybe#(ProcPerfResp) resp = Invalid;
|
||||
if(iMem.perf.respValid) begin
|
||||
let r <- iMem.perf.resp;
|
||||
resp = Valid(ProcPerfResp {
|
||||
loc: ICache,
|
||||
pType: zeroExtend(pack(r.pType)),
|
||||
data: r.data
|
||||
});
|
||||
end
|
||||
else if(dMem.perf.respValid) begin
|
||||
let r <- dMem.perf.resp;
|
||||
resp = Valid(ProcPerfResp {
|
||||
loc: DCache,
|
||||
pType: zeroExtend(pack(r.pType)),
|
||||
data: r.data
|
||||
});
|
||||
end
|
||||
else if(iTlb.perf.respValid) begin
|
||||
let r <- iTlb.perf.resp;
|
||||
resp = Valid(ProcPerfResp {
|
||||
loc: ITlb,
|
||||
pType: zeroExtend(pack(r.pType)),
|
||||
data: r.data
|
||||
});
|
||||
end
|
||||
else if(dTlb.perf.respValid) begin
|
||||
let r <- dTlb.perf.resp;
|
||||
resp = Valid(ProcPerfResp {
|
||||
loc: DTlb,
|
||||
pType: zeroExtend(pack(r.pType)),
|
||||
data: r.data
|
||||
});
|
||||
end
|
||||
else if(l2Tlb.perf.respValid) begin
|
||||
let r <- l2Tlb.perf.resp;
|
||||
resp = Valid(ProcPerfResp {
|
||||
loc: L2Tlb,
|
||||
pType: zeroExtend(pack(r.pType)),
|
||||
data: r.data
|
||||
});
|
||||
end
|
||||
else if(fetchStage.perf.respValid) begin
|
||||
let r <- fetchStage.perf.resp;
|
||||
resp = Valid(ProcPerfResp {
|
||||
loc: DecStage,
|
||||
pType: zeroExtend(pack(r.pType)),
|
||||
data: r.data
|
||||
});
|
||||
end
|
||||
else if(exePerfRespQ.notEmpty) begin
|
||||
let r <- toGet(exePerfRespQ).get;
|
||||
resp = Valid(ProcPerfResp {
|
||||
loc: ExeStage,
|
||||
pType: zeroExtend(pack(r.pType)),
|
||||
data: r.data
|
||||
});
|
||||
end
|
||||
else if(comPerfRespQ.notEmpty) begin
|
||||
let r <- toGet(comPerfRespQ).get;
|
||||
resp = Valid(ProcPerfResp {
|
||||
loc: ComStage,
|
||||
pType: zeroExtend(pack(r.pType)),
|
||||
data: r.data
|
||||
});
|
||||
end
|
||||
else if(sizePerfRespQ.notEmpty) begin
|
||||
let r <- toGet(sizePerfRespQ).get;
|
||||
resp = Valid (ProcPerfResp {
|
||||
loc: CoreSize,
|
||||
pType: zeroExtend(pack(r.pType)),
|
||||
data: r.data
|
||||
});
|
||||
end
|
||||
// enq to resp Q
|
||||
if(resp matches tagged Valid .r) begin
|
||||
perfRespQ.enq(r);
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
interface CoreReq coreReq;
|
||||
method Action start(
|
||||
Bit#(64) startpc,
|
||||
Addr toHostAddr, Addr fromHostAddr
|
||||
);
|
||||
fetchStage.start(startpc);
|
||||
started <= True;
|
||||
mmio.setHtifAddrs(toHostAddr, fromHostAddr);
|
||||
// start rename debug
|
||||
commitStage.startRenameDebug;
|
||||
endmethod
|
||||
|
||||
method Action perfReq(PerfLocation loc, PerfType t);
|
||||
perfReqQ.enq(ProcPerfReq {
|
||||
loc: loc,
|
||||
pType: t
|
||||
});
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface CoreIndInv coreIndInv;
|
||||
method ActionValue#(ProcPerfResp) perfResp;
|
||||
`ifdef PERF_COUNT
|
||||
perfRespQ.deq;
|
||||
return perfRespQ.first;
|
||||
`else
|
||||
perfReqQ.deq;
|
||||
let r = perfReqQ.first;
|
||||
return ProcPerfResp {
|
||||
loc: r.loc,
|
||||
pType: r.pType,
|
||||
data: 0
|
||||
};
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method terminate = csrf.terminate;
|
||||
endinterface
|
||||
|
||||
interface dCacheToParent = dMem.to_parent;
|
||||
interface iCacheToParent = iMem.to_parent;
|
||||
|
||||
interface tlbToMem = l2Tlb.toMem;
|
||||
|
||||
interface mmioToPlatform = mmio.toP;
|
||||
|
||||
method sendDoStats = csrf.sendDoStats;
|
||||
method recvDoStats = csrf.recvDoStats;
|
||||
|
||||
// deadlock check
|
||||
interface CoreDeadlock deadlock;
|
||||
interface dCacheCRqStuck = dMem.cRqStuck;
|
||||
interface dCachePRqStuck = dMem.pRqStuck;
|
||||
interface iCacheCRqStuck = iMem.cRqStuck;
|
||||
interface iCachePRqStuck = iMem.pRqStuck;
|
||||
interface renameInstStuck = renameStage.renameInstStuck;
|
||||
interface renameCorrectPathStuck = renameStage.renameCorrectPathStuck;
|
||||
interface commitInstStuck = commitStage.commitInstStuck;
|
||||
interface commitUserInstStuck = commitStage.commitUserInstStuck;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface checkStarted = toGet(deadlockCheckStartedQ);
|
||||
`else
|
||||
interface checkStarted = nullGet;
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
// rename debug
|
||||
interface CoreRenameDebug renameDebug;
|
||||
interface renameErr = commitStage.renameErr;
|
||||
endinterface
|
||||
|
||||
// Bluespec: external interrupt requests targeting Machine and Supervisor modes
|
||||
method Action setMEIP (v) = csrf.setMEIP (v);
|
||||
method Action setSEIP (v) = csrf.setSEIP (v);
|
||||
endmodule
|
||||
|
||||
842
src_Core/CPU/CsrFile.bsv
Normal file
842
src_Core/CPU/CsrFile.bsv
Normal file
@@ -0,0 +1,842 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// Portions Copyright (c) Bluespec, Inc.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import DefaultValue::*;
|
||||
import ConcatReg::*;
|
||||
import ConfigReg::*;
|
||||
import Ehr::*;
|
||||
import Fifo::*;
|
||||
import Vector::*;
|
||||
import FIFO::*;
|
||||
import GetPut::*;
|
||||
import BuildVector::*;
|
||||
//import TRNG::*;
|
||||
|
||||
interface CsrFile;
|
||||
// Read
|
||||
method Data rd(CSR csr);
|
||||
// normal write by CSRXXX inst to any CSR
|
||||
method Action csrInstWr(CSR csr, Data x);
|
||||
// normal write by FPU inst to FPU CSR
|
||||
method Bool fpuInstNeedWr(Bit#(5) fflags, Bool fpu_dirty);
|
||||
method Action fpuInstWr(Bit#(5) fflags); // FPU must become dirty
|
||||
|
||||
// Methods for handling traps
|
||||
method Maybe#(Interrupt) pending_interrupt;
|
||||
method ActionValue#(Addr) trap(Trap t, Addr pc, Addr faultAddr);
|
||||
method ActionValue#(Addr) sret;
|
||||
method ActionValue#(Addr) mret;
|
||||
|
||||
// Outputs for CSRs that the rest of the processor needs to know about
|
||||
method VMInfo vmI;
|
||||
method VMInfo vmD;
|
||||
method CsrDecodeInfo decodeInfo;
|
||||
|
||||
// Updating minstret CSR outside of normal CSR write instructions. This
|
||||
// increment will see the effect of normal CSR write.
|
||||
method Action incInstret(SupCnt x);
|
||||
|
||||
// update copy of mtime
|
||||
method Action setTime(Data t);
|
||||
|
||||
// MSIP/MTIP bits for external world (e.g., for MMIO and timer interrupt).
|
||||
// XXX These methods should only be called when the processor backend
|
||||
// pipeline does not contain any CSRXXX inst or corresponding interrupt
|
||||
// inst (the inst which is turned into an interrupt). This ensures that
|
||||
// CSRXXX and interrupt are handled atomically. MSIP/MTIP should not
|
||||
// affect other insts (e.g., address translation of loads/stores),
|
||||
// synchronous exceptions and other types of interrupts.
|
||||
method Bit#(1) getMSIP;
|
||||
method Action setMSIP(Bit#(1) v);
|
||||
method Action setMTIP(Bit#(1) v);
|
||||
|
||||
// Bluespec: external interrupts targeting machine and supervisor modes
|
||||
method Action setMEIP (Bit #(1) v);
|
||||
method Action setSEIP (Bit #(1) v);
|
||||
|
||||
// performance stats is collected or not
|
||||
method Bool doPerfStats;
|
||||
// send/recv updates on stats CSR globally
|
||||
method ActionValue#(Bool) sendDoStats;
|
||||
method Action recvDoStats(Bool s);
|
||||
|
||||
// terminate
|
||||
method ActionValue#(void) terminate;
|
||||
endinterface
|
||||
|
||||
// Fancy Reg functions
|
||||
function Reg#(Bit#(n)) truncateReg(Reg#(Bit#(m)) r) provisos (Add#(a__,n,m));
|
||||
return (interface Reg;
|
||||
method Bit#(n) _read = truncate(r._read);
|
||||
method Action _write(Bit#(n) x) = r._write({truncateLSB(r._read), x});
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) truncateRegLSB(Reg#(Bit#(m)) r) provisos (Add#(a__,n,m));
|
||||
return (interface Reg;
|
||||
method Bit#(n) _read = truncateLSB(r._read);
|
||||
method Action _write(Bit#(n) x) = r._write({x, truncate(r._read)});
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) zeroExtendReg(Reg#(Bit#(m)) r) provisos (Add#(a__,m,n));
|
||||
return (interface Reg;
|
||||
method Bit#(n) _read = zeroExtend(r._read);
|
||||
method Action _write(Bit#(n) x) = r._write(truncate(x));
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function Reg#(t) readOnlyReg(t r);
|
||||
return (interface Reg;
|
||||
method t _read = r;
|
||||
method Action _write(t x) = noAction;
|
||||
endinterface);
|
||||
endfunction
|
||||
// module version of readOnlyReg for convenience
|
||||
module mkReadOnlyReg#(t x)(Reg#(t));
|
||||
return readOnlyReg(x);
|
||||
endmodule
|
||||
|
||||
function Reg#(t) regFromReadOnly(ReadOnly#(t) r);
|
||||
return (interface Reg;
|
||||
method t _read = r._read;
|
||||
method Action _write(t x);
|
||||
noAction;
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function Reg#(t) addWriteSideEffect(Reg#(t) r, Action a);
|
||||
return (interface Reg;
|
||||
method t _read = r._read;
|
||||
method Action _write(t x);
|
||||
r._write(x);
|
||||
a;
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function Bool has_csr_permission(CSR csr, Bit#(2) prv, Bool write);
|
||||
Bit#(12) csr_index = pack(csr);
|
||||
return ((prv >= csr_index[9:8]) && (!write || (csr_index[11:10] != 2'b11)));
|
||||
endfunction
|
||||
|
||||
// non-standard terminate CSR
|
||||
interface Terminate;
|
||||
interface Reg#(Data) reg_ifc;
|
||||
method ActionValue#(void) terminate;
|
||||
endinterface
|
||||
|
||||
module mkTerminate(Terminate);
|
||||
FIFO#(void) terminateQ <- mkFIFO1;
|
||||
|
||||
interface Reg reg_ifc;
|
||||
method Action _write(Data x);
|
||||
terminateQ.enq(?);
|
||||
$display(
|
||||
"[Terminate CSR] being written (val = %x), ",
|
||||
"send terminate signal to host", x
|
||||
);
|
||||
endmethod
|
||||
method Data _read = 0;
|
||||
endinterface
|
||||
|
||||
method terminate = toGet(terminateQ).get;
|
||||
endmodule
|
||||
|
||||
// stats CSR: there is only one copy in the whole multiprocessor, so any write
|
||||
// to stats CSR will be broadcasted
|
||||
interface StatsCsr;
|
||||
interface Reg#(Data) reg_ifc;
|
||||
method Bool doPerfStats;
|
||||
// send/recv updates on stats CSR globally
|
||||
method ActionValue#(Bool) sendDoStats;
|
||||
method Action recvDoStats(Bool s);
|
||||
endinterface
|
||||
|
||||
module mkStatsCsr(StatsCsr);
|
||||
Reg#(Bool) doStats <- mkConfigReg(False);
|
||||
|
||||
FIFO#(Bool) writeQ <- mkFIFO1;
|
||||
|
||||
interface Reg reg_ifc;
|
||||
method Data _read = zeroExtend(pack(doStats));
|
||||
method Action _write(Data x);
|
||||
writeQ.enq(unpack(truncate(x)));
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
method Bool doPerfStats = doStats;
|
||||
|
||||
method ActionValue#(Bool) sendDoStats;
|
||||
writeQ.deq;
|
||||
return writeQ.first;
|
||||
endmethod
|
||||
|
||||
method Action recvDoStats(Bool s);
|
||||
doStats <= s;
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
// same as EHR except that read port 0 is not ordered with other methods. Read
|
||||
// port 1 will still get bypassing from write port 0.
|
||||
module mkConfigEhr#(t init)(Ehr#(n, t)) provisos(Bits#(t, w));
|
||||
Ehr#(n, t) data <- mkEhr(init);
|
||||
Wire#(t) read <- mkBypassWire;
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule setRead;
|
||||
read <= data[0];
|
||||
endrule
|
||||
|
||||
Ehr#(n, t) ifc = ?;
|
||||
ifc[0] = (interface Reg;
|
||||
method _read = read._read;
|
||||
method _write = data[0]._write;
|
||||
endinterface);
|
||||
for(Integer i = 1; i < valueOf(n); i = i+1) begin
|
||||
ifc[i] = (interface Reg;
|
||||
method _read = data[i]._read;
|
||||
method _write = data[i]._write;
|
||||
endinterface);
|
||||
end
|
||||
return ifc;
|
||||
endmodule
|
||||
|
||||
module mkCsrFile #(Data hartid)(CsrFile);
|
||||
RiscVISASubset isa = defaultValue;
|
||||
|
||||
// To save from bypassing logic, CSR reads will get stale value
|
||||
let mkCsrReg = mkConfigReg;
|
||||
let mkCsrEhr = mkConfigEhr;
|
||||
|
||||
// current prv level (this is not a csr...)
|
||||
Reg#(Bit#(2)) prv_reg <- mkCsrReg(prvM);
|
||||
|
||||
// Machine level CSRs
|
||||
// mstatus
|
||||
Reg#(Bit#(2)) xs_reg <- mkReadOnlyReg(0); // XXX no extension
|
||||
Reg#(Bit#(2)) fs_reg <- (isa.f || isa.d) ? mkCsrReg(0) : mkReadOnlyReg(0);
|
||||
Reg#(Bit#(1)) sd_reg = readOnlyReg(
|
||||
((xs_reg == 2'b11) || (fs_reg == 2'b11)) ? 1 : 0
|
||||
);
|
||||
Reg#(Bit#(2)) sxl_reg = readOnlyReg(getXLBits);
|
||||
Reg#(Bit#(2)) uxl_reg = readOnlyReg(getXLBits);
|
||||
Reg#(Bit#(1)) tsr_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) tw_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) tvm_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) mxr_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) sum_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) mprv_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(2)) mpp_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) spp_reg <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(2))) prev_prv_vec = vec(
|
||||
// prev_prv_vec[x]: privilege mode before trapping into mode x
|
||||
readOnlyReg(prvU), // upp
|
||||
concatReg2(readOnlyReg(1'b0), spp_reg), // spp
|
||||
readOnlyReg(2'b0), // reserved
|
||||
mpp_reg
|
||||
);
|
||||
Vector#(4, Reg#(Bit#(1))) ie_vec = replicate(
|
||||
readOnlyReg(0) // ie_vec[x]: interrupt enable for mode x
|
||||
);
|
||||
ie_vec[prvU] <- mkCsrReg(0);
|
||||
ie_vec[prvS] <- mkCsrReg(0);
|
||||
ie_vec[prvM] <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(1))) prev_ie_vec = replicate(
|
||||
readOnlyReg(0) // prev_ie_vec[x]: ie_vec[x] before trapping into mode x
|
||||
);
|
||||
prev_ie_vec[prvU] <- mkCsrReg(0);
|
||||
prev_ie_vec[prvS] <- mkCsrReg(0);
|
||||
prev_ie_vec[prvM] <- mkCsrReg(0);
|
||||
Reg#(Data) mstatus_csr = concatReg24(
|
||||
sd_reg, readOnlyReg(27'b0), sxl_reg, uxl_reg, readOnlyReg(9'b0),
|
||||
tsr_reg, tw_reg, tvm_reg, mxr_reg, sum_reg, mprv_reg, xs_reg, fs_reg,
|
||||
mpp_reg, readOnlyReg(2'b0), spp_reg,
|
||||
prev_ie_vec[prvM], readOnlyReg(1'b0),
|
||||
prev_ie_vec[prvS], prev_ie_vec[prvU],
|
||||
ie_vec[prvM], readOnlyReg(1'b0),
|
||||
ie_vec[prvS], ie_vec[prvU]
|
||||
);
|
||||
// misa
|
||||
Reg#(Data) misa_csr = readOnlyReg({getXLBits, 36'b0, getExtensionBits(isa)});
|
||||
// medeleg: some exceptions don't exist, fix corresponding bits to 0
|
||||
Reg#(Bit#(1)) medeleg_15_reg <- mkCsrReg(0); // cause 15
|
||||
Reg#(Bit#(3)) medeleg_13_11_reg <- mkCsrReg(0); // case 13-11
|
||||
Reg#(Bit#(10)) medeleg_9_0_reg <- mkCsrReg(0); // cause 9-0
|
||||
Reg#(Data) medeleg_csr = concatReg6(
|
||||
readOnlyReg(48'b0), medeleg_15_reg,
|
||||
readOnlyReg(1'b0), medeleg_13_11_reg,
|
||||
readOnlyReg(1'b0), medeleg_9_0_reg
|
||||
);
|
||||
// mideleg: some interrupts don't exist, fix corresponding bits to 0
|
||||
Reg#(Bit#(1)) mideleg_11_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(3)) mideleg_9_7_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(3)) mideleg_5_3_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(2)) mideleg_1_0_reg <- mkCsrReg(0);
|
||||
Reg#(Data) mideleg_csr = concatReg8(
|
||||
readOnlyReg(52'b0), mideleg_11_reg,
|
||||
readOnlyReg(1'b0), mideleg_9_7_reg,
|
||||
readOnlyReg(1'b0), mideleg_5_3_reg,
|
||||
readOnlyReg(1'b0), mideleg_1_0_reg
|
||||
);
|
||||
// mie
|
||||
Vector#(4, Reg#(Bit#(1))) external_int_en_vec = replicate(readOnlyReg(0));
|
||||
external_int_en_vec[prvU] <- mkCsrReg(0);
|
||||
external_int_en_vec[prvS] <- mkCsrReg(0);
|
||||
external_int_en_vec[prvM] <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(1))) timer_int_en_vec = replicate(readOnlyReg(0));
|
||||
timer_int_en_vec[prvU] <- mkCsrReg(0);
|
||||
timer_int_en_vec[prvS] <- mkCsrReg(0);
|
||||
timer_int_en_vec[prvM] <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(1))) software_int_en_vec = replicate(readOnlyReg(0));
|
||||
software_int_en_vec[prvU] <- mkCsrReg(0);
|
||||
software_int_en_vec[prvS] <- mkCsrReg(0);
|
||||
software_int_en_vec[prvM] <- mkCsrReg(0);
|
||||
Reg#(Data) mie_csr = concatReg13(
|
||||
readOnlyReg(52'b0),
|
||||
external_int_en_vec[prvM], readOnlyReg(1'b0),
|
||||
external_int_en_vec[prvS], external_int_en_vec[prvU],
|
||||
timer_int_en_vec[prvM], readOnlyReg(1'b0),
|
||||
timer_int_en_vec[prvS], timer_int_en_vec[prvU],
|
||||
software_int_en_vec[prvM], readOnlyReg(1'b0),
|
||||
software_int_en_vec[prvS], software_int_en_vec[prvU]
|
||||
);
|
||||
// mtvec
|
||||
Reg#(Bit#(62)) mtvec_base_hi_reg <- mkCsrReg(0); // this is BASE[63:2]
|
||||
Reg#(Bit#(1)) mtvec_mode_low_reg <- mkCsrReg(0); // this is MODE[0]
|
||||
Reg#(Data) mtvec_csr = concatReg3(
|
||||
mtvec_base_hi_reg, readOnlyReg(1'b0), mtvec_mode_low_reg
|
||||
);
|
||||
// mcounteren
|
||||
Reg#(Bit#(1)) mcounteren_ir_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) mcounteren_tm_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) mcounteren_cy_reg <- mkCsrReg(0);
|
||||
Reg#(Data) mcounteren_csr = concatReg5(
|
||||
readOnlyReg(32'b0),
|
||||
readOnlyReg(29'b0), // hpmcounter 3-31 not accessible in S mode
|
||||
mcounteren_ir_reg, mcounteren_tm_reg, mcounteren_cy_reg
|
||||
);
|
||||
// mscratch
|
||||
Reg#(Data) mscratch_csr <- mkCsrReg(0);
|
||||
// mepc: FIXME Since we don't have C extension, mepc should be 4-byte
|
||||
// aligned. However, spike is not checking this, so we don't implement it.
|
||||
Reg#(Data) mepc_csr <- mkCsrReg(0);
|
||||
// mcause
|
||||
Reg#(Bit#(1)) mcause_interrupt_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(4)) mcause_code_reg <- mkCsrReg(0);
|
||||
Reg#(Data) mcause_csr = concatReg3(
|
||||
mcause_interrupt_reg, readOnlyReg(59'b0), mcause_code_reg
|
||||
);
|
||||
// mtval (mbadaddr in spike)
|
||||
Reg#(Data) mtval_csr <- mkCsrReg(0);
|
||||
// mip
|
||||
Vector#(4, Reg#(Bit#(1))) external_int_pend_vec = replicate(readOnlyReg(0));
|
||||
external_int_pend_vec[prvU] <- mkCsrReg(0);
|
||||
external_int_pend_vec[prvS] <- mkCsrReg(0);
|
||||
external_int_pend_vec[prvM] <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(1))) timer_int_pend_vec = replicate(readOnlyReg(0));
|
||||
timer_int_pend_vec[prvU] <- mkCsrReg(0);
|
||||
timer_int_pend_vec[prvS] <- mkCsrReg(0);
|
||||
timer_int_pend_vec[prvM] <- mkCsrReg(0);
|
||||
Vector#(4, Reg#(Bit#(1))) software_int_pend_vec = replicate(readOnlyReg(0));
|
||||
software_int_pend_vec[prvU] <- mkCsrReg(0);
|
||||
software_int_pend_vec[prvS] <- mkCsrReg(0);
|
||||
software_int_pend_vec[prvM] <- mkCsrReg(0);
|
||||
Reg#(Data) mip_csr = concatReg13(
|
||||
readOnlyReg(52'b0),
|
||||
external_int_pend_vec[prvM], readOnlyReg(1'b0),
|
||||
external_int_pend_vec[prvS], external_int_pend_vec[prvU],
|
||||
readOnlyReg(timer_int_pend_vec[prvM]), // MTIP is read-only to software
|
||||
readOnlyReg(1'b0),
|
||||
timer_int_pend_vec[prvS], timer_int_pend_vec[prvU],
|
||||
software_int_pend_vec[prvM], readOnlyReg(1'b0),
|
||||
software_int_pend_vec[prvS], software_int_pend_vec[prvU]
|
||||
);
|
||||
// minstret
|
||||
Ehr#(2, Data) minstret_ehr <- mkCsrEhr(0);
|
||||
Reg#(Data) minstret_csr = minstret_ehr[0];
|
||||
// mcycle
|
||||
Ehr#(2, Data) mcycle_ehr <- mkCsrEhr(0);
|
||||
Reg#(Data) mcycle_csr = mcycle_ehr[0];
|
||||
// mvendorid
|
||||
Reg#(Data) mvendorid_csr = readOnlyReg(0);
|
||||
// marchid
|
||||
Reg#(Data) marchid_csr = readOnlyReg(0);
|
||||
// mimpid
|
||||
Reg#(Data) mimpid_csr = readOnlyReg(0);
|
||||
// mhartid
|
||||
Reg#(Data) mhartid_csr = readOnlyReg(hartid);
|
||||
|
||||
// Supervisor level CSRs
|
||||
// sstatus: restricted view of mstatus
|
||||
Reg#(Data) sstatus_csr = concatReg17(
|
||||
sd_reg, readOnlyReg(29'b0), uxl_reg, readOnlyReg(12'b0),
|
||||
mxr_reg, sum_reg, readOnlyReg(1'b0), xs_reg, fs_reg,
|
||||
readOnlyReg(4'b0), spp_reg,
|
||||
readOnlyReg(2'b0), prev_ie_vec[prvS], prev_ie_vec[prvU],
|
||||
readOnlyReg(2'b0), ie_vec[prvS], ie_vec[prvU]
|
||||
);
|
||||
// sie: restricted view of mie
|
||||
Reg#(Data) sie_csr = concatReg9(
|
||||
readOnlyReg(54'b0),
|
||||
external_int_en_vec[prvS], external_int_en_vec[prvU],
|
||||
readOnlyReg(2'b0),
|
||||
timer_int_en_vec[prvS], timer_int_en_vec[prvU],
|
||||
readOnlyReg(2'b0),
|
||||
software_int_en_vec[prvS], software_int_en_vec[prvU]
|
||||
);
|
||||
// stvec
|
||||
Reg#(Bit#(62)) stvec_base_hi_reg <- mkCsrReg(0); // BASE[63:2]
|
||||
Reg#(Bit#(1)) stvec_mode_low_reg <- mkCsrReg(0); // MODE[0]
|
||||
Reg#(Data) stvec_csr = concatReg3(
|
||||
stvec_base_hi_reg, readOnlyReg(1'b0), stvec_mode_low_reg
|
||||
);
|
||||
// scounteren
|
||||
Reg#(Bit#(1)) scounteren_ir_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) scounteren_tm_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(1)) scounteren_cy_reg <- mkCsrReg(0);
|
||||
Reg#(Data) scounteren_csr = concatReg5(
|
||||
readOnlyReg(32'b0),
|
||||
readOnlyReg(29'b0), // hpmcounter 3-31 not accessible in U mode
|
||||
scounteren_ir_reg, scounteren_tm_reg, scounteren_cy_reg
|
||||
);
|
||||
// sscratch
|
||||
Reg#(Data) sscratch_csr <- mkCsrReg(0);
|
||||
// sepc: FIXME Since we don't have C extension, sepc should be 4-byte
|
||||
// aligned. However, spike is not checking this, so we don't implement it.
|
||||
Reg#(Data) sepc_csr <- mkCsrReg(0);
|
||||
// scause
|
||||
Reg#(Bit#(1)) scause_interrupt_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(4)) scause_code_reg <- mkCsrReg(0);
|
||||
Reg#(Data) scause_csr = concatReg3(
|
||||
scause_interrupt_reg, readOnlyReg(59'b0), scause_code_reg
|
||||
);
|
||||
// stval (sbadaddr in spike)
|
||||
Reg#(Data) stval_csr <- mkCsrReg(0);
|
||||
// sip: restricted view of mip
|
||||
Reg#(Data) sip_csr = concatReg9(
|
||||
readOnlyReg(54'b0),
|
||||
external_int_pend_vec[prvS], external_int_pend_vec[prvU],
|
||||
readOnlyReg(2'b0),
|
||||
timer_int_pend_vec[prvS], timer_int_pend_vec[prvU],
|
||||
readOnlyReg(2'b0),
|
||||
software_int_pend_vec[prvS], software_int_pend_vec[prvU]
|
||||
);
|
||||
// satp (sptbr in spike): FIXME we only support Bare and Sv39, so we hack
|
||||
// the encoding of mode[3:0] field. Only mode[3] is relevant, other bits
|
||||
// are always 0
|
||||
Reg#(Bit#(1)) vm_mode_sv39_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(4)) vm_mode_reg = concatReg2(vm_mode_sv39_reg, readOnlyReg(3'b0));
|
||||
Reg#(Asid) asid_reg <- mkCsrReg(0);
|
||||
Reg#(Bit#(16)) full_asid_reg = zeroExtendReg(asid_reg);
|
||||
Reg#(Bit#(44)) ppn_reg <- mkCsrReg(0);
|
||||
Reg#(Data) satp_csr = concatReg3(vm_mode_reg, full_asid_reg, ppn_reg);
|
||||
|
||||
// User level CSRs
|
||||
// According to spike, any write to fflags/frm/fcsr will set fs_reg as
|
||||
// dirty, regardless of whether the write truly changes value or not.
|
||||
// Besides, any non-zero FP exception flags will also make fs_reg dirty.
|
||||
// fflags: if we directly change fflags_reg (instead of fflags_csr), then
|
||||
// we must set fs_reg manually
|
||||
Reg#(Bit#(5)) fflags_reg <- mkCsrReg(0);
|
||||
Reg#(Data) fflags_csr = addWriteSideEffect(
|
||||
zeroExtendReg(fflags_reg), fs_reg._write(2'b11)
|
||||
);
|
||||
// frm: if we directly change frm_reg (instead of frm_csr), then we must
|
||||
// set fs_reg manually
|
||||
Reg#(Bit#(3)) frm_reg <- mkCsrReg(0);
|
||||
Reg#(Data) frm_csr = addWriteSideEffect(
|
||||
zeroExtendReg(frm_reg), fs_reg._write(2'b11)
|
||||
);
|
||||
// fcsr
|
||||
Reg#(Data) fcsr_csr = addWriteSideEffect(
|
||||
zeroExtendReg(concatReg2(frm_reg, fflags_reg)), fs_reg._write(2'b11)
|
||||
);
|
||||
// cycle
|
||||
Reg#(Data) cycle_csr = readOnlyReg(mcycle_csr);
|
||||
// time
|
||||
Reg#(Data) time_reg <- mkCsrReg(0);
|
||||
Reg#(Data) time_csr = readOnlyReg(time_reg);
|
||||
// instret
|
||||
Reg#(Data) instret_csr = readOnlyReg(minstret_csr);
|
||||
// terminate (non-standard)
|
||||
Terminate terminate_module <- mkTerminate;
|
||||
Reg#(Data) terminate_csr = terminate_module.reg_ifc;
|
||||
// whether performance stats is collected
|
||||
StatsCsr stats_module <- mkStatsCsr;
|
||||
Reg#(Data) stats_csr = stats_module.reg_ifc;
|
||||
|
||||
`ifdef SECURITY
|
||||
// sanctum machine CSRs
|
||||
|
||||
// ### Enclave virtual base and mask
|
||||
// (per-core) registers
|
||||
// ( defines a virtual region for which enclave page tables are used in
|
||||
// place of OS-controlled page tables)
|
||||
// (machine-mode non-standard read/write)
|
||||
Reg#(Data) mevbase_csr <- mkCsrReg(maxBound); // impossible base & mask,
|
||||
Reg#(Data) mevmask_csr <- mkCsrReg(0); // so no enclave accesses are possible
|
||||
|
||||
// ### Enclave page table base
|
||||
// (per core) register
|
||||
// ( pointer to a separate page table data structure used to translate enclave
|
||||
// virtual addresses)
|
||||
// (machine-mode non-standard read/write)
|
||||
Reg#(Bit#(44)) eppn_reg <- mkCsrReg(0);
|
||||
Reg#(Data) meatp_csr = zeroExtendReg(eppn_reg);
|
||||
|
||||
// ### DRAM bitmap
|
||||
// (per core) registers (OS and Enclave)
|
||||
// ( white-lists the DRAM regions the core is allowed to access via OS and
|
||||
// enclave virtual addresses)
|
||||
// (machine-mode non-standard read/write)
|
||||
Reg#(Data) mmrbm_csr <- mkCsrReg(maxBound);
|
||||
Reg#(Data) memrbm_csr <- mkCsrReg(0);
|
||||
|
||||
// ### Protected region base and mask
|
||||
// (per core) registers (OS and Enclave)
|
||||
// ( these are used to prevent address translation into a specific range of
|
||||
// physical addresses, for example to protect the security monitor from all software)
|
||||
// (machine-mode non-standard read/write)
|
||||
Reg#(Data) mparbase_csr <- mkCsrReg(maxBound);
|
||||
Reg#(Data) mparmask_csr <- mkCsrReg(0);
|
||||
Reg#(Data) meparbase_csr <- mkCsrReg(0);
|
||||
Reg#(Data) meparmask_csr <- mkCsrReg(0);
|
||||
|
||||
// ### Turn on/off speculation
|
||||
Reg#(Bit#(2)) mspec_reg <- mkCsrReg(mSpecAll);
|
||||
Reg#(Data) mspec_csr = zeroExtendReg(mspec_reg);
|
||||
|
||||
// sanctum user CSR
|
||||
// ### true random number
|
||||
// For now, we skip secure boot, keep TRNG = 0
|
||||
Reg#(Data) trng_csr <- mkReadOnlyReg(0); //mkTRNG;
|
||||
`endif
|
||||
|
||||
rule incCycle;
|
||||
mcycle_ehr[1] <= mcycle_ehr[1] + 1;
|
||||
endrule
|
||||
|
||||
// Function for getting a csr given an index
|
||||
function Reg#(Data) get_csr(CSR csr);
|
||||
return (case (csr)
|
||||
// User CSRs
|
||||
CSRfflags: fflags_csr;
|
||||
CSRfrm: frm_csr;
|
||||
CSRfcsr: fcsr_csr;
|
||||
CSRcycle: cycle_csr;
|
||||
CSRtime: time_csr;
|
||||
CSRinstret: instret_csr;
|
||||
CSRterminate: terminate_csr;
|
||||
CSRstats: stats_csr;
|
||||
// Supervisor CSRs
|
||||
CSRsstatus: sstatus_csr;
|
||||
CSRsie: sie_csr;
|
||||
CSRstvec: stvec_csr;
|
||||
CSRscounteren: scounteren_csr;
|
||||
CSRsscratch: sscratch_csr;
|
||||
CSRsepc: sepc_csr;
|
||||
CSRscause: scause_csr;
|
||||
CSRstval: stval_csr;
|
||||
CSRsip: sip_csr;
|
||||
CSRsatp: satp_csr;
|
||||
// Machine CSRs
|
||||
CSRmstatus: mstatus_csr;
|
||||
CSRmisa: misa_csr;
|
||||
CSRmedeleg: medeleg_csr;
|
||||
CSRmideleg: mideleg_csr;
|
||||
CSRmie: mie_csr;
|
||||
CSRmtvec: mtvec_csr;
|
||||
CSRmcounteren: mcounteren_csr;
|
||||
CSRmscratch: mscratch_csr;
|
||||
CSRmepc: mepc_csr;
|
||||
CSRmcause: mcause_csr;
|
||||
CSRmtval: mtval_csr;
|
||||
CSRmip: mip_csr;
|
||||
CSRmcycle: mcycle_csr;
|
||||
CSRminstret: minstret_csr;
|
||||
CSRmvendorid: mvendorid_csr;
|
||||
CSRmarchid: marchid_csr;
|
||||
CSRmimpid: mimpid_csr;
|
||||
CSRmhartid: mhartid_csr;
|
||||
`ifdef SECURITY
|
||||
CSRmevbase: mevbase_csr;
|
||||
CSRmevmask: mevmask_csr;
|
||||
CSRmeatp: meatp_csr;
|
||||
CSRmmrbm: mmrbm_csr;
|
||||
CSRmemrbm: memrbm_csr;
|
||||
CSRmparbase: mparbase_csr;
|
||||
CSRmparmask: mparmask_csr;
|
||||
CSRmeparbase: meparbase_csr;
|
||||
CSRmeparmask: meparmask_csr;
|
||||
CSRmspec: mspec_csr;
|
||||
CSRtrng: trng_csr;
|
||||
`endif
|
||||
default: readOnlyReg(64'b0);
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
method Data rd(CSR csr);
|
||||
return get_csr(csr)._read;
|
||||
endmethod
|
||||
|
||||
method Action csrInstWr(CSR csr, Data x);
|
||||
get_csr(csr)._write(x);
|
||||
endmethod
|
||||
|
||||
method Bool fpuInstNeedWr(Bit#(5) fflags, Bool fpu_dirty);
|
||||
Bool fflags_change = (fflags & fflags_reg) != fflags;
|
||||
// we need to set fs_reg as dirty in two cases
|
||||
// 1. FP reg is written (i.e., fpu_dirty)
|
||||
// 2. FP exception (i.e., fflags) is non-zero (try to match spike)
|
||||
Bool need_set_dirty = fs_reg != 2'b11 && (fpu_dirty || fflags != 0);
|
||||
return fflags_change || need_set_dirty;
|
||||
endmethod
|
||||
|
||||
method Action fpuInstWr(Bit#(5) fflags);
|
||||
fs_reg <= 2'b11; // FPU must be dirty
|
||||
fflags_reg <= fflags_reg | fflags;
|
||||
endmethod
|
||||
|
||||
method Maybe#(Interrupt) pending_interrupt;
|
||||
// first get all the pending interrupts
|
||||
Bit#(InterruptNum) pend_ints = truncate(mie_csr & mip_csr);
|
||||
// now find out all the truly enabled interrupts (that needs handling)
|
||||
Bit#(InterruptNum) enabled_ints = 0;
|
||||
// check interrupts that needs to be handled at M mode: all interrupts
|
||||
// are by default handled at M mode unless it is delegated in
|
||||
// mideleg_csr, we just need to ignore those interrupts
|
||||
if(prv_reg < prvM || (prv_reg == prvM && ie_vec[prvM] == 1)) begin
|
||||
enabled_ints = pend_ints & ~truncate(mideleg_csr);
|
||||
end
|
||||
// check interrupts that needs to be handled at S mode only if no
|
||||
// interrupt needs to be handled at M mode: interrupts handled at S
|
||||
// mode must be delegated in mideleg_csr
|
||||
if (enabled_ints == 0 &&
|
||||
(prv_reg < prvS || (prv_reg == prvS && ie_vec[prvS] == 1))) begin
|
||||
enabled_ints = pend_ints & truncate(mideleg_csr);
|
||||
end
|
||||
// According to spike, return the interrupt bit at LSB
|
||||
function Bool isEnabled(Integer i) = (enabled_ints[i] == 1);
|
||||
Vector#(InterruptNum, Integer) idxVec = genVector;
|
||||
if(find(isEnabled, idxVec) matches tagged Valid .i) begin
|
||||
return Valid (unpack(fromInteger(i)));
|
||||
end
|
||||
else begin
|
||||
return Invalid;
|
||||
end
|
||||
endmethod
|
||||
|
||||
method ActionValue#(Addr) trap(Trap t, Addr pc, Addr addr);
|
||||
// figure out trap cause & trap val
|
||||
Bit#(1) cause_interrupt = 0;
|
||||
Bit#(4) cause_code = 0;
|
||||
Data trap_val = 0;
|
||||
case(t) matches
|
||||
tagged Exception .e: begin
|
||||
cause_code = pack(e);
|
||||
trap_val = (case(e)
|
||||
InstAddrMisaligned, InstAccessFault,
|
||||
Breakpoint, InstPageFault: return pc;
|
||||
LoadAddrMisaligned, LoadAccessFault,
|
||||
StoreAddrMisaligned, StoreAccessFault,
|
||||
LoadPageFault, StorePageFault: return addr;
|
||||
default: return 0;
|
||||
endcase);
|
||||
end
|
||||
tagged Interrupt .i: begin
|
||||
cause_code = pack(i);
|
||||
cause_interrupt = 1;
|
||||
end
|
||||
endcase
|
||||
// function to figure out next PC
|
||||
function Addr getNextPc(Bit#(1) mode_low, Bit#(62) base_hi);
|
||||
Addr base = {base_hi, 2'b0};
|
||||
if(mode_low == 1 && cause_interrupt == 1) begin
|
||||
// vector jump: only for interrupt
|
||||
return base + zeroExtend({cause_code, 2'b0});
|
||||
end
|
||||
else begin // direct jump
|
||||
return base;
|
||||
end
|
||||
endfunction
|
||||
// check if trap is delegated
|
||||
Bool deleg = prv_reg <= prvS && (case(t) matches
|
||||
tagged Exception .e: return medeleg_csr[pack(e)] == 1;
|
||||
tagged Interrupt .i: return mideleg_csr[pack(i)] == 1;
|
||||
default: return False;
|
||||
endcase);
|
||||
// handle the trap
|
||||
if(deleg) begin // handle in S mode
|
||||
// ie/prv stack
|
||||
prev_prv_vec[prvS] <= prv_reg;
|
||||
prv_reg <= prvS;
|
||||
prev_ie_vec[prvS] <= ie_vec[prvS];
|
||||
ie_vec[prvS] <= 0;
|
||||
// record trap info
|
||||
sepc_csr <= pc;
|
||||
scause_interrupt_reg <= cause_interrupt;
|
||||
scause_code_reg <= cause_code;
|
||||
stval_csr <= trap_val;
|
||||
// return next pc
|
||||
return getNextPc(stvec_mode_low_reg, stvec_base_hi_reg);
|
||||
end
|
||||
else begin
|
||||
// ie/prv stack
|
||||
prev_prv_vec[prvM] <= prv_reg;
|
||||
prv_reg <= prvM;
|
||||
prev_ie_vec[prvM] <= ie_vec[prvM];
|
||||
ie_vec[prvM] <= 0;
|
||||
// record trap info
|
||||
mepc_csr <= pc;
|
||||
mcause_interrupt_reg <= cause_interrupt;
|
||||
mcause_code_reg <= cause_code;
|
||||
mtval_csr <= trap_val;
|
||||
// return next pc
|
||||
return getNextPc(mtvec_mode_low_reg, mtvec_base_hi_reg);
|
||||
end
|
||||
// XXX yield load reservation should be done outside this method
|
||||
endmethod
|
||||
|
||||
method ActionValue#(Addr) mret;
|
||||
prv_reg <= prev_prv_vec[prvM];
|
||||
prev_prv_vec[prvM] <= prvU;
|
||||
ie_vec[prvM] <= prev_ie_vec[prvM];
|
||||
prev_ie_vec[prvM] <= 1;
|
||||
return mepc_csr;
|
||||
endmethod
|
||||
|
||||
method ActionValue#(Addr) sret;
|
||||
prv_reg <= prev_prv_vec[prvS];
|
||||
prev_prv_vec[prvS] <= prvU;
|
||||
ie_vec[prvS] <= prev_ie_vec[prvS];
|
||||
prev_ie_vec[prvS] <= 1;
|
||||
return sepc_csr;
|
||||
endmethod
|
||||
|
||||
method VMInfo vmI;
|
||||
// for inst fetch, NO need to consider MPRV
|
||||
Bit#(2) prv = prv_reg;
|
||||
return VMInfo {
|
||||
prv: prv,
|
||||
asid: asid_reg,
|
||||
sv39: prv < prvM && vm_mode_sv39_reg == 1,
|
||||
exeReadable: mxr_reg == 1,
|
||||
userAccessibleByS: sum_reg == 1,
|
||||
basePPN: ppn_reg
|
||||
`ifdef SECURITY
|
||||
, sanctum_evbase: mevbase_csr,
|
||||
sanctum_evmask: mevmask_csr,
|
||||
sanctum_ebasePPN: eppn_reg,
|
||||
sanctum_mrbm: mmrbm_csr,
|
||||
sanctum_emrbm: memrbm_csr,
|
||||
sanctum_parbase: mparbase_csr,
|
||||
sanctum_parmask: mparmask_csr,
|
||||
sanctum_eparbase: meparbase_csr,
|
||||
sanctum_eparmask: meparmask_csr,
|
||||
// enclave / security monitor should never execute instructions
|
||||
// from untrusted shared region
|
||||
sanctum_authShared: False
|
||||
`endif
|
||||
};
|
||||
endmethod
|
||||
|
||||
method VMInfo vmD;
|
||||
// for load/store, need to consider MPRV
|
||||
Bit#(2) prv = (mprv_reg == 1) ? prev_prv_vec[prvM] : prv_reg;
|
||||
return VMInfo {
|
||||
prv: prv,
|
||||
asid: asid_reg,
|
||||
sv39: prv < prvM && vm_mode_sv39_reg == 1,
|
||||
exeReadable: mxr_reg == 1,
|
||||
userAccessibleByS: sum_reg == 1,
|
||||
basePPN: ppn_reg
|
||||
`ifdef SECURITY
|
||||
, sanctum_evbase: mevbase_csr,
|
||||
sanctum_evmask: mevmask_csr,
|
||||
sanctum_ebasePPN: eppn_reg,
|
||||
sanctum_mrbm: mmrbm_csr,
|
||||
sanctum_emrbm: memrbm_csr,
|
||||
sanctum_parbase: mparbase_csr,
|
||||
sanctum_parmask: mparmask_csr,
|
||||
sanctum_eparbase: meparbase_csr,
|
||||
sanctum_eparmask: meparmask_csr,
|
||||
// enclave / security monitor can read/write untrusted shared
|
||||
// region when speculation is off (either by mspec CSR or in M
|
||||
// mode)
|
||||
// XXX Because of the effects of mprv, we have to use prv_reg here
|
||||
// instead of prv. Otherwise, we may be in M mode, but prv=S, and
|
||||
// still forbid shared accesses
|
||||
sanctum_authShared: mspec_reg != mSpecAll || prv_reg == prvM
|
||||
`endif
|
||||
};
|
||||
endmethod
|
||||
|
||||
method CsrDecodeInfo decodeInfo = CsrDecodeInfo {
|
||||
frm: frm_reg,
|
||||
fEnabled: fs_reg != 0,
|
||||
prv: prv_reg,
|
||||
trapVM: tvm_reg == 1,
|
||||
timeoutWait: tw_reg == 1,
|
||||
trapSret: tsr_reg == 1,
|
||||
cycleReadableByS: mcounteren_cy_reg == 1,
|
||||
cycleReadableByU: mcounteren_cy_reg == 1 && scounteren_cy_reg == 1,
|
||||
instretReadableByS: mcounteren_ir_reg == 1,
|
||||
instretReadableByU: mcounteren_ir_reg == 1 && scounteren_ir_reg == 1,
|
||||
timeReadableByS: mcounteren_tm_reg == 1,
|
||||
timeReadableByU: mcounteren_tm_reg == 1 && scounteren_tm_reg == 1
|
||||
};
|
||||
|
||||
method Action incInstret(SupCnt x);
|
||||
minstret_ehr[1] <= minstret_ehr[1] + zeroExtend(x);
|
||||
endmethod
|
||||
|
||||
method Action setTime(Data t);
|
||||
time_reg <= t;
|
||||
endmethod
|
||||
|
||||
method getMSIP = software_int_pend_vec[prvM]._read;
|
||||
method setMSIP = software_int_pend_vec[prvM]._write;
|
||||
method setMTIP = timer_int_pend_vec[prvM]._write;
|
||||
|
||||
// Bluespec: external interrupts targeting machine and supervisor modes
|
||||
method Action setMEIP (Bit #(1) v);
|
||||
external_int_pend_vec[prvM] <= v;
|
||||
endmethod
|
||||
method Action setSEIP (Bit #(1) v);
|
||||
external_int_pend_vec[prvS] <= v;
|
||||
endmethod
|
||||
|
||||
method terminate = terminate_module.terminate;
|
||||
|
||||
// performance stats
|
||||
method doPerfStats = stats_module.doPerfStats;
|
||||
method sendDoStats = stats_module.sendDoStats;
|
||||
method recvDoStats = stats_module.recvDoStats;
|
||||
endmodule
|
||||
277
src_Core/CPU/LLC_AXI4_Adapter.bsv
Normal file
277
src_Core/CPU/LLC_AXI4_Adapter.bsv
Normal file
@@ -0,0 +1,277 @@
|
||||
package LLC_AXI4_Adapter;
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
import ConfigReg :: *;
|
||||
import Assert :: *;
|
||||
import FIFOF :: *;
|
||||
import Vector :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import GetPut_Aux :: *;
|
||||
import Cur_Cycle :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
import CreditCounter :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// ----------------
|
||||
// From MIT RISCY-OOO
|
||||
|
||||
import Types :: *;
|
||||
import CacheUtils :: *;
|
||||
import CCTypes :: *;
|
||||
|
||||
// ----------------
|
||||
// From Bluespec Pipes
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
interface LLC_AXI4_Adapter_IFC;
|
||||
method Action reset;
|
||||
|
||||
// Fabric master interface for memory
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) mem_master;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
module mkLLC_AXi4_Adapter #(MemFifoClient #(idT, childT) llc)
|
||||
(LLC_AXI4_Adapter_IFC)
|
||||
provisos(Bits#(idT, a__),
|
||||
Bits#(childT, b__),
|
||||
FShow#(ToMemMsg#(idT, childT)),
|
||||
FShow#(MemRsMsg#(idT, childT)),
|
||||
Add#(SizeOf#(Line), 0, 512)); // assert Line sz = 512
|
||||
|
||||
// Verbosity: 0: quiet; 1: LLC transactions; 2: loop detail
|
||||
Integer verbosity = 2;
|
||||
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (fromInteger (verbosity));
|
||||
|
||||
// ================================================================
|
||||
// Fabric request/response
|
||||
|
||||
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor_2;
|
||||
|
||||
// For discarding write-responses
|
||||
CreditCounter_IFC #(4) ctr_wr_rsps_pending <- mkCreditCounter; // Max 15 writes outstanding
|
||||
|
||||
// ================================================================
|
||||
// Functions to interact with the fabric
|
||||
|
||||
// Send a read-request into the fabric
|
||||
function Action fa_fabric_send_read_req (Fabric_Addr addr);
|
||||
action
|
||||
AXI4_Size size = axsize_8;
|
||||
let mem_req_rd_addr = AXI4_Rd_Addr {arid: fabric_default_id,
|
||||
araddr: addr,
|
||||
arlen: 0, // burst len = arlen+1
|
||||
arsize: size,
|
||||
arburst: fabric_default_burst,
|
||||
arlock: fabric_default_lock,
|
||||
arcache: fabric_default_arcache,
|
||||
arprot: fabric_default_prot,
|
||||
arqos: fabric_default_qos,
|
||||
arregion: fabric_default_region,
|
||||
aruser: fabric_default_user};
|
||||
|
||||
master_xactor.i_rd_addr.enq (mem_req_rd_addr);
|
||||
|
||||
// Debugging
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display (" ", fshow (mem_req_rd_addr));
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// Send a write-request into the fabric
|
||||
function Action fa_fabric_send_write_req (Fabric_Addr addr, Fabric_Strb strb, Bit #(64) st_val);
|
||||
action
|
||||
AXI4_Size size = axsize_8;
|
||||
let mem_req_wr_addr = AXI4_Wr_Addr {awid: fabric_default_id,
|
||||
awaddr: addr,
|
||||
awlen: 0, // burst len = awlen+1
|
||||
awsize: size,
|
||||
awburst: fabric_default_burst,
|
||||
awlock: fabric_default_lock,
|
||||
awcache: fabric_default_awcache,
|
||||
awprot: fabric_default_prot,
|
||||
awqos: fabric_default_qos,
|
||||
awregion: fabric_default_region,
|
||||
awuser: fabric_default_user};
|
||||
|
||||
let mem_req_wr_data = AXI4_Wr_Data {wid: fabric_default_id,
|
||||
wdata: st_val,
|
||||
wstrb: strb,
|
||||
wlast: True,
|
||||
wuser: fabric_default_user};
|
||||
|
||||
master_xactor.i_wr_addr.enq (mem_req_wr_addr);
|
||||
master_xactor.i_wr_data.enq (mem_req_wr_data);
|
||||
|
||||
// Expect a fabric response
|
||||
ctr_wr_rsps_pending.incr;
|
||||
|
||||
// Debugging
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display (" To fabric: ", fshow (mem_req_wr_addr));
|
||||
$display (" ", fshow (mem_req_wr_data));
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Handle read requests and responses
|
||||
// Don't do reads while writes are outstanding.
|
||||
|
||||
// Each 512b cache line takes 8 beats, each handling 64 bits
|
||||
Reg #(Bit #(3)) rg_rd_req_beat <- mkReg (0);
|
||||
Reg #(Bit #(3)) rg_rd_rsp_beat <- mkReg (0);
|
||||
|
||||
FIFOF #(LdMemRq #(idT, childT)) f_pending_reads <- mkFIFOF;
|
||||
Reg #(Bit #(512)) rg_cline <- mkRegU;
|
||||
|
||||
rule rl_handle_read_req (llc.toM.first matches tagged Ld .ld
|
||||
&&& (ctr_wr_rsps_pending.value == 0));
|
||||
if ((cfg_verbosity > 0) && (rg_rd_req_beat == 0)) begin
|
||||
$display ("%0d: LLC_AXI4_Adapter.rl_handle_read_req: Ld request from LLC to memory: beat %0d",
|
||||
cur_cycle, rg_rd_req_beat);
|
||||
$display (" ", fshow (ld));
|
||||
end
|
||||
|
||||
Addr line_addr = { ld.addr [63:6], 6'h0 }; // Addr of containing cache line
|
||||
Addr offset = zeroExtend ( { rg_rd_req_beat, 3'b_000 } ); // Addr offset of 64b word for this beat
|
||||
fa_fabric_send_read_req (line_addr | offset);
|
||||
|
||||
if (rg_rd_req_beat == 0)
|
||||
f_pending_reads.enq (ld);
|
||||
|
||||
if (rg_rd_req_beat == 7)
|
||||
llc.toM.deq;
|
||||
|
||||
rg_rd_req_beat <= rg_rd_req_beat + 1;
|
||||
endrule
|
||||
|
||||
rule rl_handle_read_rsps;
|
||||
let mem_rsp <- pop_o (master_xactor.o_rd_data);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: LLC_AXI4_Adapter.rl_handle_read_rsps: beat %0d ", cur_cycle, rg_rd_rsp_beat);
|
||||
$display (" ", fshow (mem_rsp));
|
||||
end
|
||||
|
||||
if (mem_rsp.rresp != axi4_resp_okay) begin
|
||||
// TODO: need to raise a non-maskable interrupt (NMI) here
|
||||
$display ("%0d: LLC_AXI4_Adapter.rl_handle_read_rsp: fabric response error; exit", cur_cycle);
|
||||
$display (" ", fshow (mem_rsp));
|
||||
$finish (1);
|
||||
end
|
||||
|
||||
// Shift next 64 bits from fabric into the cache line being assembled
|
||||
let new_cline = { mem_rsp.rdata, rg_cline [511:64] };
|
||||
|
||||
if (rg_rd_rsp_beat == 7) begin
|
||||
let ldreq <- pop (f_pending_reads);
|
||||
MemRsMsg #(idT, childT) resp = MemRsMsg {data: unpack (new_cline),
|
||||
child: ldreq.child,
|
||||
id: ldreq.id};
|
||||
|
||||
llc.rsFromM.enq (resp);
|
||||
|
||||
if (cfg_verbosity > 1)
|
||||
$display (" Response to LLC: ", fshow (resp));
|
||||
end
|
||||
|
||||
rg_cline <= new_cline;
|
||||
rg_rd_rsp_beat <= rg_rd_rsp_beat + 1;
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Handle write requests and responses
|
||||
|
||||
// Each 512b cache line takes 8 beats, each handling 64 bits
|
||||
Reg #(Bit #(3)) rg_wr_req_beat <- mkReg (0);
|
||||
Reg #(Bit #(3)) rg_wr_rsp_beat <- mkReg (0);
|
||||
|
||||
FIFOF #(WbMemRs) f_pending_writes <- mkFIFOF;
|
||||
|
||||
rule rl_handle_write_req (llc.toM.first matches tagged Wb .wb);
|
||||
if ((cfg_verbosity > 0) && (rg_wr_req_beat == 0)) begin
|
||||
$display ("%d: LLC_AXI4_Adapter.rl_handle_write_req: Wb request from LLC to memory:", cur_cycle);
|
||||
$display (" ", fshow (wb));
|
||||
end
|
||||
|
||||
Addr line_addr = { wb.addr [63:6], 6'h0 }; // Addr of containing cache line
|
||||
Line line_data = wb.data;
|
||||
Vector #(8, Bit #(8)) line_bes = unpack (pack (wb.byteEn));
|
||||
|
||||
Addr offset = zeroExtend ( { rg_wr_req_beat, 3'b_000 } ); // Addr offset of 64b word for this beat
|
||||
Bit #(64) data64 = line_data [rg_wr_req_beat];
|
||||
Bit #(8) strb8 = line_bes [rg_wr_req_beat];
|
||||
fa_fabric_send_write_req (line_addr | offset, strb8, data64);
|
||||
|
||||
if (rg_wr_req_beat == 0)
|
||||
f_pending_writes.enq (wb);
|
||||
|
||||
if (rg_wr_req_beat == 7)
|
||||
llc.toM.deq;
|
||||
|
||||
rg_wr_req_beat <= rg_wr_req_beat + 1;
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Discard write-responses from the fabric
|
||||
|
||||
rule rl_discard_write_rsp;
|
||||
let wr_resp <- pop_o (master_xactor.o_wr_resp);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: LLC_AXI4_Adapter.rl_discard_write_rsp: beat %0d ", cur_cycle, rg_wr_rsp_beat);
|
||||
$display (" ", fshow (wr_resp));
|
||||
end
|
||||
|
||||
if (ctr_wr_rsps_pending.value == 0) begin
|
||||
$display ("%0d: ERROR: LLC_AXI4_Adapter.rl_discard_write_rsp: unexpected Wr response (ctr_wr_rsps_pending.value == 0)",
|
||||
cur_cycle);
|
||||
$display (" ", fshow (wr_resp));
|
||||
$finish (1); // Assertion failure
|
||||
end
|
||||
|
||||
ctr_wr_rsps_pending.decr;
|
||||
|
||||
if (wr_resp.bresp != axi4_resp_okay) begin
|
||||
// TODO: need to raise a non-maskable interrupt (NMI) here
|
||||
$display ("%0d: LLC_AXI4_Adapter.rl_discard_write_rsp: fabric response error: exit", cur_cycle);
|
||||
$display (" ", fshow (wr_resp));
|
||||
$finish (1);
|
||||
end
|
||||
|
||||
if (rg_wr_rsp_beat == 7) begin
|
||||
let wrreq <- pop (f_pending_writes);
|
||||
// LLC does not expect any response for writes
|
||||
end
|
||||
|
||||
rg_wr_rsp_beat <= rg_wr_rsp_beat + 1;
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
method Action reset;
|
||||
ctr_wr_rsps_pending.clear;
|
||||
endmethod
|
||||
|
||||
// Fabric interface for memory
|
||||
interface mem_master = master_xactor.axi_side;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
1076
src_Core/CPU/MMIOPlatform.bsv
Normal file
1076
src_Core/CPU/MMIOPlatform.bsv
Normal file
File diff suppressed because it is too large
Load Diff
253
src_Core/CPU/MMIO_AXI4_Adapter.bsv
Normal file
253
src_Core/CPU/MMIO_AXI4_Adapter.bsv
Normal file
@@ -0,0 +1,253 @@
|
||||
package MMIO_AXI4_Adapter;
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
import Assert :: *;
|
||||
import ConfigReg :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import GetPut_Aux :: *;
|
||||
import Cur_Cycle :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
import CreditCounter :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// ----------------
|
||||
// From MIT RISCY-OOO
|
||||
|
||||
import ProcTypes :: *;
|
||||
|
||||
// ----------------
|
||||
// From Bluespec Pipes
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
interface MMIO_AXI4_Adapter_IFC;
|
||||
method Action reset;
|
||||
|
||||
interface Server #(MMIOCRq, MMIODataPRs) core_side;
|
||||
|
||||
// Fabric master interface for IO
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) mmio_master;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
module mkMMIO_AXI4_Adapter (MMIO_AXI4_Adapter_IFC);
|
||||
|
||||
// Verbosity: 0: quiet; 1: transactions
|
||||
Integer verbosity = 2;
|
||||
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (fromInteger (verbosity));
|
||||
|
||||
// ================================================================
|
||||
// Requests from and responses to core
|
||||
|
||||
FIFOF #(MMIOCRq) f_reqs_from_core <- mkFIFOF;
|
||||
FIFOF #(MMIODataPRs) f_rsps_to_core <- mkFIFOF;
|
||||
|
||||
// ================================================================
|
||||
// Fabric request/response
|
||||
|
||||
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor_2;
|
||||
|
||||
// For discarding write-responses
|
||||
CreditCounter_IFC #(4) ctr_wr_rsps_pending <- mkCreditCounter; // Max 15 writes outstanding
|
||||
|
||||
// ================================================================
|
||||
// Functions to interact with the fabric
|
||||
|
||||
// Send a read-request into the fabric
|
||||
function Action fa_fabric_send_read_req (Fabric_Addr addr);
|
||||
action
|
||||
AXI4_Size size = axsize_8;
|
||||
let mem_req_rd_addr = AXI4_Rd_Addr {arid: fabric_default_id,
|
||||
araddr: addr,
|
||||
arlen: 0, // burst len = arlen+1
|
||||
arsize: size,
|
||||
arburst: fabric_default_burst,
|
||||
arlock: fabric_default_lock,
|
||||
arcache: fabric_default_arcache,
|
||||
arprot: fabric_default_prot,
|
||||
arqos: fabric_default_qos,
|
||||
arregion: fabric_default_region,
|
||||
aruser: fabric_default_user};
|
||||
|
||||
master_xactor.i_rd_addr.enq (mem_req_rd_addr);
|
||||
|
||||
// Debugging
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display (" ", fshow (mem_req_rd_addr));
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// Send a write-request into the fabric
|
||||
function Action fa_fabric_send_write_req (Fabric_Addr addr, Fabric_Strb strb, Bit #(64) st_val);
|
||||
action
|
||||
AXI4_Size size = axsize_8;
|
||||
let mem_req_wr_addr = AXI4_Wr_Addr {awid: fabric_default_id,
|
||||
awaddr: addr,
|
||||
awlen: 0, // burst len = awlen+1
|
||||
awsize: size,
|
||||
awburst: fabric_default_burst,
|
||||
awlock: fabric_default_lock,
|
||||
awcache: fabric_default_awcache,
|
||||
awprot: fabric_default_prot,
|
||||
awqos: fabric_default_qos,
|
||||
awregion: fabric_default_region,
|
||||
awuser: fabric_default_user};
|
||||
|
||||
let mem_req_wr_data = AXI4_Wr_Data {wid: fabric_default_id,
|
||||
wdata: st_val,
|
||||
wstrb: strb,
|
||||
wlast: True,
|
||||
wuser: fabric_default_user};
|
||||
|
||||
master_xactor.i_wr_addr.enq (mem_req_wr_addr);
|
||||
master_xactor.i_wr_data.enq (mem_req_wr_data);
|
||||
|
||||
// Expect a fabric response
|
||||
ctr_wr_rsps_pending.incr;
|
||||
|
||||
// Debugging
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display (" To fabric: ", fshow (mem_req_wr_addr));
|
||||
$display (" ", fshow (mem_req_wr_data));
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Handle read requests and responses.
|
||||
// Don't do a read while a write is outstanding.
|
||||
// This is just an adapter from MMIOCRq/MMIODataPRs to AXI4
|
||||
|
||||
rule rl_handle_read_req (f_reqs_from_core.first.func matches Ld
|
||||
&&& (ctr_wr_rsps_pending.value == 0));
|
||||
let req <- pop (f_reqs_from_core);
|
||||
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_req: Ld request", cur_cycle);
|
||||
$display (" ", fshow (req));
|
||||
end
|
||||
|
||||
fa_fabric_send_read_req (req.addr);
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
|
||||
rule rl_handle_read_rsps;
|
||||
let mem_rsp <- pop_o (master_xactor.o_rd_data);
|
||||
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_rsps ", cur_cycle);
|
||||
$display (" ", fshow (mem_rsp));
|
||||
end
|
||||
|
||||
if (mem_rsp.rresp != axi4_resp_okay) begin
|
||||
// TODO: need to raise a non-maskable interrupt (NMI) here
|
||||
$display ("%0d: MMIO_AXI4_Adapter.rl_handle_read_rsp: fabric response error; exit", cur_cycle);
|
||||
$display (" ", fshow (mem_rsp));
|
||||
$finish (1);
|
||||
end
|
||||
|
||||
let rsp = MMIODataPRs {valid: True, data: mem_rsp.rdata};
|
||||
f_rsps_to_core.enq (rsp);
|
||||
|
||||
if (cfg_verbosity > 0)
|
||||
$display (" Response MMIO to core: ", fshow (rsp));
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Handle write requests and responses
|
||||
|
||||
rule rl_handle_write_req (f_reqs_from_core.first.func matches St);
|
||||
let req <- pop (f_reqs_from_core);
|
||||
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display ("%d: MMIO_AXI4_Adapter.rl_handle_write_req: St request:", cur_cycle);
|
||||
$display (" ", fshow (req));
|
||||
end
|
||||
|
||||
fa_fabric_send_write_req (req.addr, pack (req.byteEn), req.data);
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Discard write-responses from the fabric
|
||||
|
||||
rule rl_discard_write_rsp;
|
||||
let wr_resp <- pop_o (master_xactor.o_wr_resp);
|
||||
|
||||
if (cfg_verbosity > 0) begin
|
||||
$display ("%0d: MMIO_AXI4_Adapter.rl_discard_write_rsp", cur_cycle);
|
||||
$display (" ", fshow (wr_resp));
|
||||
end
|
||||
|
||||
if (ctr_wr_rsps_pending.value == 0) begin
|
||||
$display ("%0d: ERROR: MMIO_AXI4_Adapter.rl_discard_write_rsp: unexpected Wr response (ctr_wr_rsps_pending.value == 0)",
|
||||
cur_cycle);
|
||||
$display (" ", fshow (wr_resp));
|
||||
$finish (1); // Assertion failure
|
||||
end
|
||||
|
||||
ctr_wr_rsps_pending.decr;
|
||||
|
||||
if (wr_resp.bresp != axi4_resp_okay) begin
|
||||
// TODO: need to raise a non-maskable interrupt (NMI) here
|
||||
$display ("%0d: MMIO_AXI4_Adapter.rl_discard_write_rsp: fabric response error: exit", cur_cycle);
|
||||
$display (" ", fshow (wr_resp));
|
||||
$finish (1);
|
||||
end
|
||||
else begin
|
||||
let rsp = MMIODataPRs {valid: True, data: 0};
|
||||
f_rsps_to_core.enq (rsp);
|
||||
end
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// This adapter should only receive Ld/St requests, no Inst or AMO reqs.
|
||||
|
||||
function Bool fn_is_Ld_or_St (MMIOCRq req);
|
||||
return case (req.func) matches
|
||||
Ld : True;
|
||||
St : True;
|
||||
default: False;
|
||||
endcase;
|
||||
endfunction
|
||||
|
||||
rule rl_handle_non_Ld_St (! fn_is_Ld_or_St (f_reqs_from_core.first));
|
||||
let req <- pop (f_reqs_from_core);
|
||||
|
||||
$display ("%0d: ERROR: MMIO_AXI4_Adapter.rl_handle_non_Ld_St",
|
||||
cur_cycle);
|
||||
$display (" ", fshow (req));
|
||||
$finish (1); // Assertion failure
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
method Action reset;
|
||||
ctr_wr_rsps_pending.clear;
|
||||
endmethod
|
||||
|
||||
interface Server core_side = toGPServer (f_reqs_from_core, f_rsps_to_core);
|
||||
|
||||
// Fabric master interface for IO
|
||||
interface mmio_master = master_xactor.axi_side;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
358
src_Core/CPU/Proc.bsv
Normal file
358
src_Core/CPU/Proc.bsv
Normal file
@@ -0,0 +1,358 @@
|
||||
package Proc;
|
||||
|
||||
// Copyright (c) 2018 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// Portions Copyright (c) 2019 Bluespec, Inc.
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import ClientServer::*;
|
||||
import Connectable::*;
|
||||
import FIFOF :: *;
|
||||
import ConfigReg :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import GetPut_Aux :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// ----------------
|
||||
// From MIT RISCY-OOO
|
||||
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import L1CoCache::*;
|
||||
import L2Tlb::*;
|
||||
import CCTypes::*;
|
||||
import CacheUtils::*;
|
||||
import LLCache::*;
|
||||
import MemLoader::*;
|
||||
import L1LLConnect::*;
|
||||
import LLCDmaConnect::*;
|
||||
import MMIOAddrs::*;
|
||||
import MMIOCore::*;
|
||||
import DramCommon::*;
|
||||
import Performance::*;
|
||||
|
||||
// ----------------
|
||||
// From McStriiv
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
import Core :: *;
|
||||
import Proc_IFC :: *;
|
||||
import MMIOPlatform :: *;
|
||||
import LLC_AXI4_Adapter :: *;
|
||||
import MMIO_AXI4_Adapter :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkProc (Proc_IFC);
|
||||
// cores
|
||||
Vector#(CoreNum, Core) core = ?;
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
core[i] <- mkCore(fromInteger(i));
|
||||
end
|
||||
|
||||
// ----------------
|
||||
// Verbosity control for debugging
|
||||
|
||||
// Verbosity: 0=quiet; 1=instruction trace; 2=more detail
|
||||
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
|
||||
|
||||
// ----------------
|
||||
// Reset requests and responses (TODO: to be implemented)
|
||||
|
||||
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
|
||||
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
|
||||
|
||||
// ----------------
|
||||
// Communication to/from External debug module (TODO: to be implemented)
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
|
||||
// Debugger run-control
|
||||
FIFOF #(Bool) f_run_halt_reqs <- mkFIFOF;
|
||||
FIFOF #(Bool) f_run_halt_rsps <- mkFIFOF;
|
||||
|
||||
// Stop-request from debugger (e.g., GDB ^C or Dsharp 'stop')
|
||||
Reg #(Bool) rg_stop_req <- mkReg (False);
|
||||
|
||||
// Count instrs after step-request from debugger (via dcsr.step)
|
||||
Reg #(Bit #(1)) rg_step_count <- mkReg (0);
|
||||
|
||||
// Debugger GPR read/write request/response
|
||||
FIFOF #(MemoryRequest #(5, XLEN)) f_gpr_reqs <- mkFIFOF1;
|
||||
FIFOF #(MemoryResponse #( XLEN)) f_gpr_rsps <- mkFIFOF1;
|
||||
|
||||
`ifdef ISA_F
|
||||
// Debugger FPR read/write request/response
|
||||
FIFOF #(MemoryRequest #(5, FLEN)) f_fpr_reqs <- mkFIFOF1;
|
||||
FIFOF #(MemoryResponse #( FLEN)) f_fpr_rsps <- mkFIFOF1;
|
||||
`endif
|
||||
|
||||
// Debugger CSR read/write request/response
|
||||
FIFOF #(MemoryRequest #(12, XLEN)) f_csr_reqs <- mkFIFOF1;
|
||||
FIFOF #(MemoryResponse #( XLEN)) f_csr_rsps <- mkFIFOF1;
|
||||
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// Tandem Verification (TODO: to be implemented)
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// MMIO
|
||||
|
||||
MMIO_AXI4_Adapter_IFC mmio_axi4_adapter <- mkMMIO_AXI4_Adapter;
|
||||
|
||||
// MMIO platform
|
||||
Vector#(CoreNum, MMIOCoreToPlatform) mmioToP;
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
mmioToP[i] = core[i].mmioToPlatform;
|
||||
end
|
||||
MMIOPlatform mmioPlatform <- mkMMIOPlatform (mmioToP,
|
||||
mmio_axi4_adapter.core_side);
|
||||
|
||||
// last level cache
|
||||
LLCache llc <- mkLLCache;
|
||||
|
||||
// connect LLC to L1 caches
|
||||
Vector#(L1Num, ChildCacheToParent#(L1Way, void)) l1 = ?;
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
l1[i] = core[i].dCacheToParent;
|
||||
l1[i + valueof(CoreNum)] = core[i].iCacheToParent;
|
||||
end
|
||||
mkL1LLConnect(llc.to_child, l1);
|
||||
|
||||
// ================================================================
|
||||
// LLC's DMA connections
|
||||
|
||||
// Core's tlbToMem
|
||||
Vector#(CoreNum, TlbMemClient) tlbToMem = ?;
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
tlbToMem[i] = core[i].tlbToMem;
|
||||
end
|
||||
|
||||
// Stub out memLoader (TODO: can be Debug Module's access)
|
||||
let memLoaderStub = interface MemLoaderMemClient;
|
||||
interface memReq = nullFifoDeq;
|
||||
interface respSt = nullFifoEnq;
|
||||
endinterface;
|
||||
|
||||
mkLLCDmaConnect(llc.dma, memLoaderStub, tlbToMem);
|
||||
|
||||
// ================================================================
|
||||
// interface LLC to AXI4
|
||||
|
||||
LLC_AXI4_Adapter_IFC llc_axi4_adapter <- mkLLC_AXi4_Adapter (llc.to_mem);
|
||||
|
||||
// ================================================================
|
||||
// Connect stats
|
||||
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
rule broadcastStats;
|
||||
Bool doStats <- core[i].sendDoStats;
|
||||
for(Integer j = 0; j < valueof(CoreNum); j = j+1) begin
|
||||
core[j].recvDoStats(doStats);
|
||||
end
|
||||
llc.perf.setStatus(doStats);
|
||||
endrule
|
||||
end
|
||||
|
||||
// ================================================================
|
||||
// Stub out deadlock and renameDebug interfaces
|
||||
|
||||
for(Integer j = 0; j < valueof(CoreNum); j = j+1) begin
|
||||
rule rl_dummy1;
|
||||
let x <- core[j].deadlock.dCacheCRqStuck.get;
|
||||
endrule
|
||||
rule rl_dummy2;
|
||||
let x <- core[j].deadlock.dCachePRqStuck.get;
|
||||
endrule
|
||||
rule rl_dummy3;
|
||||
let x <- core[j].deadlock.iCacheCRqStuck.get;
|
||||
endrule
|
||||
rule rl_dummy4;
|
||||
let x <- core[j].deadlock.iCachePRqStuck.get;
|
||||
endrule
|
||||
rule rl_dummy5;
|
||||
let x <- core[j].deadlock.renameInstStuck.get;
|
||||
endrule
|
||||
rule rl_dummy6;
|
||||
let x <- core[j].deadlock.renameCorrectPathStuck.get;
|
||||
endrule
|
||||
rule rl_dummy7;
|
||||
let x <- core[j].deadlock.commitInstStuck.get;
|
||||
endrule
|
||||
rule rl_dummy8;
|
||||
let x <- core[j].deadlock.commitUserInstStuck.get;
|
||||
endrule
|
||||
rule rl_dummy9;
|
||||
let x <- core[j].deadlock.checkStarted.get;
|
||||
endrule
|
||||
|
||||
rule rl_dummy20;
|
||||
let x <- core[j].renameDebug.renameErr.get;
|
||||
endrule
|
||||
end
|
||||
|
||||
// ================================================================
|
||||
// Reset
|
||||
|
||||
rule rl_reset;
|
||||
let x <- pop (f_reset_reqs);
|
||||
|
||||
llc_axi4_adapter.reset;
|
||||
mmio_axi4_adapter.reset;
|
||||
|
||||
f_reset_rsps.enq (?);
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Termination detection
|
||||
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1) begin
|
||||
rule rl_terminate;
|
||||
let x <- core[i].coreIndInv.terminate;
|
||||
$display ("Core %d terminated", i);
|
||||
endrule
|
||||
end
|
||||
|
||||
// Print out values written 'tohost'
|
||||
rule rl_tohost;
|
||||
let x <- mmioPlatform.to_host;
|
||||
$display ("mmioPlatform.rl_tohost: 0x%0x (= %0d)", x, x);
|
||||
if (x != 0) begin
|
||||
// Standard RISC-V ISA tests finish by writing a value tohost with x[0]==1.
|
||||
// Further when x[63:1]==0, all tests within the program pass,
|
||||
// otherwise x[63:1] = the test within the program that failed.
|
||||
let failed_testnum = (x >> 1);
|
||||
if (failed_testnum == 0)
|
||||
$display ("PASS");
|
||||
else
|
||||
$display ("FAIL %0d", failed_testnum);
|
||||
$finish (0);
|
||||
end
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// ================================================================
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// Reset
|
||||
interface Server hart0_server_reset = toGPServer (f_reset_reqs, f_reset_rsps);
|
||||
|
||||
// ----------------
|
||||
// Start the cores running
|
||||
method Action start (Addr startpc, Addr tohostAddr, Addr fromhostAddr);
|
||||
action
|
||||
for(Integer i = 0; i < valueof(CoreNum); i = i+1)
|
||||
core[i].coreReq.start (startpc, tohostAddr, fromhostAddr);
|
||||
endaction
|
||||
|
||||
mmioPlatform.start (tohostAddr, fromhostAddr);
|
||||
|
||||
$display ("Proc.start: startpc = 0x%0h, tohostAddr = 0x%0h, fromhostAddr = %0h",
|
||||
startpc, tohostAddr, fromhostAddr);
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// SoC fabric connections
|
||||
|
||||
// Fabric master interface for memory (from LLC)
|
||||
interface master0 = llc_axi4_adapter.mem_master;
|
||||
|
||||
// Fabric master interface for IO (from MMIOPlatform)
|
||||
interface master1 = mmio_axi4_adapter.mmio_master;
|
||||
|
||||
// ----------------
|
||||
// External interrupts
|
||||
|
||||
method Action m_external_interrupt_req (x);
|
||||
core[0].setMEIP (pack (x));
|
||||
endmethod
|
||||
|
||||
method Action s_external_interrupt_req (x);
|
||||
core[0].setSEIP (pack (x));
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Non-maskable interrupt
|
||||
|
||||
// TODO: fixup: NMIs should send CPU to an NMI vector (TBD in SoC_Map)
|
||||
method Action non_maskable_interrupt_req (Bool set_not_clear) = noAction;
|
||||
|
||||
// ----------------
|
||||
// For tracing
|
||||
|
||||
method Action set_verbosity (Bit #(4) verbosity);
|
||||
cfg_verbosity <= verbosity;
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Optional interface to Tandem Verifier
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
interface Get trace_data_out = toGet (f_trace_data);
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// Optional interface to Debug Module
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// run-control, other
|
||||
interface Server hart0_server_run_halt = toGPServer (f_run_halt_reqs, f_run_halt_rsps);
|
||||
|
||||
interface Put hart0_put_other_req;
|
||||
method Action put (Bit #(4) req);
|
||||
cfg_verbosity <= req;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
// GPR access
|
||||
interface MemoryServer hart0_gpr_mem_server = toGPServer (f_gpr_reqs, f_gpr_rsps);
|
||||
|
||||
`ifdef ISA_F
|
||||
// FPR access
|
||||
interface MemoryServer hart0_fpr_mem_server = toGPServer (f_fpr_reqs, f_fpr_rsps);
|
||||
`endif
|
||||
|
||||
// CSR access
|
||||
interface MemoryServer hart0_csr_mem_server = toGPServer (f_csr_reqs, f_csr_rsps);
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
endpackage
|
||||
99
src_Core/CPU/Proc_IFC.bsv
Normal file
99
src_Core/CPU/Proc_IFC.bsv
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2016-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package Proc_IFC;
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Memory :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import ISA_Decls :: *;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
import TV_Info :: *;
|
||||
`endif
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// ================================================================
|
||||
// CPU interface
|
||||
|
||||
// Note: this Proc_IFC is similar, but not identical to CPU_IFC for Piccolo and Flute
|
||||
// Specifically, it removes interfaces for software and timer,
|
||||
// because the RISCY-OOO mkProc contains those elements.
|
||||
|
||||
interface Proc_IFC;
|
||||
// Reset
|
||||
interface Server #(Token, Token) hart0_server_reset;
|
||||
|
||||
// ----------------
|
||||
// Start the cores running
|
||||
method Action start (Addr startpc, Addr tohostAddr, Addr fromhostAddr);
|
||||
|
||||
// ----------------
|
||||
// SoC fabric connections
|
||||
|
||||
// Fabric master interface for memory (from LLC)
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master0;
|
||||
|
||||
// Fabric master interface for IO (from MMIOPlatform)
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master1;
|
||||
|
||||
// ----------------
|
||||
// External interrupts
|
||||
|
||||
(* always_ready, always_enabled *)
|
||||
method Action m_external_interrupt_req (Bool set_not_clear);
|
||||
|
||||
(* always_ready, always_enabled *)
|
||||
method Action s_external_interrupt_req (Bool set_not_clear);
|
||||
|
||||
// ----------------
|
||||
// Non-maskable interrupt
|
||||
|
||||
(* always_ready, always_enabled *)
|
||||
method Action non_maskable_interrupt_req (Bool set_not_clear);
|
||||
|
||||
// ----------------
|
||||
// Set core's verbosity
|
||||
|
||||
method Action set_verbosity (Bit #(4) verbosity);
|
||||
|
||||
// ----------------
|
||||
// Optional interface to Tandem Verifier
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
interface Get #(Trace_Data) trace_data_out;
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// Optional interface to Debug Module
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// run-control, other
|
||||
interface Server #(Bool, Bool) hart0_server_run_halt;
|
||||
interface Put #(Bit #(4)) hart0_put_other_req;
|
||||
|
||||
// GPR access
|
||||
interface MemoryServer #(5, XLEN) hart0_gpr_mem_server;
|
||||
|
||||
`ifdef ISA_F
|
||||
// FPR access
|
||||
interface MemoryServer #(5, FLEN) hart0_fpr_mem_server;
|
||||
`endif
|
||||
|
||||
// CSR access
|
||||
interface MemoryServer #(12, XLEN) hart0_csr_mem_server;
|
||||
`endif
|
||||
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
464
src_Core/Core/CoreW.bsv
Normal file
464
src_Core/Core/CoreW.bsv
Normal file
@@ -0,0 +1,464 @@
|
||||
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package CoreW;
|
||||
|
||||
// ================================================================
|
||||
// This package defines:
|
||||
// Core_IFC
|
||||
// mkCore #(Core_IFC)
|
||||
// mkFabric_2x3 -- specialized AXI4 fabric used inside this core
|
||||
//
|
||||
// mkCoreW instantiates:
|
||||
// - mkProc (the RISC-V CPU, a version of MIT's RISCY-OOO)
|
||||
// - mkFabric_2x3
|
||||
// - mkPLIC_16_2_7
|
||||
// - mkTV_Encode (Tandem-Verification logic, optional: INCLUDE_TANDEM_VERIF)
|
||||
// - mkDebug_Module (RISC-V Debug Module, optional: INCLUDE_GDB_CONTROL)
|
||||
// and connects them all up.
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Vector :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import GetPut_Aux :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// Main fabric
|
||||
import AXI4_Types :: *;
|
||||
import AXI4_Fabric :: *;
|
||||
import Fabric_Defs :: *; // for Wd_Id, Wd_Addr, Wd_Data, Wd_User
|
||||
import SoC_Map :: *;
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
import Debug_Module :: *;
|
||||
`endif
|
||||
|
||||
import Core_IFC :: *;
|
||||
import PLIC :: *;
|
||||
import PLIC_16_2_7 :: *;
|
||||
import Proc_IFC :: *;
|
||||
import Proc :: *;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
import TV_Info :: *;
|
||||
import TV_Encode :: *;
|
||||
`endif
|
||||
|
||||
// TV_Taps needed when both GDB_CONTROL and TANDEM_VERIF are present
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
import TV_Taps :: *;
|
||||
`endif
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// The Core module
|
||||
|
||||
(* synthesize *)
|
||||
module mkCoreW (Core_IFC #(N_External_Interrupt_Sources));
|
||||
|
||||
// ================================================================
|
||||
// STATE
|
||||
|
||||
// System address map
|
||||
SoC_Map_IFC soc_map <- mkSoC_Map;
|
||||
|
||||
// McStriiv processor
|
||||
Proc_IFC proc <- mkProc;
|
||||
|
||||
// A 2x3 fabric for connecting {CPU, Debug_Module} to {Fabric, PLIC}
|
||||
Fabric_2x3_IFC fabric_2x3 <- mkFabric_2x3;
|
||||
|
||||
// PLIC (Platform-Level Interrupt Controller)
|
||||
PLIC_IFC_16_2_7 plic <- mkPLIC_16_2_7;
|
||||
|
||||
// Reset requests from SoC and responses to SoC
|
||||
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
|
||||
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// The TV encoder transforms Trace_Data structures produced by the CPU and DM
|
||||
// into encoded byte vectors for transmission to the Tandem Verifier
|
||||
TV_Encode_IFC tv_encode <- mkTV_Encode;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Debug Module
|
||||
Debug_Module_IFC debug_module <- mkDebug_Module;
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// RESET
|
||||
// There are two sources of reset requests to the CPU: externally
|
||||
// from the SoC and, optionally, the DM. The SoC requires a
|
||||
// response, the DM does not. When both requestors are present
|
||||
// (i.e., DM is present), we merge the reset requests into the CPU,
|
||||
// and we remember which one was the requestor in
|
||||
// f_reset_requestor, so that we know whether or not to respond to
|
||||
// the SoC.
|
||||
|
||||
Bit #(1) reset_requestor_dm = 0;
|
||||
Bit #(1) reset_requestor_soc = 1;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
FIFOF #(Bit #(1)) f_reset_requestor <- mkFIFOF;
|
||||
`endif
|
||||
|
||||
// Reset-hart0 request from SoC
|
||||
rule rl_cpu_hart0_reset_from_soc_start;
|
||||
let req <- pop (f_reset_reqs);
|
||||
|
||||
proc.hart0_server_reset.request.put (?); // CPU
|
||||
plic.server_reset.request.put (?); // PLIC
|
||||
fabric_2x3.reset; // Local 2x3 Fabric
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Remember the requestor, so we can respond to it
|
||||
f_reset_requestor.enq (reset_requestor_soc);
|
||||
`endif
|
||||
$display ("%0d: Core.rl_cpu_hart0_reset_from_soc_start", cur_cycle);
|
||||
endrule
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// Reset-hart0 from Debug Module
|
||||
rule rl_cpu_hart0_reset_from_dm_start;
|
||||
let req <- debug_module.hart0_get_reset_req.get;
|
||||
|
||||
proc.hart0_server_reset.request.put (?); // CPU
|
||||
plic.server_reset.request.put (?); // PLIC
|
||||
fabric_2x3.reset; // Local 2x3 fabric
|
||||
|
||||
// Remember the requestor, so we can respond to it
|
||||
f_reset_requestor.enq (reset_requestor_dm);
|
||||
$display ("%0d: Core.rl_cpu_hart0_reset_from_dm_start", cur_cycle);
|
||||
endrule
|
||||
`endif
|
||||
|
||||
rule rl_cpu_hart0_reset_complete;
|
||||
let rsp1 <- proc.hart0_server_reset.response.get; // CPU
|
||||
let rsp3 <- plic.server_reset.response.get; // PLIC
|
||||
|
||||
plic.set_addr_map (zeroExtend (soc_map.m_plic_addr_base),
|
||||
zeroExtend (soc_map.m_plic_addr_lim));
|
||||
|
||||
Bit #(1) requestor = reset_requestor_soc;
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
requestor <- pop (f_reset_requestor);
|
||||
`endif
|
||||
if (requestor == reset_requestor_soc)
|
||||
f_reset_rsps.enq (?);
|
||||
|
||||
// Start running the cores
|
||||
Bit #(64) startpc = 'h_0000_1000; // TODO: fixup
|
||||
Bit #(64) tohostAddr = 'h_8000_1000; // TODO: fixup
|
||||
Bit #(64) fromhostAddr = 0;
|
||||
proc.start (startpc, tohostAddr, fromhostAddr);
|
||||
|
||||
$display ("%0d: Core.rl_cpu_hart0_reset_complete; started running proc", cur_cycle);
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Direct DM-to-CPU connections
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// DM to CPU connections for run-control and other misc requests
|
||||
mkConnection (debug_module.hart0_client_run_halt, proc.hart0_server_run_halt);
|
||||
mkConnection (debug_module.hart0_get_other_req, proc.hart0_put_other_req);
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// Other CPU/DM/TV connections
|
||||
// (depends on whether DM, TV or both are present)
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// BEGIN SECTION: GDB and TV
|
||||
// ----------------------------------------------------------------
|
||||
// DM and TV both present. We instantiate 'taps' into connections
|
||||
// where the DM writes CPU GPRs, CPU FPRs, CPU CSRs, and main memory,
|
||||
// in order to produce corresponding writes for the Tandem Verifier.
|
||||
// Then, we merge the Trace_Data from these three taps with the
|
||||
// Trace_Data produced by the PROC.
|
||||
|
||||
FIFOF #(Trace_Data) f_trace_data_merged <- mkFIFOF;
|
||||
|
||||
// Connect merged trace data to trace encoder
|
||||
mkConnection (toGet (f_trace_data_merged), tv_encode.trace_data_in);
|
||||
|
||||
// Merge-in CPU's trace data.
|
||||
// This is equivalent to: mkConnection (proc.trace_data_out, toPut (f_trace_data_merged))
|
||||
// but using a rule allows us to name it in scheduling attributes.
|
||||
rule merge_cpu_trace_data;
|
||||
let tmp <- proc.trace_data_out.get;
|
||||
f_trace_data_merged.enq (tmp);
|
||||
endrule
|
||||
|
||||
// Create a tap for DM's memory-writes to the bus, and merge-in the trace data.
|
||||
DM_Mem_Tap_IFC dm_mem_tap <- mkDM_Mem_Tap;
|
||||
mkConnection (debug_module.master, dm_mem_tap.slave);
|
||||
let dm_master_local = dm_mem_tap.master;
|
||||
|
||||
rule merge_dm_mem_trace_data;
|
||||
let tmp <- dm_mem_tap.trace_data_out.get;
|
||||
f_trace_data_merged.enq (tmp);
|
||||
endrule
|
||||
|
||||
// Create a tap for DM's GPR writes to the CPU, and merge-in the trace data.
|
||||
DM_GPR_Tap_IFC dm_gpr_tap_ifc <- mkDM_GPR_Tap;
|
||||
mkConnection (debug_module.hart0_gpr_mem_client, dm_gpr_tap_ifc.server);
|
||||
mkConnection (dm_gpr_tap_ifc.client, proc.hart0_gpr_mem_server);
|
||||
|
||||
rule merge_dm_gpr_trace_data;
|
||||
let tmp <- dm_gpr_tap_ifc.trace_data_out.get;
|
||||
f_trace_data_merged.enq (tmp);
|
||||
endrule
|
||||
|
||||
`ifdef ISA_F_OR_D
|
||||
// Create a tap for DM's FPR writes to the CPU, and merge-in the trace data.
|
||||
DM_FPR_Tap_IFC dm_fpr_tap_ifc <- mkDM_FPR_Tap;
|
||||
mkConnection (debug_module.hart0_fpr_mem_client, dm_fpr_tap_ifc.server);
|
||||
mkConnection (dm_fpr_tap_ifc.client, proc.hart0_fpr_mem_server);
|
||||
|
||||
rule merge_dm_fpr_trace_data;
|
||||
let tmp <- dm_fpr_tap_ifc.trace_data_out.get;
|
||||
f_trace_data_merged.enq (tmp);
|
||||
endrule
|
||||
`endif
|
||||
// for ifdef ISA_F_OR_D
|
||||
|
||||
// Create a tap for DM's CSR writes, and merge-in the trace data.
|
||||
DM_CSR_Tap_IFC dm_csr_tap <- mkDM_CSR_Tap;
|
||||
mkConnection(debug_module.hart0_csr_mem_client, dm_csr_tap.server);
|
||||
mkConnection(dm_csr_tap.client, proc.hart0_csr_mem_server);
|
||||
|
||||
`ifdef ISA_F_OR_D
|
||||
(* descending_urgency = "merge_dm_fpr_trace_data, merge_dm_gpr_trace_data" *)
|
||||
`endif
|
||||
(* descending_urgency = "merge_dm_gpr_trace_data, merge_dm_csr_trace_data" *)
|
||||
(* descending_urgency = "merge_dm_csr_trace_data, merge_dm_mem_trace_data" *)
|
||||
(* descending_urgency = "merge_dm_mem_trace_data, merge_cpu_trace_data" *)
|
||||
rule merge_dm_csr_trace_data;
|
||||
let tmp <- dm_csr_tap.trace_data_out.get;
|
||||
f_trace_data_merged.enq(tmp);
|
||||
endrule
|
||||
|
||||
// END SECTION: GDB and TV
|
||||
`else
|
||||
// for ifdef INCLUDE_TANDEM_VERIF
|
||||
// ----------------------------------------------------------------
|
||||
// BEGIN SECTION: GDB and no TV
|
||||
|
||||
// Connect DM's GPR interface directly to CPU
|
||||
mkConnection (debug_module.hart0_gpr_mem_client, proc.hart0_gpr_mem_server);
|
||||
|
||||
`ifdef ISA_F_OR_D
|
||||
// Connect DM's FPR interface directly to CPU
|
||||
mkConnection (debug_module.hart0_fpr_mem_client, proc.hart0_fpr_mem_server);
|
||||
`endif
|
||||
|
||||
// Connect DM's CSR interface directly to CPU
|
||||
mkConnection (debug_module.hart0_csr_mem_client, proc.hart0_csr_mem_server);
|
||||
|
||||
// DM's bus master is directly the bus master
|
||||
let dm_master_local = debug_module.master;
|
||||
|
||||
// END SECTION: GDB and no TV
|
||||
`endif
|
||||
// for ifdef INCLUDE_TANDEM_VERIF
|
||||
|
||||
`else
|
||||
// for ifdef INCLUDE_GDB_CONTROL
|
||||
// BEGIN SECTION: no GDB
|
||||
|
||||
// No DM, so 'DM bus master' is dummy
|
||||
AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User)
|
||||
dm_master_local = dummy_AXI4_Master_ifc;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
// ----------------------------------------------------------------
|
||||
// BEGIN SECTION: no GDB, TV
|
||||
|
||||
// Connect CPU's TV out directly to TV encoder
|
||||
mkConnection (proc.trace_data_out, tv_encode.trace_data_in);
|
||||
// END SECTION: no GDB, TV
|
||||
`endif
|
||||
`endif
|
||||
// for ifdef INCLUDE_GDB_CONTROL
|
||||
|
||||
// ================================================================
|
||||
// Connect the local 2x3 fabric
|
||||
|
||||
// Masters on the local 2x3 fabric
|
||||
mkConnection (proc.master1, fabric_2x3.v_from_masters [cpu_dmem_master_num]);
|
||||
mkConnection (dm_master_local, fabric_2x3.v_from_masters [debug_module_sba_master_num]);
|
||||
|
||||
// Slaves on the local 2x3 fabric
|
||||
// default slave is taken out directly to the Core interface
|
||||
mkConnection (fabric_2x3.v_to_slaves [plic_slave_num], plic.axi4_slave);
|
||||
|
||||
// TODO: This slave can be connected to mkLLCDmaConnect for Debug Module System Bus Access
|
||||
AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) dummy_slave = dummy_AXI4_Slave_ifc;
|
||||
mkConnection (fabric_2x3.v_to_slaves [near_mem_io_slave_num], dummy_slave);
|
||||
|
||||
// ================================================================
|
||||
// Connect external interrupt lines from PLIC to CPU
|
||||
|
||||
rule rl_relay_external_interrupts; // from PLIC
|
||||
Bool meip = plic.v_targets [0].m_eip;
|
||||
proc.m_external_interrupt_req (meip);
|
||||
|
||||
Bool seip = plic.v_targets [1].m_eip;
|
||||
proc.s_external_interrupt_req (seip);
|
||||
|
||||
// $display ("%0d: Core.rl_relay_external_interrupts: relaying: %d", cur_cycle, pack (x));
|
||||
endrule
|
||||
|
||||
// TODO: fixup. Need to combine NMIs from multiple sources (cache, fabric, devices, ...)
|
||||
rule rl_relay_non_maskable_interrupt;
|
||||
proc.non_maskable_interrupt_req (False);
|
||||
|
||||
// $display ("%0d: Core.rl_relay_non_maskable_interrupts: relaying: %d", cur_cycle, pack (x));
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Debugging: set core's verbosity
|
||||
|
||||
method Action set_verbosity (Bit #(4) verbosity, Bit #(64) logdelay);
|
||||
// Warning: ignoring logdelay
|
||||
proc.set_verbosity (verbosity);
|
||||
endmethod
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Soft reset
|
||||
|
||||
interface Server cpu_reset_server = toGPServer (f_reset_reqs, f_reset_rsps);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// AXI4 Fabric interfaces
|
||||
|
||||
// IMem to Fabric master interface
|
||||
interface AXI4_Master_IFC cpu_imem_master = proc.master0;
|
||||
|
||||
// DMem to Fabric master interface
|
||||
interface AXI4_Master_IFC cpu_dmem_master = fabric_2x3.v_to_slaves [default_slave_num];
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// External interrupt sources
|
||||
|
||||
interface core_external_interrupt_sources = plic.v_sources;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Optional TV interface
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
interface Get tv_verifier_info_get;
|
||||
method ActionValue #(Info_CPU_to_Verifier) get();
|
||||
match { .n, .v } <- tv_encode.tv_vb_out.get;
|
||||
return (Info_CPU_to_Verifier { num_bytes: n, vec_bytes: v });
|
||||
endmethod
|
||||
endinterface
|
||||
`endif
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Optional DM interfaces
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// ----------------
|
||||
// DMI (Debug Module Interface) facing remote debugger
|
||||
|
||||
interface DMI dm_dmi = debug_module.dmi;
|
||||
|
||||
// ----------------
|
||||
// Facing Platform
|
||||
|
||||
// Non-Debug-Module Reset (reset all except DM)
|
||||
interface Get dm_ndm_reset_req_get = debug_module.get_ndm_reset_req;
|
||||
`endif
|
||||
|
||||
endmodule: mkCoreW
|
||||
|
||||
// ================================================================
|
||||
// 2x3 Fabric for this Core
|
||||
// Masters: CPU DMem, Debug Module System Bus Access, External access
|
||||
|
||||
// ----------------
|
||||
// Fabric port numbers for masters
|
||||
|
||||
typedef 2 Num_Masters_2x3;
|
||||
|
||||
typedef Bit #(TLog #(Num_Masters_2x3)) Master_Num_2x3;
|
||||
|
||||
Master_Num_2x3 cpu_dmem_master_num = 0;
|
||||
Master_Num_2x3 debug_module_sba_master_num = 1;
|
||||
|
||||
// ----------------
|
||||
// Fabric port numbers for slaves
|
||||
|
||||
typedef 3 Num_Slaves_2x3;
|
||||
|
||||
typedef Bit #(TLog #(Num_Slaves_2x3)) Slave_Num_2x3;
|
||||
|
||||
Slave_Num_2x3 default_slave_num = 0;
|
||||
Slave_Num_2x3 plic_slave_num = 1;
|
||||
|
||||
// TODO: repurpose this for Debug Module System Bus Access to connect to mkLLCDramConnect
|
||||
Slave_Num_2x3 near_mem_io_slave_num = 2;
|
||||
|
||||
|
||||
// ----------------
|
||||
// Specialization of parameterized AXI4 fabric for 2x3 Core fabric
|
||||
|
||||
typedef AXI4_Fabric_IFC #(Num_Masters_2x3,
|
||||
Num_Slaves_2x3,
|
||||
Wd_Id,
|
||||
Wd_Addr,
|
||||
Wd_Data,
|
||||
Wd_User) Fabric_2x3_IFC;
|
||||
|
||||
// ----------------
|
||||
|
||||
(* synthesize *)
|
||||
module mkFabric_2x3 (Fabric_2x3_IFC);
|
||||
|
||||
// System address map
|
||||
SoC_Map_IFC soc_map <- mkSoC_Map;
|
||||
|
||||
// ----------------
|
||||
// Slave address decoder
|
||||
// Any addr is legal, and there is only one slave to service it.
|
||||
|
||||
function Tuple2 #(Bool, Slave_Num_2x3) fn_addr_to_slave_num_2x3 (Fabric_Addr addr);
|
||||
if ( (soc_map.m_near_mem_io_addr_base <= addr)
|
||||
&& (addr < soc_map.m_near_mem_io_addr_lim))
|
||||
return tuple2 (True, near_mem_io_slave_num);
|
||||
|
||||
else if ( (soc_map.m_plic_addr_base <= addr)
|
||||
&& (addr < soc_map.m_plic_addr_lim))
|
||||
return tuple2 (True, plic_slave_num);
|
||||
|
||||
else
|
||||
return tuple2 (True, default_slave_num);
|
||||
endfunction
|
||||
|
||||
AXI4_Fabric_IFC #(Num_Masters_2x3, Num_Slaves_2x3, Wd_Id, Wd_Addr, Wd_Data, Wd_User)
|
||||
fabric <- mkAXI4_Fabric (fn_addr_to_slave_num_2x3);
|
||||
|
||||
return fabric;
|
||||
endmodule: mkFabric_2x3
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
97
src_Core/Core/Core_IFC.bsv
Normal file
97
src_Core/Core/Core_IFC.bsv
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package Core_IFC;
|
||||
|
||||
// ================================================================
|
||||
// This package defines the interface of a Core module which
|
||||
// contains:
|
||||
// - mkCPU (the RISC-V CPU)
|
||||
// - mkFabric_2x3
|
||||
// - mkNear_Mem_IO_AXI4
|
||||
// - mkPLIC_16_2_7
|
||||
// - mkTV_Encode (Tandem-Verification logic, optional: INCLUDE_TANDEM_VERIF)
|
||||
// - mkDebug_Module (RISC-V Debug Module, optional: INCLUDE_GDB_CONTROL)
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Vector :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// Main fabric
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// External interrupt request interface
|
||||
import PLIC :: *;
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
import TV_Info :: *;
|
||||
`endif
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
import Debug_Module :: *;
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// The Core interface
|
||||
|
||||
interface Core_IFC #(numeric type t_n_interrupt_sources);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Debugging: set core's verbosity
|
||||
|
||||
method Action set_verbosity (Bit #(4) verbosity, Bit #(64) logdelay);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Soft reset
|
||||
|
||||
interface Server #(Bit #(0), Bit #(0)) cpu_reset_server;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// AXI4 Fabric interfaces
|
||||
|
||||
// CPU IMem to Fabric master interface
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) cpu_imem_master;
|
||||
|
||||
// CPU DMem to Fabric master interface
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) cpu_dmem_master;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// External interrupt sources
|
||||
|
||||
interface Vector #(t_n_interrupt_sources, PLIC_Source_IFC) core_external_interrupt_sources;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Optional Tandem Verifier interface output tuples (n,vb),
|
||||
// where 'vb' is a vector of bytes
|
||||
// with relevant bytes in locations [0]..[n-1]
|
||||
|
||||
`ifdef INCLUDE_TANDEM_VERIF
|
||||
interface Get #(Info_CPU_to_Verifier) tv_verifier_info_get;
|
||||
`endif
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Optional Debug Module interfaces
|
||||
|
||||
`ifdef INCLUDE_GDB_CONTROL
|
||||
// ----------------
|
||||
// DMI (Debug Module Interface) facing remote debugger
|
||||
|
||||
interface DMI dm_dmi;
|
||||
|
||||
// ----------------
|
||||
// Facing Platform
|
||||
// Non-Debug-Module Reset (reset all except DM)
|
||||
|
||||
interface Get #(Bit #(0)) dm_ndm_reset_req_get;
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
88
src_Core/Core/Fabric_Defs.bsv
Normal file
88
src_Core/Core/Fabric_Defs.bsv
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package Fabric_Defs;
|
||||
|
||||
// ================================================================
|
||||
// Defines key parameters of the AXI4/AXI4-Lite system interconnect
|
||||
// fabric to which the core connects, such as address bus width, data
|
||||
// bus width, etc.
|
||||
|
||||
// ***** WARNING! WARNING! WARNING! *****
|
||||
|
||||
// During system integration, these parameters should be checked to be
|
||||
// identical to the system interconnect settings. Strong
|
||||
// type-checking (EXACT match on bus widths) will do this; but some
|
||||
// languages/tools may silently ignore mismatched widths.
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
// None
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import AXI4_Types :: *;
|
||||
|
||||
// ================================================================
|
||||
// Fabric parameters
|
||||
|
||||
// ----------------
|
||||
// Width of fabric 'id' buses
|
||||
typedef 4 Wd_Id;
|
||||
typedef Bit #(Wd_Id) Fabric_Id;
|
||||
|
||||
// ----------------
|
||||
// Width of fabric 'addr' buses
|
||||
`ifdef FABRIC64
|
||||
typedef 64 Wd_Addr;
|
||||
`else
|
||||
typedef 32 Wd_Addr;
|
||||
`endif
|
||||
|
||||
typedef Bit #(Wd_Addr) Fabric_Addr;
|
||||
typedef TDiv #(Wd_Addr, 8) Bytes_per_Fabric_Addr;
|
||||
|
||||
Integer bytes_per_fabric_addr = valueOf (Bytes_per_Fabric_Addr);
|
||||
|
||||
// ----------------
|
||||
// Width of fabric 'data' buses
|
||||
`ifdef FABRIC64
|
||||
typedef 64 Wd_Data;
|
||||
`else
|
||||
typedef 32 Wd_Data;
|
||||
`endif
|
||||
|
||||
typedef Bit #(Wd_Data) Fabric_Data;
|
||||
typedef Bit #(TDiv #(Wd_Data, 8)) Fabric_Strb;
|
||||
|
||||
typedef TDiv #(Wd_Data, 8) Bytes_per_Fabric_Data;
|
||||
Integer bytes_per_fabric_data = valueOf (Bytes_per_Fabric_Data);
|
||||
|
||||
// ----------------
|
||||
// Width of fabric 'user' datapaths
|
||||
typedef 0 Wd_User;
|
||||
typedef Bit #(Wd_User) Fabric_User;
|
||||
|
||||
// ----------------
|
||||
// Number of zero LSBs in a fabric address aligned to the fabric data width
|
||||
|
||||
typedef TLog #(Bytes_per_Fabric_Data) ZLSBs_Aligned_Fabric_Addr;
|
||||
Integer zlsbs_aligned_fabric_addr = valueOf (ZLSBs_Aligned_Fabric_Addr);
|
||||
|
||||
// ================================================================
|
||||
// AXI4 defaults for this project
|
||||
|
||||
Fabric_Id fabric_default_id = 0;
|
||||
AXI4_Burst fabric_default_burst = axburst_incr;
|
||||
AXI4_Lock fabric_default_lock = axlock_normal;
|
||||
AXI4_Cache fabric_default_arcache = arcache_dev_nonbuf;
|
||||
AXI4_Cache fabric_default_awcache = awcache_dev_nonbuf;
|
||||
AXI4_Prot fabric_default_prot = { axprot_2_data, axprot_1_secure, axprot_0_unpriv };
|
||||
AXI4_QoS fabric_default_qos = 0;
|
||||
AXI4_Region fabric_default_region = 0;
|
||||
Fabric_User fabric_default_user = ?;
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
736
src_Core/Core/TV_Encode.bsv
Normal file
736
src_Core/Core/TV_Encode.bsv
Normal file
@@ -0,0 +1,736 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package TV_Encode;
|
||||
|
||||
// ================================================================
|
||||
// module mkTV_Encode is a transforming FIFO
|
||||
// converting Trace_Data into encoded byte vectors
|
||||
|
||||
// ================================================================
|
||||
// BSV lib imports
|
||||
|
||||
import Vector :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import GetPut_Aux :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import ISA_Decls :: *;
|
||||
import TV_Info :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
interface TV_Encode_IFC;
|
||||
method Action reset;
|
||||
|
||||
// This module receives Trace_Data structs from the CPU and Debug Module
|
||||
interface Put #(Trace_Data) trace_data_in;
|
||||
|
||||
// This module produces tuples (n,vb),
|
||||
// where 'vb' is a vector of bytes
|
||||
// with relevant bytes in locations [0]..[n-1]
|
||||
interface Get #(Tuple2 #(Bit #(32), TV_Vec_Bytes)) tv_vb_out;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkTV_Encode (TV_Encode_IFC);
|
||||
|
||||
Reg #(Bool) rg_reset_done <- mkReg (True);
|
||||
|
||||
// Keep track of last PC for more efficient encoding of incremented PCs
|
||||
// TODO: currently always sending full PC
|
||||
Reg #(WordXL) rg_last_pc <- mkReg (0);
|
||||
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
FIFOF #(Tuple2 #(Bit #(32), TV_Vec_Bytes)) f_vb <- mkFIFOF;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// BEHAVIOR
|
||||
|
||||
rule rl_log_trace_RESET (rg_reset_done && (f_trace_data.first.op == TRACE_RESET));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_byte (te_op_hart_reset);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nnN, .xN } = vsubst (nn1, x1, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_GPR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_GPR_WRITE));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_byte (te_op_state_init);
|
||||
match { .n2, .vb2 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_FPR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_FPR_WRITE));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_byte (te_op_state_init);
|
||||
match { .n2, .vb2 } = encode_reg (fv_fpr_regnum (td.rd), td.word1);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_CSR_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_CSR_WRITE));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_byte (te_op_state_init);
|
||||
match { .n2, .vb2 } = encode_reg (fv_csr_regnum (truncate (td.word3)), td.word4);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_MEM_WRITE (rg_reset_done && (f_trace_data.first.op == TRACE_MEM_WRITE));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
Bit #(2) mem_req_size = td.word1 [1:0];
|
||||
Byte size_and_mem_req_op = { 2'b0, mem_req_size, te_mem_req_op_Store };
|
||||
Byte result_and_size = { te_mem_result_success, 2'b0, mem_req_size };
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_byte (te_op_state_init);
|
||||
match { .n2, .vb2 } = encode_byte (te_op_mem_req);
|
||||
match { .n3, .vb3 } = encode_mlen (td.word3);
|
||||
match { .n4, .vb4 } = encode_byte (size_and_mem_req_op);
|
||||
match { .n5, .vb5 } = encode_mdata (mem_req_size, td.word2);
|
||||
//match { .n6, .vb6 } = encode_byte (te_op_mem_rsp);
|
||||
//match { .n7, .vb7 } = encode_byte (result_and_size);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
|
||||
//match { .nn6, .x6 } = vsubst (nn5, x5, n6, vb6);
|
||||
//match { .nn7, .x7 } = vsubst (nn6, x6, n7, vb7);
|
||||
//match { .nnN, .xN } = vsubst (nn7, x7, nN, vbN);
|
||||
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_OTHER (rg_reset_done && (f_trace_data.first.op == TRACE_OTHER));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_I_RD (rg_reset_done && (f_trace_data.first.op == TRACE_I_RD));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nnN, .xN } = vsubst (nn3, x3, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_F_RD (rg_reset_done && (f_trace_data.first.op == TRACE_F_RD));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_fpr_regnum (td.rd), td.word1);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nnN, .xN } = vsubst (nn2, x2, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_I_LOAD (rg_reset_done && (f_trace_data.first.op == TRACE_I_LOAD));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
|
||||
match { .n4, .vb4 } = encode_eaddr (truncate (td.word3));
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_F_LOAD (rg_reset_done && (f_trace_data.first.op == TRACE_F_LOAD));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_fpr_regnum (td.rd), td.word1);
|
||||
match { .n4, .vb4 } = encode_eaddr (truncate (td.word3));
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_STORE (rg_reset_done && (f_trace_data.first.op == TRACE_STORE));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
let funct3 = instr_funct3 (td.instr); // TODO: what if it's a 16b instr?
|
||||
let mem_req_size = funct3 [1:0];
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_stval (mem_req_size, td.word2);
|
||||
match { .n4, .vb4 } = encode_eaddr (truncate (td.word3));
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_AMO (rg_reset_done && (f_trace_data.first.op == TRACE_AMO));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
let funct3 = instr_funct3 (td.instr); // TODO: what if it's a 16b instr?
|
||||
let mem_req_size = funct3 [1:0];
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
|
||||
match { .n4, .vb4 } = encode_stval (mem_req_size, td.word2);
|
||||
match { .n5, .vb5 } = encode_eaddr (truncate (td.word3));
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
|
||||
match { .nnN, .xN } = vsubst (nn5, x5, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_CSRRX (rg_reset_done && (f_trace_data.first.op == TRACE_CSRRX));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_reg (fv_gpr_regnum (td.rd), td.word1);
|
||||
match { .n4, .vb4 } = ((td.word2 == 0)
|
||||
? tuple2 (0, ?) // CSR was not written
|
||||
: encode_reg (fv_csr_regnum (truncate (td.word3)), td.word4));
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_TRAP (rg_reset_done && (f_trace_data.first.op == TRACE_TRAP));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Use new priv mode to decide which trap regs are updated (M, S or U priv)
|
||||
Priv_Mode priv = truncate (td.rd);
|
||||
CSR_Addr csr_addr_status = csr_addr_mstatus;
|
||||
CSR_Addr csr_addr_cause = csr_addr_mcause;
|
||||
CSR_Addr csr_addr_epc = csr_addr_mepc;
|
||||
CSR_Addr csr_addr_tval = csr_addr_mtval;
|
||||
if (priv == s_Priv_Mode) begin
|
||||
csr_addr_status = csr_addr_sstatus;
|
||||
csr_addr_cause = csr_addr_scause;
|
||||
csr_addr_epc = csr_addr_sepc;
|
||||
csr_addr_tval = csr_addr_stval;
|
||||
end
|
||||
else if (priv == u_Priv_Mode) begin
|
||||
csr_addr_status = csr_addr_ustatus;
|
||||
csr_addr_cause = csr_addr_ucause;
|
||||
csr_addr_epc = csr_addr_uepc;
|
||||
csr_addr_tval = csr_addr_utval;
|
||||
end
|
||||
|
||||
// Omit the instruction if cause is instruction fault since the instruction is then bogus
|
||||
Bool is_instr_fault = ( (truncate (td.word2) == exc_code_INSTR_ACCESS_FAULT)
|
||||
|| (truncate (td.word2) == exc_code_INSTR_PAGE_FAULT));
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = (is_instr_fault
|
||||
? tuple2 (0, ?)
|
||||
: encode_instr (td.instr_sz, td.instr));
|
||||
match { .n3, .vb3 } = encode_priv (td.rd);
|
||||
match { .n4, .vb4 } = encode_reg (fv_csr_regnum (csr_addr_status), td.word1);
|
||||
match { .n5, .vb5 } = encode_reg (fv_csr_regnum (csr_addr_cause), td.word2);
|
||||
match { .n6, .vb6 } = encode_reg (fv_csr_regnum (csr_addr_epc), truncate (td.word3));
|
||||
match { .n7, .vb7 } = encode_reg (fv_csr_regnum (csr_addr_tval), td.word4);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
|
||||
match { .nn6, .x6 } = vsubst (nn5, x5, n6, vb6);
|
||||
match { .nn7, .x7 } = vsubst (nn6, x6, n7, vb7);
|
||||
match { .nnN, .xN } = vsubst (nn7, x7, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_INTR (rg_reset_done && (f_trace_data.first.op == TRACE_INTR));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Use new priv mode to decide which trap regs are updated (M, S or U priv)
|
||||
Priv_Mode priv = truncate (td.rd);
|
||||
CSR_Addr csr_addr_status = csr_addr_mstatus;
|
||||
CSR_Addr csr_addr_cause = csr_addr_mcause;
|
||||
CSR_Addr csr_addr_epc = csr_addr_mepc;
|
||||
CSR_Addr csr_addr_tval = csr_addr_mtval;
|
||||
if (priv == s_Priv_Mode) begin
|
||||
csr_addr_status = csr_addr_sstatus;
|
||||
csr_addr_cause = csr_addr_scause;
|
||||
csr_addr_epc = csr_addr_sepc;
|
||||
csr_addr_tval = csr_addr_stval;
|
||||
end
|
||||
else if (priv == u_Priv_Mode) begin
|
||||
csr_addr_status = csr_addr_ustatus;
|
||||
csr_addr_cause = csr_addr_ucause;
|
||||
csr_addr_epc = csr_addr_uepc;
|
||||
csr_addr_tval = csr_addr_utval;
|
||||
end
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_priv (td.rd);
|
||||
match { .n3, .vb3 } = encode_reg (fv_csr_regnum (csr_addr_status), td.word1);
|
||||
match { .n4, .vb4 } = encode_reg (fv_csr_regnum (csr_addr_cause), td.word2);
|
||||
match { .n5, .vb5 } = encode_reg (fv_csr_regnum (csr_addr_epc), truncate (td.word3));
|
||||
match { .n6, .vb6 } = encode_reg (fv_csr_regnum (csr_addr_tval), td.word4);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nn5, .x5 } = vsubst (nn4, x4, n5, vb5);
|
||||
match { .nn6, .x6 } = vsubst (nn5, x5, n6, vb6);
|
||||
match { .nnN, .xN } = vsubst (nn6, x6, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
rule rl_log_trace_RET (rg_reset_done && (f_trace_data.first.op == TRACE_RET));
|
||||
let td <- pop (f_trace_data);
|
||||
|
||||
// Encode components of td into byte vecs
|
||||
match { .n0, .vb0 } = encode_byte (te_op_begin_group);
|
||||
match { .n1, .vb1 } = encode_pc (td.pc);
|
||||
match { .n2, .vb2 } = encode_instr (td.instr_sz, td.instr);
|
||||
match { .n3, .vb3 } = encode_priv (td.rd);
|
||||
match { .n4, .vb4 } = encode_reg (fv_csr_regnum (csr_addr_mstatus), td.word1);
|
||||
match { .nN, .vbN } = encode_byte (te_op_end_group);
|
||||
|
||||
// Concatenate components into a single byte vec
|
||||
match { .nn0, .x0 } = vsubst ( 0, ?, n0, vb0);
|
||||
match { .nn1, .x1 } = vsubst (nn0, x0, n1, vb1);
|
||||
match { .nn2, .x2 } = vsubst (nn1, x1, n2, vb2);
|
||||
match { .nn3, .x3 } = vsubst (nn2, x2, n3, vb3);
|
||||
match { .nn4, .x4 } = vsubst (nn3, x3, n4, vb4);
|
||||
match { .nnN, .xN } = vsubst (nn4, x4, nN, vbN);
|
||||
|
||||
f_vb.enq (tuple2 (nnN, xN));
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// INTERFACE
|
||||
|
||||
method Action reset ();
|
||||
endmethod
|
||||
|
||||
interface Put trace_data_in = toPut (f_trace_data);
|
||||
interface Get tv_vb_out = toGet (f_vb);
|
||||
endmodule
|
||||
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// ****************************************************************
|
||||
// Encoding Trace_Data into Byte vectors
|
||||
|
||||
// ================================================================
|
||||
// Encodings
|
||||
// cf. "Trace Protocol Specification Version 2018-09-12, Darius Rad, Bluespec, Inc."
|
||||
|
||||
Bit #(8) te_op_begin_group = 1;
|
||||
Bit #(8) te_op_end_group = 2;
|
||||
Bit #(8) te_op_incr_pc = 3;
|
||||
Bit #(8) te_op_full_reg = 4;
|
||||
Bit #(8) te_op_incr_reg = 5;
|
||||
Bit #(8) te_op_incr_reg_OR = 6;
|
||||
Bit #(8) te_op_addl_state = 7;
|
||||
Bit #(8) te_op_mem_req = 8;
|
||||
Bit #(8) te_op_mem_rsp = 9;
|
||||
Bit #(8) te_op_hart_reset = 10;
|
||||
Bit #(8) te_op_state_init = 11;
|
||||
Bit #(8) te_op_16b_instr = 16;
|
||||
Bit #(8) te_op_32b_instr = 17;
|
||||
|
||||
Bit #(4) te_mem_req_size_8 = 0;
|
||||
Bit #(4) te_mem_req_size_16 = 1;
|
||||
Bit #(4) te_mem_req_size_32 = 2;
|
||||
Bit #(4) te_mem_req_size_64 = 3;
|
||||
|
||||
Bit #(4) te_mem_req_op_Load = 0;
|
||||
Bit #(4) te_mem_req_op_Store = 1;
|
||||
Bit #(4) te_mem_req_op_LR = 2;
|
||||
Bit #(4) te_mem_req_op_SC = 3;
|
||||
Bit #(4) te_mem_req_op_AMO_swap = 4;
|
||||
Bit #(4) te_mem_req_op_AMO_add = 5;
|
||||
Bit #(4) te_mem_req_op_AMO_xor = 6;
|
||||
Bit #(4) te_mem_req_op_AMO_and = 7;
|
||||
Bit #(4) te_mem_req_op_AMO_or = 8;
|
||||
Bit #(4) te_mem_req_op_AMO_min = 9;
|
||||
Bit #(4) te_mem_req_op_AMO_max = 10;
|
||||
Bit #(4) te_mem_req_op_AMO_minu = 11;
|
||||
Bit #(4) te_mem_req_op_AMO_maxu = 12;
|
||||
Bit #(4) te_mem_req_op_ifetch = 13;
|
||||
|
||||
Bit #(4) te_mem_result_success = 0;
|
||||
Bit #(4) te_mem_result_failure = 1;
|
||||
|
||||
Bit #(8) te_op_addl_state_priv = 1;
|
||||
Bit #(8) te_op_addl_state_paddr = 2;
|
||||
Bit #(8) te_op_addl_state_eaddr = 3;
|
||||
Bit #(8) te_op_addl_state_data8 = 4;
|
||||
Bit #(8) te_op_addl_state_data16 = 5;
|
||||
Bit #(8) te_op_addl_state_data32 = 6;
|
||||
Bit #(8) te_op_addl_state_data64 = 7;
|
||||
Bit #(8) te_op_addl_state_mtime = 8;
|
||||
Bit #(8) te_op_addl_state_pc_paddr = 9;
|
||||
Bit #(8) te_op_addl_state_pc = 10;
|
||||
|
||||
// ================================================================
|
||||
// Architectural register address encodings
|
||||
// cf. "RISC-V External Debug Support"
|
||||
// 2018-10-02_riscv_debug_spec_v0.13_DRAFT_f2873e71
|
||||
// "Table 3.3 Abstract Register Numbers"
|
||||
|
||||
function Bit #(16) fv_csr_regnum (CSR_Addr csr_addr);
|
||||
return zeroExtend (csr_addr);
|
||||
endfunction
|
||||
|
||||
function Bit #(16) fv_gpr_regnum (RegName gpr_addr);
|
||||
return 'h1000 + zeroExtend (gpr_addr);
|
||||
endfunction
|
||||
|
||||
function Bit #(16) fv_fpr_regnum (RegName fpr_addr);
|
||||
return 'h1020 + zeroExtend (fpr_addr);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// vsubst substitutes vb1[j1:j1+j2-1] with vb2[0:j2-1]
|
||||
|
||||
function Tuple2 #(Bit #(32),
|
||||
Vector #(TV_VB_SIZE, Byte))
|
||||
vsubst (Bit #(32) j1, Vector #(TV_VB_SIZE, Byte) vb1,
|
||||
Bit #(32) j2, Vector #(m, Byte) vb2);
|
||||
|
||||
function Byte f (Integer j);
|
||||
Byte x = vb1 [j];
|
||||
Bit #(32) jj = fromInteger (j);
|
||||
if ((j1 <= jj) && (jj < j1 + j2))
|
||||
x = vb2 [jj - j1];
|
||||
return x;
|
||||
endfunction
|
||||
|
||||
let v = genWith (f);
|
||||
let n = j1 + j2;
|
||||
|
||||
return tuple2 (n, v);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Encoding of Trace_Data into byte vectors
|
||||
// Every function below returns:
|
||||
// (n, vb) :: Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte))
|
||||
// where vb is a vector of bytes with relevant bytes in vb[0]..vb[n-1]
|
||||
|
||||
// ================================================================
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_byte (Byte x);
|
||||
return tuple2 (1, replicate (x));
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_mlen (Bit #(64) word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n;
|
||||
vb [0] = word[7:0];
|
||||
vb [1] = word [15:8];
|
||||
vb [2] = word [23:16];
|
||||
vb [3] = word [31:24];
|
||||
vb [4] = word [39:32];
|
||||
vb [5] = word [47:40];
|
||||
vb [6] = word [55:48];
|
||||
vb [7] = word [63:56];
|
||||
`ifdef RV32
|
||||
n = 4; // MLEN = 32
|
||||
`ifdef SV34
|
||||
n = 5; // MLEN = 34
|
||||
`endif
|
||||
`else
|
||||
n = 8; // MLEN = 64
|
||||
`endif
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_mdata (MemReqSize mem_req_size, WordXL word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n;
|
||||
vb [0] = word[7:0];
|
||||
vb [1] = word [15:8];
|
||||
vb [2] = word [23:16];
|
||||
vb [3] = word [31:24];
|
||||
`ifdef RV64
|
||||
vb [4] = word [39:32];
|
||||
vb [5] = word [47:40];
|
||||
vb [6] = word [55:48];
|
||||
vb [7] = word [63:56];
|
||||
`endif
|
||||
n = case (mem_req_size)
|
||||
f3_SIZE_B: 1;
|
||||
f3_SIZE_H: 2;
|
||||
f3_SIZE_W: 4;
|
||||
f3_SIZE_D: 8;
|
||||
endcase;
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_instr (ISize isize, Bit #(32) instr);
|
||||
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n = ((isize == ISIZE16BIT) ? 3 : 5);
|
||||
vb [0] = ((isize == ISIZE16BIT) ? te_op_16b_instr : te_op_32b_instr);
|
||||
vb [1] = instr [7:0];
|
||||
vb [2] = instr [15:8];
|
||||
vb [3] = instr [23:16];
|
||||
vb [4] = instr [31:24];
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_reg (Bit #(16) regnum, WordXL word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n = 0;
|
||||
vb [0] = te_op_full_reg;
|
||||
vb [1] = regnum [7:0];
|
||||
vb [2] = regnum [15:8];
|
||||
vb [3] = word[7:0];
|
||||
vb [4] = word [15:8];
|
||||
vb [5] = word [23:16];
|
||||
vb [6] = word [31:24];
|
||||
n = 7;
|
||||
`ifdef RV64
|
||||
vb [7] = word [39:32];
|
||||
vb [8] = word [47:40];
|
||||
vb [9] = word [55:48];
|
||||
vb [10] = word [63:56];
|
||||
n = 11;
|
||||
`endif
|
||||
if (regnum == fv_gpr_regnum (0)) n = 0;
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_priv (Bit #(5) priv);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
vb [0] = te_op_addl_state;
|
||||
vb [1] = te_op_addl_state_priv;
|
||||
vb [2] = zeroExtend (priv);
|
||||
return tuple2 (3, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_pc (WordXL word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n;
|
||||
vb [0] = te_op_addl_state;
|
||||
vb [1] = te_op_addl_state_pc;
|
||||
vb [2] = word [7:0];
|
||||
vb [3] = word [15:8];
|
||||
vb [4] = word [23:16];
|
||||
vb [5] = word [31:24];
|
||||
n = 6;
|
||||
`ifdef RV64
|
||||
vb [6] = word [39:32];
|
||||
vb [7] = word [47:40];
|
||||
vb [8] = word [55:48];
|
||||
vb [9] = word [63:56];
|
||||
n = 10;
|
||||
`endif
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_eaddr (WordXL word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n;
|
||||
vb [0] = te_op_addl_state;
|
||||
vb [1] = te_op_addl_state_eaddr;
|
||||
vb [2] = word [7:0];
|
||||
vb [3] = word [15:8];
|
||||
vb [4] = word [23:16];
|
||||
vb [5] = word [31:24];
|
||||
n = 6;
|
||||
`ifdef RV64
|
||||
vb [6] = word [39:32];
|
||||
vb [7] = word [47:40];
|
||||
vb [8] = word [55:48];
|
||||
vb [9] = word [63:56];
|
||||
n = 10;
|
||||
`endif
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(Bit #(32), Vector #(TV_VB_SIZE, Byte)) encode_stval (MemReqSize mem_req_size, WordXL word);
|
||||
Vector #(TV_VB_SIZE, Byte) vb = newVector;
|
||||
Bit #(32) n;
|
||||
vb [0] = te_op_addl_state;
|
||||
vb [1] = case (mem_req_size)
|
||||
f3_SIZE_B: te_op_addl_state_data8;
|
||||
f3_SIZE_H: te_op_addl_state_data16;
|
||||
f3_SIZE_W: te_op_addl_state_data32;
|
||||
f3_SIZE_D: te_op_addl_state_data64;
|
||||
endcase;
|
||||
vb [2] = word [7:0];
|
||||
vb [3] = word [15:8];
|
||||
vb [4] = word [23:16];
|
||||
vb [5] = word [31:24];
|
||||
`ifdef RV64
|
||||
vb [6] = word [39:32];
|
||||
vb [7] = word [47:40];
|
||||
vb [8] = word [55:48];
|
||||
vb [9] = word [63:56];
|
||||
`endif
|
||||
n = case (mem_req_size)
|
||||
f3_SIZE_B: 2 + 1;
|
||||
f3_SIZE_H: 2 + 2;
|
||||
f3_SIZE_W: 2 + 4;
|
||||
f3_SIZE_D: 2 + 8;
|
||||
endcase;
|
||||
return tuple2 (n, vb);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
239
src_Core/Core/TV_Taps.bsv
Normal file
239
src_Core/Core/TV_Taps.bsv
Normal file
@@ -0,0 +1,239 @@
|
||||
// Copyright (c) 2018-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package TV_Taps;
|
||||
|
||||
// ================================================================
|
||||
// This package defines 'taps' on connections between
|
||||
// - DM and CPU, on which DM accesses CPU GPRs, FPRs and CSRs
|
||||
// - DM and memory bus, on which DM accesses memory
|
||||
// Each tap snoops 'writes', and produces a corresponsing Trace_Data
|
||||
// write-memory command for the Tandem Verifier, so that it keeps its
|
||||
// GPRs, FPRs, CSRs and memories in sync.
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Assert :: *;
|
||||
import BUtils :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
import Memory :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Semi_FIFOF :: *;
|
||||
import GetPut_Aux :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import ISA_Decls :: *;
|
||||
import TV_Info :: *;
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// ================================================================
|
||||
// DM-to-memory tap
|
||||
|
||||
interface DM_Mem_Tap_IFC;
|
||||
interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave;
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master;
|
||||
interface Get #(Trace_Data) trace_data_out;
|
||||
endinterface
|
||||
|
||||
(* synthesize *)
|
||||
module mkDM_Mem_Tap (DM_Mem_Tap_IFC);
|
||||
|
||||
// Transactor facing DM
|
||||
AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;
|
||||
|
||||
// Transactor facing memory bus
|
||||
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor;
|
||||
|
||||
// Tap output
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
|
||||
// ----------------
|
||||
// AXI requests
|
||||
|
||||
// Snoop write requests
|
||||
rule write_reqs;
|
||||
let wr_addr = slave_xactor.o_wr_addr.first;
|
||||
slave_xactor.o_wr_addr.deq;
|
||||
|
||||
let wr_data = slave_xactor.o_wr_data.first;
|
||||
slave_xactor.o_wr_data.deq;
|
||||
|
||||
// Pass-through
|
||||
master_xactor.i_wr_addr.enq (wr_addr);
|
||||
master_xactor.i_wr_data.enq (wr_data);
|
||||
|
||||
// Tap
|
||||
Bit #(64) paddr = ?;
|
||||
Bit #(64) stval = ?;
|
||||
`ifdef FABRIC64
|
||||
if (wr_data.wstrb == 'h0f) begin
|
||||
paddr = zeroExtend (wr_addr.awaddr);
|
||||
stval = (wr_data.wdata & 'h_FFFF_FFFF);
|
||||
end
|
||||
else if (wr_data.wstrb == 'hf0) begin
|
||||
paddr = zeroExtend (wr_addr.awaddr);
|
||||
stval = ((wr_data.wdata >> 32) & 'h_FFFF_FFFF);
|
||||
end
|
||||
else
|
||||
dynamicAssert(False, "mkDM_Mem_Tap: unsupported byte enables");
|
||||
`else
|
||||
paddr = zeroExtend (wr_addr.awaddr);
|
||||
stval = zeroExtend (wr_data.wdata);
|
||||
`endif
|
||||
Trace_Data td = mkTrace_MEM_WRITE (f3_SIZE_W, truncate (stval), paddr);
|
||||
f_trace_data.enq (td);
|
||||
endrule
|
||||
|
||||
// Read requests, write responses and read responses are not snooped
|
||||
mkConnection (slave_xactor.o_rd_addr, master_xactor.i_rd_addr);
|
||||
mkConnection (slave_xactor.i_wr_resp, master_xactor.o_wr_resp);
|
||||
mkConnection (slave_xactor.i_rd_data, master_xactor.o_rd_data);
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// Facing DM
|
||||
interface slave = slave_xactor.axi_side;
|
||||
// Facing bus
|
||||
interface master = master_xactor.axi_side;
|
||||
// Tap towards verifier
|
||||
interface Get trace_data_out = toGet (f_trace_data);
|
||||
|
||||
endmodule: mkDM_Mem_Tap
|
||||
|
||||
// ================================================================
|
||||
// DM-to-CPU GPR tap (for writes to GPRs)
|
||||
|
||||
interface DM_GPR_Tap_IFC;
|
||||
interface MemoryClient #(5, XLEN) client;
|
||||
interface MemoryServer #(5, XLEN) server;
|
||||
interface Get #(Trace_Data) trace_data_out;
|
||||
endinterface
|
||||
|
||||
(* synthesize *)
|
||||
module mkDM_GPR_Tap (DM_GPR_Tap_IFC);
|
||||
// req from DM
|
||||
FIFOF #(MemoryRequest #(5, XLEN)) f_req_in <- mkFIFOF;
|
||||
// req to CPU
|
||||
FIFOF #(MemoryRequest #(5, XLEN)) f_req_out <- mkFIFOF;
|
||||
// resp CPU->DM
|
||||
FIFOF #(MemoryResponse #(XLEN)) f_rsp <- mkFIFOF;
|
||||
// Tap to TV
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
|
||||
rule request;
|
||||
let req <- pop (f_req_in);
|
||||
|
||||
// Pass-through to CPU
|
||||
f_req_out.enq(req);
|
||||
|
||||
// Snoop writes and send trace data to TV
|
||||
if (req.write) begin
|
||||
Trace_Data td;
|
||||
td = mkTrace_GPR_WRITE (req.address, req.data);
|
||||
f_trace_data.enq (td);
|
||||
end
|
||||
endrule
|
||||
|
||||
interface MemoryClient client = toGPClient (f_req_out, f_rsp);
|
||||
interface MemoryServer server = toGPServer (f_req_in, f_rsp);
|
||||
|
||||
interface Get trace_data_out = toGet (f_trace_data);
|
||||
endmodule: mkDM_GPR_Tap
|
||||
|
||||
// ================================================================
|
||||
// DM-to-CPU FPR tap (for writes to FPRs)
|
||||
|
||||
`ifdef ISA_F_OR_D
|
||||
|
||||
interface DM_FPR_Tap_IFC;
|
||||
interface MemoryClient #(5, FLEN) client;
|
||||
interface MemoryServer #(5, FLEN) server;
|
||||
interface Get #(Trace_Data) trace_data_out;
|
||||
endinterface
|
||||
|
||||
(* synthesize *)
|
||||
module mkDM_FPR_Tap (DM_FPR_Tap_IFC);
|
||||
// req from DM
|
||||
FIFOF #(MemoryRequest #(5, FLEN)) f_req_in <- mkFIFOF;
|
||||
// req to CPU
|
||||
FIFOF #(MemoryRequest #(5, FLEN)) f_req_out <- mkFIFOF;
|
||||
// resp CPU->DM
|
||||
FIFOF #(MemoryResponse #(FLEN)) f_rsp <- mkFIFOF;
|
||||
// Tap to TV
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
|
||||
rule request;
|
||||
let req <- pop (f_req_in);
|
||||
|
||||
// Pass-through to CPU
|
||||
f_req_out.enq(req);
|
||||
|
||||
// Snoop writes and send trace data to TV
|
||||
if (req.write) begin
|
||||
Trace_Data td;
|
||||
td = mkTrace_FPR_WRITE (req.address, req.data);
|
||||
f_trace_data.enq (td);
|
||||
end
|
||||
endrule
|
||||
|
||||
interface MemoryClient client = toGPClient (f_req_out, f_rsp);
|
||||
interface MemoryServer server = toGPServer (f_req_in, f_rsp);
|
||||
|
||||
interface Get trace_data_out = toGet (f_trace_data);
|
||||
endmodule: mkDM_FPR_Tap
|
||||
|
||||
`endif
|
||||
|
||||
// ================================================================
|
||||
// DM-to-CPU CSR tap (for writes to CSRs)
|
||||
|
||||
interface DM_CSR_Tap_IFC;
|
||||
interface MemoryClient #(12, XLEN) client;
|
||||
interface MemoryServer #(12, XLEN) server;
|
||||
interface Get #(Trace_Data) trace_data_out;
|
||||
endinterface
|
||||
|
||||
(* synthesize *)
|
||||
module mkDM_CSR_Tap (DM_CSR_Tap_IFC);
|
||||
// req from DM
|
||||
FIFOF #(MemoryRequest #(12, XLEN)) f_req_in <- mkFIFOF;
|
||||
// req to CPU
|
||||
FIFOF #(MemoryRequest #(12, XLEN)) f_req_out <- mkFIFOF;
|
||||
// resp CPU->DM
|
||||
FIFOF #(MemoryResponse #(XLEN)) f_rsp <- mkFIFOF;
|
||||
// Tap to TV
|
||||
FIFOF #(Trace_Data) f_trace_data <- mkFIFOF;
|
||||
|
||||
rule request;
|
||||
let req <- pop (f_req_in);
|
||||
|
||||
// Pass-through to CPU
|
||||
f_req_out.enq(req);
|
||||
|
||||
// Snoop writes and send trace data to TV
|
||||
if (req.write) begin
|
||||
Trace_Data td = mkTrace_CSR_WRITE (req.address, req.data);
|
||||
f_trace_data.enq (td);
|
||||
end
|
||||
endrule
|
||||
|
||||
interface MemoryClient client = toGPClient (f_req_out, f_rsp);
|
||||
interface MemoryServer server = toGPServer (f_req_in, f_rsp);
|
||||
|
||||
interface Get trace_data_out = toGet (f_trace_data);
|
||||
endmodule: mkDM_CSR_Tap
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
487
src_Core/Debug_Module/DM_Abstract_Commands.bsv
Normal file
487
src_Core/Debug_Module/DM_Abstract_Commands.bsv
Normal file
@@ -0,0 +1,487 @@
|
||||
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package DM_Abstract_Commands;
|
||||
|
||||
// ================================================================
|
||||
// This package implements the 'Abstract Command' part of the RISC-V
|
||||
// Debug Module, i.e., read/write access to RISC-V GPRs, FPRs and CSRs.
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Memory :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
|
||||
// ----------------
|
||||
// Other library imports
|
||||
|
||||
import GetPut_Aux :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
import ISA_Decls :: *;
|
||||
import DM_Common :: *;
|
||||
|
||||
// ================================================================
|
||||
// Interface
|
||||
|
||||
interface DM_Abstract_Commands_IFC;
|
||||
method Action reset;
|
||||
|
||||
// ----------------
|
||||
// DMI facing GDB/host
|
||||
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
|
||||
method Action write (DM_Addr dm_addr, DM_Word dm_word);
|
||||
|
||||
// ----------------
|
||||
// Facing CPU/hart
|
||||
interface MemoryClient #(5, XLEN) hart0_gpr_mem_client;
|
||||
interface MemoryClient #(12, XLEN) hart0_csr_mem_client;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkDM_Abstract_Commands (DM_Abstract_Commands_IFC);
|
||||
|
||||
Integer verbosity = 0; // Normally 0; non-zero for debugging
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
Reg #(Bool) rg_start_reg_access <- mkReg (False);
|
||||
|
||||
// FIFOs for request/response to access GPRs
|
||||
FIFOF #(MemoryRequest #(5, XLEN)) f_hart0_gpr_reqs <- mkFIFOF1;
|
||||
FIFOF #(MemoryResponse #( XLEN)) f_hart0_gpr_rsps <- mkFIFOF1;
|
||||
|
||||
// FIFOs for request/response to access CSRs
|
||||
FIFOF #(MemoryRequest #(12, XLEN)) f_hart0_csr_reqs <- mkFIFOF1;
|
||||
FIFOF #(MemoryResponse #( XLEN)) f_hart0_csr_rsps <- mkFIFOF1;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// rg_data0
|
||||
|
||||
Reg #(DM_Word ) rg_data0 <- mkRegU;
|
||||
`ifdef RV64
|
||||
Reg #(DM_Word ) rg_data1 <- mkRegU;
|
||||
`endif
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// rg_data2..11: not implemented
|
||||
// rg_abstractauto: not implemented
|
||||
// rg_progbuf0..15: not implemented
|
||||
// ----------------------------------------------------------------
|
||||
// rg_abstractcs
|
||||
|
||||
Reg #(Bool) rg_abstractcs_busy <- mkRegU;
|
||||
Reg #(DM_abstractcs_cmderr) rg_abstractcs_cmderr <- mkRegU;
|
||||
|
||||
Bit #(5) abstractcs_progsize = 0;
|
||||
Bit #(5) abstractcs_datacount = 0;
|
||||
DM_Word virt_rg_abstractcs = {3'b0,
|
||||
abstractcs_progsize,
|
||||
11'b0,
|
||||
pack (rg_abstractcs_busy),
|
||||
1'b0,
|
||||
pack (rg_abstractcs_cmderr),
|
||||
3'b0,
|
||||
abstractcs_datacount};
|
||||
|
||||
function Action fa_rg_abstractcs_write (DM_Word dm_word);
|
||||
action
|
||||
if (rg_abstractcs_busy) begin
|
||||
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_BUSY;
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [abstractcs] <= 0x%08h: ERROR", cur_cycle, dm_word);
|
||||
$display (" DM is busy with a previous abstract command");
|
||||
end
|
||||
else if (fn_abstractcs_cmderr (dm_word) != DM_ABSTRACTCS_CMDERR_NONE) begin
|
||||
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
|
||||
if (verbosity != 0)
|
||||
$display ("(%0d): DM_Abstract_Commands.write [abstractcs]: clearing cmderr", cur_cycle);
|
||||
end
|
||||
else begin
|
||||
if (verbosity != 0)
|
||||
$display ("(%0d): DM_Abstract_Commands.write [abstractcs]: cmderr unchanged", cur_cycle);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// rg_command
|
||||
// cmdtype no register, since we only support 'access reg'
|
||||
// size no register, since we only support 'lower 32b' for RV32
|
||||
// and 'lower 64b' for RV64
|
||||
// postexec no register, since we don't support Program Buffer
|
||||
// transfer no register, since we always do transfers
|
||||
|
||||
Reg #(Bool) rg_command_access_reg_write <- mkRegU;
|
||||
|
||||
// regno: we only implement lower 13 bits of this 16-bit field
|
||||
Reg #(Bit #(13)) rg_command_access_reg_regno <- mkRegU;
|
||||
|
||||
DM_Word virt_rg_command = fn_mk_command_access_reg (
|
||||
DM_COMMAND_ACCESS_REG_SIZE_LOWER32
|
||||
, False // postexec
|
||||
, True // transfer
|
||||
, rg_command_access_reg_write
|
||||
, zeroExtend (rg_command_access_reg_regno));
|
||||
|
||||
function Action fa_rg_command_write (DM_Word dm_word);
|
||||
action
|
||||
// TODO: check that CPU is halted, else set cmderr = DM_ABSTRACTCS_CMDERR_HALT_RESUME
|
||||
|
||||
DM_abstractcs_cmderr cmderr = rg_abstractcs_cmderr;
|
||||
let size = fn_command_access_reg_size (dm_word);
|
||||
|
||||
// Ignore if 'cmderr' is non-zero
|
||||
if (cmderr != DM_ABSTRACTCS_CMDERR_NONE) begin
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
|
||||
$display (" Ignoring since 'cmderr' is 0x%0h", cmderr);
|
||||
end
|
||||
else begin
|
||||
if (rg_abstractcs_busy) begin
|
||||
cmderr = DM_ABSTRACTCS_CMDERR_BUSY;
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
|
||||
$display (" DM is busy with a previous abstract command");
|
||||
end
|
||||
|
||||
// Only 'Access Reg' cmdtype is supported
|
||||
else if (fn_command_cmdtype (dm_word) != DM_COMMAND_CMDTYPE_ACCESS_REG) begin
|
||||
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
|
||||
$display (" ", fshow (fn_command_cmdtype (dm_word)), " not supported");
|
||||
end
|
||||
|
||||
`ifdef RV32
|
||||
// Only lower 32-bit access is supported
|
||||
else if (size != DM_COMMAND_ACCESS_REG_SIZE_LOWER32) begin
|
||||
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
|
||||
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, ",
|
||||
fshow (fn_command_access_reg_size (dm_word)), " not supported in RV32 mode");
|
||||
end
|
||||
`endif
|
||||
`ifdef RV64
|
||||
// Only lower 32-bit and 64-bit access is supported
|
||||
else if (size != DM_COMMAND_ACCESS_REG_SIZE_LOWER64)
|
||||
begin
|
||||
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
|
||||
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, ",
|
||||
fshow (fn_command_access_reg_size (dm_word)), " not supported in RV64 mode");
|
||||
end
|
||||
`endif
|
||||
|
||||
// 'postexec' is not supported
|
||||
else if (fn_command_access_reg_postexec (dm_word) == True) begin
|
||||
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
|
||||
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, postexec not supported");
|
||||
end
|
||||
|
||||
// non-'transfer' is not supported
|
||||
else if (fn_command_access_reg_transfer (dm_word) == False) begin
|
||||
cmderr = DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: ERROR", cur_cycle, dm_word);
|
||||
$display (" For DM_COMMAND_CMDTYPE_ACCESS_REG, no-transfer not supported");
|
||||
end
|
||||
|
||||
else begin
|
||||
Bool is_write = fn_command_access_reg_write (dm_word);
|
||||
Bit #(13) regno = truncate (fn_command_access_reg_regno (dm_word));
|
||||
|
||||
rg_command_access_reg_write <= is_write;
|
||||
rg_command_access_reg_regno <= regno;
|
||||
rg_abstractcs_busy <= True;
|
||||
rg_start_reg_access <= True;
|
||||
cmderr = DM_ABSTRACTCS_CMDERR_NONE;
|
||||
if (verbosity != 0)
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [command] <= 0x%08h: OKAY", cur_cycle, dm_word);
|
||||
end
|
||||
rg_abstractcs_cmderr <= cmderr;
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Start reads/writes
|
||||
|
||||
Bool is_csr = ( (fromInteger (dm_command_access_reg_regno_csr_0) <= rg_command_access_reg_regno)
|
||||
&& (rg_command_access_reg_regno <= fromInteger (dm_command_access_reg_regno_csr_FFF)));
|
||||
|
||||
Bool is_gpr = ( (fromInteger (dm_command_access_reg_regno_gpr_0) <= rg_command_access_reg_regno)
|
||||
&& (rg_command_access_reg_regno <= fromInteger (dm_command_access_reg_regno_gpr_1F)));
|
||||
|
||||
Bit #(12) csr_addr = truncate (rg_command_access_reg_regno - fromInteger (dm_command_access_reg_regno_csr_0));
|
||||
Bit #(5) gpr_addr = truncate (rg_command_access_reg_regno - fromInteger (dm_command_access_reg_regno_gpr_0));
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Read/Write CSR
|
||||
|
||||
rule rl_start_write_csr ( rg_abstractcs_busy
|
||||
&& rg_start_reg_access
|
||||
&& rg_command_access_reg_write
|
||||
&& is_csr);
|
||||
if (verbosity != 0)
|
||||
`ifdef RV32
|
||||
$display ("(%0d): DM_Abstract_Commands.write [command]: write CSR [0x%0h] <= 0x%08h", cur_cycle,
|
||||
csr_addr, rg_data0);
|
||||
`endif
|
||||
`ifdef RV64
|
||||
$display ("(%0d): DM_Abstract_Commands.write [command]: write CSR [0x%0h] <= 0x%016h", cur_cycle,
|
||||
csr_addr, {rg_data1, rg_data0});
|
||||
`endif
|
||||
|
||||
let req = MemoryRequest {write: True, byteen: '1, address: csr_addr,
|
||||
`ifdef RV32
|
||||
data: rg_data0
|
||||
`endif
|
||||
`ifdef RV64
|
||||
data: {rg_data1, rg_data0}
|
||||
`endif
|
||||
};
|
||||
f_hart0_csr_reqs.enq (req);
|
||||
rg_start_reg_access <= False;
|
||||
rg_abstractcs_busy <= False;
|
||||
endrule
|
||||
|
||||
rule rl_start_read_csr ( rg_abstractcs_busy
|
||||
&& rg_start_reg_access
|
||||
&& (! rg_command_access_reg_write)
|
||||
&& is_csr);
|
||||
if (verbosity != 0)
|
||||
$display ("(%0d): DM_Abstract_Commands.write [command]: read CSR [0x%0h]", cur_cycle, csr_addr);
|
||||
|
||||
let req = MemoryRequest {write: False, byteen: '1, address: csr_addr, data: ?};
|
||||
f_hart0_csr_reqs.enq (req);
|
||||
rg_start_reg_access <= False;
|
||||
endrule
|
||||
|
||||
rule rl_finish_csr_read (rg_abstractcs_busy);
|
||||
let rsp <- pop (f_hart0_csr_rsps);
|
||||
`ifdef RV32
|
||||
rg_data0 <= rsp.data;
|
||||
`endif
|
||||
`ifdef RV64
|
||||
rg_data0 <= truncate (rsp.data);
|
||||
rg_data1 <= rsp.data[63:32];
|
||||
`endif
|
||||
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
|
||||
if (verbosity != 0)
|
||||
`ifdef RV32
|
||||
$display ("(%0d): DM_Abstract_Commands: csr read data is 0x%08h", cur_cycle, rsp.data);
|
||||
`endif
|
||||
`ifdef RV64
|
||||
$display ("(%0d): DM_Abstract_Commands: csr read data is 0x%016h", cur_cycle, rsp.data);
|
||||
`endif
|
||||
|
||||
rg_abstractcs_busy <= False;
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Read/Write GPR
|
||||
|
||||
rule rl_start_write_gpr ( rg_abstractcs_busy
|
||||
&& rg_start_reg_access
|
||||
&& rg_command_access_reg_write
|
||||
&& is_gpr);
|
||||
if (verbosity != 0)
|
||||
`ifdef RV32
|
||||
$display ("(%0d): DM_Abstract_Commands.write [command]: write GPR [0x%0h] <= 0x%08h", cur_cycle,
|
||||
gpr_addr, rg_data0);
|
||||
`endif
|
||||
`ifdef RV64
|
||||
$display ("(%0d): DM_Abstract_Commands.write [command]: write GPR [0x%0h] <= 0x%016h", cur_cycle,
|
||||
gpr_addr, {rg_data1, rg_data0});
|
||||
`endif
|
||||
|
||||
let req = MemoryRequest {write: True,
|
||||
byteen: '1,
|
||||
address: gpr_addr,
|
||||
`ifdef RV32
|
||||
data: rg_data0
|
||||
`endif
|
||||
`ifdef RV64
|
||||
data: {rg_data1, rg_data0}
|
||||
`endif
|
||||
};
|
||||
|
||||
f_hart0_gpr_reqs.enq (req);
|
||||
rg_start_reg_access <= False;
|
||||
rg_abstractcs_busy <= False;
|
||||
endrule
|
||||
|
||||
rule rl_start_read_gpr ( rg_abstractcs_busy
|
||||
&& rg_start_reg_access
|
||||
&& (! rg_command_access_reg_write)
|
||||
&& is_gpr);
|
||||
if (verbosity != 0)
|
||||
$display ("(%0d): DM_Abstract_Commands.write [command]: read GPR [0x%0h]", cur_cycle, gpr_addr);
|
||||
|
||||
let req = MemoryRequest {
|
||||
write: False
|
||||
, byteen: '1
|
||||
, address: gpr_addr
|
||||
, data: ?
|
||||
};
|
||||
f_hart0_gpr_reqs.enq (req);
|
||||
rg_start_reg_access <= False;
|
||||
endrule
|
||||
|
||||
rule rl_finish_gpr_read (rg_abstractcs_busy);
|
||||
let rsp <- pop (f_hart0_gpr_rsps);
|
||||
`ifdef RV32
|
||||
rg_data0 <= rsp.data;
|
||||
`endif
|
||||
`ifdef RV64
|
||||
rg_data0 <= truncate (rsp.data);
|
||||
rg_data1 <= rsp.data[63:32];
|
||||
`endif
|
||||
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
|
||||
if (verbosity != 0)
|
||||
`ifdef RV32
|
||||
$display ("(%0d): DM_Abstract_Commands: gpr read data is 0x%08h", cur_cycle, rsp.data);
|
||||
`endif
|
||||
`ifdef RV64
|
||||
$display ("(%0d): DM_Abstract_Commands: gpr read data is 0x%016h", cur_cycle, rsp.data);
|
||||
`endif
|
||||
rg_abstractcs_busy <= False;
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Read/Write unknown address
|
||||
|
||||
rule rl_start_write_unknown ( rg_abstractcs_busy
|
||||
&& rg_start_reg_access
|
||||
&& rg_command_access_reg_write
|
||||
&& (! is_csr) && (! is_gpr));
|
||||
if (verbosity != 0)
|
||||
$display ("(%0d): DM_Abstract_Commands.write [command]: write unknown RISC-V regno [0x%0h] <= 0x%08h", cur_cycle,
|
||||
rg_command_access_reg_regno, rg_data0);
|
||||
|
||||
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_OTHER;
|
||||
rg_start_reg_access <= False;
|
||||
rg_abstractcs_busy <= False;
|
||||
endrule
|
||||
|
||||
rule rl_start_read_unknown ( rg_abstractcs_busy
|
||||
&& rg_start_reg_access
|
||||
&& (! rg_command_access_reg_write)
|
||||
&& (! is_csr) && (! is_gpr));
|
||||
if (verbosity != 0)
|
||||
$display ("(%0d): DM_Abstract_Commands.write [command]: read unknown RISC-V regno [0x%0h] => ...", cur_cycle,
|
||||
rg_command_access_reg_regno);
|
||||
|
||||
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_OTHER;
|
||||
rg_start_reg_access <= False;
|
||||
rg_abstractcs_busy <= False;
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Finish CSR and GPR reads
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
method Action reset;
|
||||
rg_start_reg_access <= False;
|
||||
|
||||
f_hart0_gpr_reqs.clear;
|
||||
f_hart0_gpr_rsps.clear;
|
||||
f_hart0_csr_reqs.clear;
|
||||
f_hart0_csr_rsps.clear;
|
||||
|
||||
rg_abstractcs_busy <= False;
|
||||
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NONE;
|
||||
|
||||
rg_command_access_reg_write <= False;
|
||||
rg_command_access_reg_regno <= fromInteger (dm_command_access_reg_regno_gpr_0);
|
||||
|
||||
rg_data0 <= 0;
|
||||
`ifdef RV64
|
||||
rg_data1 <= 0;
|
||||
`endif
|
||||
|
||||
if (verbosity != 0)
|
||||
$display ("(%0d): DM_Abstract_Commands: reset", cur_cycle);
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Facing DMI/GDB
|
||||
|
||||
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
|
||||
actionvalue
|
||||
let dm_addr_name = fshow_dm_addr (dm_addr);
|
||||
DM_Word dm_word = case (dm_addr)
|
||||
dm_addr_abstractcs: virt_rg_abstractcs;
|
||||
dm_addr_command: virt_rg_command;
|
||||
dm_addr_data0: rg_data0;
|
||||
`ifdef RV64
|
||||
dm_addr_data1: rg_data1;
|
||||
`endif
|
||||
// dm_addr_data2..data3
|
||||
// dm_addr_abstractauto
|
||||
// dm_addr_progbuf0..15
|
||||
endcase;
|
||||
if (verbosity != 0)
|
||||
$display ("(%0d): DM_Abstract_Commands.av_read: [", cur_cycle, dm_addr_name, "] => 0x%08h", dm_word);
|
||||
return dm_word;
|
||||
endactionvalue
|
||||
endmethod
|
||||
|
||||
method Action write (DM_Addr dm_addr, DM_Word dm_word);
|
||||
action
|
||||
let dm_addr_name = fshow_dm_addr (dm_addr);
|
||||
|
||||
if (dm_addr == dm_addr_abstractcs)
|
||||
fa_rg_abstractcs_write (dm_word);
|
||||
|
||||
else if (rg_abstractcs_cmderr != DM_ABSTRACTCS_CMDERR_NONE) begin
|
||||
if (verbosity != 0) begin
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name, "] <= 0x%08h: ERROR", dm_word);
|
||||
$display (" Ignoring: previous cmderr ", fshow (rg_abstractcs_cmderr));
|
||||
end
|
||||
end
|
||||
|
||||
else if (dm_addr == dm_addr_command)
|
||||
fa_rg_command_write (dm_word);
|
||||
|
||||
else if (dm_addr == dm_addr_data0) begin
|
||||
rg_data0 <= dm_word;
|
||||
|
||||
if (verbosity != 0)
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name, "] <= 0x%08h", dm_word);
|
||||
end
|
||||
`ifdef RV64
|
||||
else if (dm_addr == dm_addr_data1) begin
|
||||
rg_data1 <= dm_word;
|
||||
|
||||
if (verbosity != 0)
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name, "] <= 0x%08h", dm_word);
|
||||
end
|
||||
`endif
|
||||
else begin
|
||||
// dm_addr_data2..12
|
||||
// dm_addr_abstractauto
|
||||
// dm_addr_progbuf0..15
|
||||
rg_abstractcs_cmderr <= DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED;
|
||||
|
||||
$display ("(%0d): DM_Abstract_Commands.write: [", cur_cycle, dm_addr_name,
|
||||
"] <= 0x%08h: ERROR: not supported", dm_word);
|
||||
end
|
||||
endaction
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Facing CPU/hart
|
||||
interface MemoryClient hart0_gpr_mem_client = toGPClient (f_hart0_gpr_reqs, f_hart0_gpr_rsps);
|
||||
interface MemoryClient hart0_csr_mem_client = toGPClient (f_hart0_csr_reqs, f_hart0_csr_rsps);
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
512
src_Core/Debug_Module/DM_Common.bsv
Normal file
512
src_Core/Debug_Module/DM_Common.bsv
Normal file
@@ -0,0 +1,512 @@
|
||||
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package DM_Common;
|
||||
|
||||
// ================================================================
|
||||
// This package has non-implementation-specific definitions, i.e.,
|
||||
// just encodes things in the spec.
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
// None
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
// None
|
||||
|
||||
// ================================================================
|
||||
// Debug Module Interface (DMI) addresses and data.
|
||||
// Note: data is always 32b, whether the connected CPU is RV32, RV64 or RV128.
|
||||
|
||||
typedef Bit #(7) DM_Addr;
|
||||
|
||||
DM_Addr max_DM_Addr = 'h5F;
|
||||
|
||||
typedef Bit #(32) DM_Word;
|
||||
|
||||
// ================================================================
|
||||
// Debug Module address map
|
||||
|
||||
// ----------------
|
||||
// Run Control
|
||||
|
||||
DM_Addr dm_addr_dmcontrol = 'h10;
|
||||
DM_Addr dm_addr_dmstatus = 'h11;
|
||||
DM_Addr dm_addr_hartinfo = 'h12;
|
||||
DM_Addr dm_addr_haltsum = 'h13;
|
||||
DM_Addr dm_addr_hawindowsel = 'h14;
|
||||
DM_Addr dm_addr_hawindow = 'h15;
|
||||
DM_Addr dm_addr_devtreeaddr0 = 'h19;
|
||||
DM_Addr dm_addr_authdata = 'h30;
|
||||
DM_Addr dm_addr_haltregion0 = 'h40;
|
||||
DM_Addr dm_addr_haltregion31 = 'h5F;
|
||||
|
||||
DM_Addr dm_addr_verbosity = 'h60; // Non-standard (not in spec)
|
||||
|
||||
// ----------------
|
||||
// Abstract commands (read/write RISC-V registers and RISC-V CSRs)
|
||||
|
||||
DM_Addr dm_addr_abstractcs = 'h16;
|
||||
DM_Addr dm_addr_command = 'h17;
|
||||
|
||||
DM_Addr dm_addr_data0 = 'h04;
|
||||
DM_Addr dm_addr_data1 = 'h05;
|
||||
DM_Addr dm_addr_data2 = 'h06;
|
||||
DM_Addr dm_addr_data3 = 'h07;
|
||||
DM_Addr dm_addr_data4 = 'h08;
|
||||
DM_Addr dm_addr_data5 = 'h09;
|
||||
DM_Addr dm_addr_data6 = 'h0a;
|
||||
DM_Addr dm_addr_data7 = 'h0b;
|
||||
DM_Addr dm_addr_data8 = 'h0c;
|
||||
DM_Addr dm_addr_data9 = 'h0d;
|
||||
DM_Addr dm_addr_data10 = 'h0d;
|
||||
DM_Addr dm_addr_data11 = 'h0f;
|
||||
|
||||
DM_Addr dm_addr_abstractauto = 'h18;
|
||||
DM_Addr dm_addr_progbuf0 = 'h20;
|
||||
|
||||
// ----------------
|
||||
// System Bus access (read/write RISC-V memory/devices)
|
||||
|
||||
DM_Addr dm_addr_sbcs = 'h38;
|
||||
DM_Addr dm_addr_sbaddress0 = 'h39;
|
||||
DM_Addr dm_addr_sbaddress1 = 'h3a;
|
||||
DM_Addr dm_addr_sbaddress2 = 'h3b;
|
||||
DM_Addr dm_addr_sbdata0 = 'h3c;
|
||||
DM_Addr dm_addr_sbdata1 = 'h3d;
|
||||
DM_Addr dm_addr_sbdata2 = 'h3e;
|
||||
DM_Addr dm_addr_sbdata3 = 'h3f;
|
||||
|
||||
// ================================================================
|
||||
|
||||
function Fmt fshow_dm_addr (DM_Addr dm_addr);
|
||||
return case (dm_addr)
|
||||
// Run Control
|
||||
dm_addr_dmcontrol: $format ("dm_addr_dmcontrol");
|
||||
dm_addr_dmstatus: $format ("dm_addr_dmstatus");
|
||||
dm_addr_hartinfo: $format ("dm_addr_hartinfo");
|
||||
dm_addr_haltsum: $format ("dm_addr_haltsum");
|
||||
dm_addr_hawindowsel: $format ("dm_addr_hawindowsel");
|
||||
dm_addr_hawindow: $format ("dm_addr_hawindow");
|
||||
dm_addr_devtreeaddr0: $format ("dm_addr_devtreeaddr0");
|
||||
dm_addr_authdata: $format ("dm_addr_authdata");
|
||||
dm_addr_haltregion0: $format ("dm_addr_haltregion0");
|
||||
dm_addr_haltregion31: $format ("dm_addr_haltregion31");
|
||||
dm_addr_verbosity: $format ("dm_addr_verbosity");
|
||||
|
||||
// Abstract Commands
|
||||
dm_addr_abstractcs: $format ("dm_addr_abstractcs");
|
||||
dm_addr_command: $format ("dm_addr_command");
|
||||
dm_addr_data0: $format ("dm_addr_data0");
|
||||
dm_addr_data1: $format ("dm_addr_data1");
|
||||
dm_addr_data2: $format ("dm_addr_data2");
|
||||
dm_addr_data3: $format ("dm_addr_data3");
|
||||
dm_addr_data4: $format ("dm_addr_data4");
|
||||
dm_addr_data5: $format ("dm_addr_data5");
|
||||
dm_addr_data6: $format ("dm_addr_data6");
|
||||
dm_addr_data7: $format ("dm_addr_data7");
|
||||
dm_addr_data8: $format ("dm_addr_data8");
|
||||
dm_addr_data9: $format ("dm_addr_data9");
|
||||
dm_addr_data10: $format ("dm_addr_data10");
|
||||
dm_addr_data11: $format ("dm_addr_data11");
|
||||
dm_addr_abstractauto: $format ("dm_addr_abstractauto");
|
||||
dm_addr_progbuf0: $format ("dm_addr_progbuf0");
|
||||
|
||||
// System Bus
|
||||
dm_addr_sbcs: $format ("dm_addr_sbcs");
|
||||
dm_addr_sbaddress0: $format ("dm_addr_sbaddress0");
|
||||
dm_addr_sbaddress1: $format ("dm_addr_sbaddress1");
|
||||
dm_addr_sbaddress2: $format ("dm_addr_sbaddress2");
|
||||
dm_addr_sbdata0: $format ("dm_addr_sbdata0");
|
||||
dm_addr_sbdata1: $format ("dm_addr_sbdata1");
|
||||
dm_addr_sbdata2: $format ("dm_addr_sbdata2");
|
||||
dm_addr_sbdata3: $format ("dm_addr_sbdata3");
|
||||
|
||||
default: $format ("<Unknown dm_abstract_command dm_addr 0x%0h>", dm_addr);
|
||||
endcase;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Run Control DM register fields
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 'dmcontrol' register
|
||||
|
||||
function DM_Word fn_mk_dmcontrol (Bool haltreq,
|
||||
Bool resumereq,
|
||||
Bool hartreset,
|
||||
Bool hasel,
|
||||
Bit #(10) hartsel,
|
||||
Bool ndmreset,
|
||||
Bool dmactive);
|
||||
return {pack (haltreq),
|
||||
pack (resumereq),
|
||||
pack (hartreset),
|
||||
2'b0,
|
||||
pack (hasel),
|
||||
hartsel,
|
||||
14'b0,
|
||||
pack (ndmreset),
|
||||
pack (dmactive)};
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmcontrol_haltreq (DM_Word dm_word);
|
||||
return unpack (dm_word [31]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmcontrol_resumereq (DM_Word dm_word);
|
||||
return unpack (dm_word [30]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmcontrol_hartreset (DM_Word dm_word);
|
||||
return unpack (dm_word [29]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmcontrol_hasel (DM_Word dm_word);
|
||||
return unpack (dm_word [26]);
|
||||
endfunction
|
||||
|
||||
function Bit #(10) fn_dmcontrol_hartsel (DM_Word dm_word);
|
||||
return dm_word [25:16];
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmcontrol_ndmreset (DM_Word dm_word);
|
||||
return unpack (dm_word [1]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmcontrol_dmactive (DM_Word dm_word);
|
||||
return unpack (dm_word [0]);
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 'dmstatus' register
|
||||
|
||||
function Bool fn_dmstatus_allresumeack (DM_Word x);
|
||||
return unpack (x [17]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_anyresumeack (DM_Word x);
|
||||
return unpack (x [16]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_allnonexistent (DM_Word x);
|
||||
return unpack (x [15]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_anynonexistent (DM_Word x);
|
||||
return unpack (x [14]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_allunavail (DM_Word x);
|
||||
return unpack (x [13]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_anyunavail (DM_Word x);
|
||||
return unpack (x [12]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_allrunning (DM_Word x);
|
||||
return unpack (x [11]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_anyrunning (DM_Word x);
|
||||
return unpack (x [10]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_allhalted (DM_Word x);
|
||||
return unpack (x [9]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_anyhalted (DM_Word x);
|
||||
return unpack (x [8]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_authenticated (DM_Word x);
|
||||
return unpack (x [7]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_authbusy (DM_Word x);
|
||||
return unpack (x [6]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_dmstatus_devtreevalid (DM_Word x);
|
||||
return unpack (x [4]);
|
||||
endfunction
|
||||
|
||||
function Bit #(4) fn_dmstatus_version (DM_Word x);
|
||||
return unpack (x [3:0]);
|
||||
endfunction
|
||||
|
||||
function Fmt fshow_dmstatus (DM_Word x);
|
||||
Fmt fmt_version = ( (x[3:0] == 0)
|
||||
? $format ("v.none")
|
||||
: ( (x[3:0] == 1)
|
||||
? $format ("v0.11")
|
||||
: ( (x[3:0] == 2)
|
||||
? $format ("v0.13")
|
||||
: $format ("v??"))));
|
||||
|
||||
return ( $format ("(all/any) ")
|
||||
+ $format ("resumeack %0d/%0d ", x[17], x[16])
|
||||
+ $format ("nonexistent %0d/%0d ", x[15], x[14])
|
||||
+ $format ("unavail %0d/%0d ", x[13], x[12])
|
||||
+ $format ("running %0d/%0d ", x[11], x[10])
|
||||
+ $format ("halted %0d/%0d ", x[9], x[8])
|
||||
+ $format ("authenticated %0d ", x[7])
|
||||
+ $format ("authbusy %0d ", x[6])
|
||||
+ $format ("devtreevalid %0d ", x[4])
|
||||
+ fmt_version);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Abstract Command register fields
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 'dm_abstractcs' register
|
||||
|
||||
typedef enum {DM_ABSTRACTCS_CMDERR_NONE, // 0
|
||||
DM_ABSTRACTCS_CMDERR_BUSY, // 1
|
||||
DM_ABSTRACTCS_CMDERR_NOT_SUPPORTED, // 2
|
||||
DM_ABSTRACTCS_CMDERR_EXCEPTION, // 3
|
||||
DM_ABSTRACTCS_CMDERR_HALT_RESUME, // 4
|
||||
DM_ABSTRACTCS_CMDERR_UNDEF5, // 5
|
||||
DM_ABSTRACTCS_CMDERR_UNDEF6, // 6
|
||||
DM_ABSTRACTCS_CMDERR_OTHER // 7
|
||||
} DM_abstractcs_cmderr
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
// The following is used in writes, to clear cmderr
|
||||
DM_abstractcs_cmderr dm_cmderr_w1c = DM_ABSTRACTCS_CMDERR_OTHER;
|
||||
|
||||
function DM_Word fn_mk_abstractcs (DM_abstractcs_cmderr cmderr);
|
||||
return { 0, pack (cmderr), 8'h0 };
|
||||
endfunction
|
||||
|
||||
function Bit #(5) fn_abstractcs_progsize (DM_Word dm_word);
|
||||
return unpack (dm_word [28:24]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_abstractcs_busy (DM_Word dm_word);
|
||||
return unpack (dm_word [12]);
|
||||
endfunction
|
||||
|
||||
function DM_abstractcs_cmderr fn_abstractcs_cmderr (DM_Word dm_word);
|
||||
return unpack (dm_word [10:8]);
|
||||
endfunction
|
||||
|
||||
function Bit #(5) fn_abstractcs_datacount (DM_Word dm_word);
|
||||
return unpack (dm_word [4:0]);
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 'command' register
|
||||
|
||||
typedef enum {DM_COMMAND_CMDTYPE_ACCESS_REG,
|
||||
DM_COMMAND_CMDTYPE_QUICK_ACCESS
|
||||
} DM_command_cmdtype
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
typedef enum {DM_COMMAND_ACCESS_REG_SIZE_UNDEF0, // 0
|
||||
DM_COMMAND_ACCESS_REG_SIZE_UNDEF1, // 1
|
||||
DM_COMMAND_ACCESS_REG_SIZE_LOWER32, // 2
|
||||
DM_COMMAND_ACCESS_REG_SIZE_LOWER64, // 3
|
||||
DM_COMMAND_ACCESS_REG_SIZE_LOWER128, // 4
|
||||
DM_COMMAND_ACCESS_REG_SIZE_UNDEF5, // 5
|
||||
DM_COMMAND_ACCESS_REG_SIZE_UNDEF6, // 6
|
||||
DM_COMMAND_ACCESS_REG_SIZE_UNDEF7 // 7
|
||||
} DM_command_access_reg_size
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
Integer dm_command_access_reg_regno_csr_0 = 'h0000;
|
||||
Integer dm_command_access_reg_regno_csr_FFF = 'h0FFF;
|
||||
Integer dm_command_access_reg_regno_gpr_0 = 'h1000;
|
||||
Integer dm_command_access_reg_regno_gpr_1F = 'h101F;
|
||||
Integer dm_command_access_reg_regno_fpr_0 = 'h1020;
|
||||
Integer dm_command_access_reg_regno_fpr_1F = 'h103F;
|
||||
|
||||
function DM_Word fn_mk_command_access_reg (DM_command_access_reg_size size,
|
||||
Bool postexec,
|
||||
Bool transfer,
|
||||
Bool write,
|
||||
Bit #(16) regno);
|
||||
Bit #(8) b8_cmdtype = zeroExtend (pack (DM_COMMAND_CMDTYPE_ACCESS_REG));
|
||||
Bit #(3) b3_size = pack (size);
|
||||
return {b8_cmdtype,
|
||||
1'b0,
|
||||
b3_size,
|
||||
1'b0,
|
||||
pack (postexec),
|
||||
pack (transfer),
|
||||
pack (write),
|
||||
regno};
|
||||
endfunction
|
||||
|
||||
function DM_command_cmdtype fn_command_cmdtype (DM_Word dm_word);
|
||||
return unpack (truncate (dm_word [31:24]));
|
||||
endfunction
|
||||
|
||||
function DM_command_access_reg_size fn_command_access_reg_size (DM_Word dm_word);
|
||||
return unpack (dm_word [22:20]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_command_access_reg_postexec (DM_Word dm_word);
|
||||
return unpack (dm_word [18]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_command_access_reg_transfer (DM_Word dm_word);
|
||||
return unpack (dm_word [17]);
|
||||
endfunction
|
||||
|
||||
function Bool fn_command_access_reg_write (DM_Word dm_word);
|
||||
return unpack (dm_word [16]);
|
||||
endfunction
|
||||
|
||||
function Bit #(16) fn_command_access_reg_regno (DM_Word dm_word);
|
||||
return dm_word [15:0];
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// System Bus Access DM register fields
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// 'dm_sbcs' register
|
||||
|
||||
typedef enum {DM_SBACCESS_8_BIT,
|
||||
DM_SBACCESS_16_BIT,
|
||||
DM_SBACCESS_32_BIT,
|
||||
DM_SBACCESS_64_BIT,
|
||||
DM_SBACCESS_128_BIT
|
||||
} DM_sbaccess
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
function Integer fn_sbaccess_to_addr_incr (DM_sbaccess sbaccess);
|
||||
case (sbaccess)
|
||||
DM_SBACCESS_8_BIT: return 1;
|
||||
DM_SBACCESS_16_BIT: return 2;
|
||||
DM_SBACCESS_32_BIT: return 4;
|
||||
DM_SBACCESS_64_BIT: return 8;
|
||||
DM_SBACCESS_128_BIT: return 16;
|
||||
endcase
|
||||
endfunction
|
||||
|
||||
typedef enum {DM_SBERROR_NONE, // 0
|
||||
DM_SBERROR_TIMEOUT, // 1
|
||||
DM_SBERROR_BADADDR, // 2
|
||||
DM_SBERROR_OTHER, // 3
|
||||
DM_SBERROR_BUSY_STALE, // 4
|
||||
DM_SBERROR_UNDEF5, // 5
|
||||
DM_SBERROR_UNDEF6, // 6
|
||||
DM_SBERROR_UNDEF7_W1C // 7, used in writes, to clear sberror
|
||||
} DM_sberror
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
// Constructor
|
||||
|
||||
function DM_Word fn_mk_sbcs_val (Bit #(3) sbversion,
|
||||
Bool sbbusyerror,
|
||||
Bool sbbusy,
|
||||
Bool sbreadonaddr,
|
||||
DM_sbaccess sbaccess,
|
||||
Bool sbautoincrement,
|
||||
Bool sbreadondata,
|
||||
DM_sberror sberror,
|
||||
Bit #(7) sbasize,
|
||||
Bit #(1) sbaccess128,
|
||||
Bit #(1) sbaccess64,
|
||||
Bit #(1) sbaccess32,
|
||||
Bit #(1) sbaccess16,
|
||||
Bit #(1) sbaccess8);
|
||||
return {sbversion,
|
||||
6'b0,
|
||||
pack (sbbusyerror),
|
||||
pack (sbbusy),
|
||||
pack (sbreadonaddr),
|
||||
pack (sbaccess),
|
||||
pack (sbautoincrement),
|
||||
pack (sbreadondata),
|
||||
pack (sberror),
|
||||
sbasize,
|
||||
sbaccess128,
|
||||
sbaccess64,
|
||||
sbaccess32,
|
||||
sbaccess16,
|
||||
sbaccess8};
|
||||
endfunction
|
||||
|
||||
// Selectors
|
||||
|
||||
function Bit #(3) fn_sbcs_sbversion (DM_Word dm_word); return unpack (dm_word [31:29]); endfunction
|
||||
function Bool fn_sbcs_sbbusyerror (DM_Word dm_word); return unpack (dm_word [22]); endfunction
|
||||
function Bool fn_sbcs_sbbusy (DM_Word dm_word); return unpack (dm_word [21]); endfunction
|
||||
function Bool fn_sbcs_sbreadonaddr (DM_Word dm_word); return unpack (dm_word [20]); endfunction
|
||||
function DM_sbaccess fn_sbcs_sbaccess (DM_Word dm_word); return unpack (dm_word [19:17]); endfunction
|
||||
function Bool fn_sbcs_sbautoincrement (DM_Word dm_word); return unpack (dm_word [16]); endfunction
|
||||
function Bool fn_sbcs_sbreadondata (DM_Word dm_word); return unpack (dm_word [15]); endfunction
|
||||
function DM_sberror fn_sbcs_sberror (DM_Word dm_word); return unpack (dm_word [14:12]); endfunction
|
||||
function Bit #(7) fn_sbcs_sbasize (DM_Word dm_word); return dm_word [11:5]; endfunction
|
||||
function Bool fn_sbcs_sbaccess128 (DM_Word dm_word); return unpack (dm_word [4]); endfunction
|
||||
function Bool fn_sbcs_sbaccess64 (DM_Word dm_word); return unpack (dm_word [3]); endfunction
|
||||
function Bool fn_sbcs_sbaccess32 (DM_Word dm_word); return unpack (dm_word [2]); endfunction
|
||||
function Bool fn_sbcs_sbaccess16 (DM_Word dm_word); return unpack (dm_word [1]); endfunction
|
||||
function Bool fn_sbcs_sbaccess8 (DM_Word dm_word); return unpack (dm_word [0]); endfunction
|
||||
|
||||
// Debugging
|
||||
|
||||
function Fmt fshow_sbcs (DM_Word dm_word);
|
||||
return ( $format ("SBCS{")
|
||||
+ $format ("sbversion %0d", fn_sbcs_sbversion (dm_word))
|
||||
+ $format (" sbbusyerror %0d", fn_sbcs_sbbusyerror (dm_word))
|
||||
+ $format (" sbbusy %0d", fn_sbcs_sbbusy (dm_word))
|
||||
+ $format (" sbreadonaddr ") + fshow (fn_sbcs_sbreadonaddr (dm_word))
|
||||
+ $format (" sbaccess ") + fshow (fn_sbcs_sbaccess (dm_word))
|
||||
+ $format (" sbautoincrement ") + fshow (fn_sbcs_sbautoincrement (dm_word))
|
||||
+ $format (" sbreadondata ") + fshow (fn_sbcs_sbreadondata (dm_word))
|
||||
+ $format (" sberror ") + fshow (fn_sbcs_sberror (dm_word))
|
||||
+ $format (" sbasize %0d", fn_sbcs_sbasize (dm_word))
|
||||
+ $format (" sbaccess")
|
||||
+ ((fn_sbcs_sbaccess128 (dm_word)) ? $format ("_128") : $format ("x"))
|
||||
+ ((fn_sbcs_sbaccess64 (dm_word)) ? $format ("_64") : $format ("x"))
|
||||
+ ((fn_sbcs_sbaccess32 (dm_word)) ? $format ("_32") : $format ("x"))
|
||||
+ ((fn_sbcs_sbaccess16 (dm_word)) ? $format ("_16") : $format ("x"))
|
||||
+ ((fn_sbcs_sbaccess8 (dm_word)) ? $format ("_8") : $format ("x"))
|
||||
+ $format ("}"));
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// DCSR 'cause' field values
|
||||
|
||||
typedef enum {DCSR_CAUSE_RESERVED0,
|
||||
DCSR_CAUSE_EBREAK,
|
||||
DCSR_CAUSE_TRIGGER,
|
||||
DCSR_CAUSE_HALTREQ,
|
||||
DCSR_CAUSE_STEP,
|
||||
DCSR_CAUSE_RESERVED5,
|
||||
DCSR_CAUSE_RESERVED6,
|
||||
DCSR_CAUSE_RESERVED7
|
||||
} DCSR_Cause
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
// ================================================================
|
||||
// Sub-interface of the Debug Module facing the remote debugger (e.g. GDB)
|
||||
|
||||
interface DMI;
|
||||
method Action read_addr (DM_Addr dm_addr);
|
||||
method ActionValue #(DM_Word) read_data;
|
||||
method Action write (DM_Addr dm_addr, DM_Word dm_word);
|
||||
endinterface
|
||||
|
||||
// A dummy interface to tie off DMI if it is not used.
|
||||
|
||||
DMI dummy_DMI_ifc = interface DMI;
|
||||
method Action read_addr (DM_Addr dm_addr) = noAction;
|
||||
method ActionValue #(DM_Word) read_data = actionvalue
|
||||
return 0;
|
||||
endactionvalue;
|
||||
method Action write (DM_Addr dm_addr, DM_Word dm_word) = noAction;
|
||||
endinterface;
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
343
src_Core/Debug_Module/DM_Run_Control.bsv
Normal file
343
src_Core/Debug_Module/DM_Run_Control.bsv
Normal file
@@ -0,0 +1,343 @@
|
||||
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package DM_Run_Control;
|
||||
|
||||
// ================================================================
|
||||
// This package implements the 'Run Control' part of the RISC-V Debug
|
||||
// Module, i.e., reset platform, reset hart, run/halt hart
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import ISA_Decls :: *;
|
||||
import DM_Common :: *;
|
||||
|
||||
// ================================================================
|
||||
// Interface
|
||||
|
||||
interface DM_Run_Control_IFC;
|
||||
method Bool dmactive;
|
||||
method Action reset;
|
||||
|
||||
// ----------------
|
||||
// DMI facing GDB/host
|
||||
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
|
||||
method Action write (DM_Addr dm_addr, DM_Word dm_word);
|
||||
|
||||
// ----------------
|
||||
// Facing a hart: reset and run-control
|
||||
interface Get #(Token) hart0_get_reset_req;
|
||||
interface Client #(Bool, Bool) hart0_client_run_halt;
|
||||
interface Get #(Bit #(4)) hart0_get_other_req;
|
||||
|
||||
// ----------------
|
||||
// Facing Platform: Non-Debug-Module Reset (reset all except DM)
|
||||
interface Get #(Token) get_ndm_reset_req;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkDM_Run_Control (DM_Run_Control_IFC);
|
||||
|
||||
Integer verbosity = 0; // Normally 0; non-zero for debugging
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// NDM Reset
|
||||
|
||||
FIFOF #(Token) f_ndm_reset_reqs <- mkFIFOF;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Hart0 run control
|
||||
|
||||
Reg #(Bool) rg_hart0_running <- mkRegU;
|
||||
|
||||
// Reset requests to hart
|
||||
FIFOF #(Token) f_hart0_reset_reqs <- mkFIFOF;
|
||||
|
||||
// Run/halt requests to hart and responses
|
||||
FIFOF #(Bool) f_hart0_run_halt_reqs <- mkFIFOF;
|
||||
FIFOF #(Bool) f_hart0_run_halt_rsps <- mkFIFOF;
|
||||
|
||||
// Non-standard requests to hart and responses
|
||||
// Currently only verbosity
|
||||
FIFOF #(Bit #(4)) f_hart0_other_reqs <- mkFIFOF;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
Bit #(32) haltregion0 = { 31'h0, pack (! rg_hart0_running) };
|
||||
Bit #(32) haltsum = { 31'h0, pack (! rg_hart0_running) };
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// rg_dmstatus
|
||||
// Since we currently support only 1 hart,
|
||||
// 'anyXX' = 'allXX'
|
||||
// 'allrunning' = NOT 'allhalted'
|
||||
|
||||
Reg#(Bool) rg_dmstatus_allresumeack <- mkRegU;
|
||||
|
||||
Bool dmstatus_allresumeack = rg_dmstatus_allresumeack;
|
||||
Bool dmstatus_anyresumeack = rg_dmstatus_allresumeack;
|
||||
|
||||
Bool dmstatus_allnonexistent = False;
|
||||
Bool dmstatus_anynonexistent = dmstatus_allnonexistent;
|
||||
|
||||
Bool dmstatus_allunavail = False;
|
||||
Bool dmstatus_anyunavail = dmstatus_allunavail;
|
||||
|
||||
Bool dmstatus_allrunning = rg_hart0_running;
|
||||
Bool dmstatus_anyrunning = dmstatus_allrunning;
|
||||
|
||||
Bool dmstatus_allhalted = (! rg_hart0_running);
|
||||
Bool dmstatus_anyhalted = dmstatus_allhalted;
|
||||
|
||||
DM_Word virt_rg_dmstatus = {14'b0,
|
||||
pack (dmstatus_allresumeack),
|
||||
pack (dmstatus_anyresumeack),
|
||||
pack (dmstatus_allnonexistent),
|
||||
pack (dmstatus_anynonexistent),
|
||||
pack (dmstatus_allunavail),
|
||||
pack (dmstatus_anyunavail),
|
||||
pack (dmstatus_allrunning),
|
||||
pack (dmstatus_anyrunning),
|
||||
pack (dmstatus_allhalted),
|
||||
pack (dmstatus_anyhalted),
|
||||
pack (True), // authenticated
|
||||
pack (False), // authbusy
|
||||
1'b0,
|
||||
pack (False), // devtreevalid
|
||||
4'h2}; // version
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// rg_dmcontrol
|
||||
|
||||
Reg #(Bool) rg_dmcontrol_haltreq <- mkRegU;
|
||||
// resumereq is a W1 field, no need for a register
|
||||
Reg #(Bool) rg_dmcontrol_hartreset <- mkRegU;
|
||||
Reg #(Bool) rg_dmcontrol_ndmreset <- mkRegU;
|
||||
Reg #(Bool) rg_dmcontrol_dmactive <- mkReg (False);
|
||||
|
||||
DM_Word virt_rg_dmcontrol = {2'b0, // haltreq, resumereq (w-o)
|
||||
pack (rg_dmcontrol_hartreset),
|
||||
2'b0,
|
||||
pack (False), // hasel
|
||||
10'b0, // hartsel
|
||||
14'b0,
|
||||
pack (rg_dmcontrol_ndmreset),
|
||||
pack (rg_dmcontrol_dmactive)};
|
||||
|
||||
function Action fa_rg_dmcontrol_write (DM_Word dm_word);
|
||||
action
|
||||
let haltreq = fn_dmcontrol_haltreq (dm_word);
|
||||
let resumereq = fn_dmcontrol_resumereq (dm_word);
|
||||
let hartreset = fn_dmcontrol_hartreset (dm_word);
|
||||
let hasel = fn_dmcontrol_hasel (dm_word);
|
||||
let hartsel = fn_dmcontrol_hartsel (dm_word);
|
||||
let ndmreset = fn_dmcontrol_ndmreset (dm_word);
|
||||
let dmactive = fn_dmcontrol_dmactive (dm_word);
|
||||
|
||||
rg_dmcontrol_haltreq <= haltreq;
|
||||
rg_dmcontrol_hartreset <= hartreset;
|
||||
rg_dmcontrol_ndmreset <= ndmreset;
|
||||
rg_dmcontrol_dmactive <= dmactive;
|
||||
|
||||
// Debug Module reset
|
||||
if (! dmactive) begin
|
||||
// Reset the DM module itself
|
||||
$display ("DM_Run_Control.write: dmcontrol 0x%08h (dmactive=0): resetting Debug Module",
|
||||
dm_word);
|
||||
|
||||
// Error-checking
|
||||
if (ndmreset) begin
|
||||
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
|
||||
dm_word);
|
||||
$display (" [1] (ndmreset) and [0] (dmactive) both asserted");
|
||||
$display (" dmactive has priority; ignoring ndmreset");
|
||||
end
|
||||
if (hartreset) begin
|
||||
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
|
||||
dm_word);
|
||||
$display (" [29] (hartreset) and [0] (dmactive) both asserted");
|
||||
$display (" dmactive has priority; ignoring hartreset");
|
||||
end
|
||||
|
||||
// No action here; other rules will fire (see method dmactive, Debug_Module.rl_reset)
|
||||
noAction;
|
||||
end
|
||||
|
||||
// Platform reset (non-Debug Module)
|
||||
else if (ndmreset) begin
|
||||
$display ("DM_Run_Control.write: dmcontrol 0x%08h: ndmreset=1: resetting platform",
|
||||
dm_word);
|
||||
f_ndm_reset_reqs.enq (?);
|
||||
|
||||
// Error-checking
|
||||
if (hartreset) begin
|
||||
$display ("DM_Run_Control.write: WARNING: in word written to dmcontrol (0x%08h):",
|
||||
dm_word);
|
||||
$display (" Both ndmreset (bit 1) and hartreset (bit 29) are asserted");
|
||||
$display (" ndmreset has priority; ignoring hartreset");
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// Deassert platform reset
|
||||
if ((verbosity != 0) && rg_dmcontrol_ndmreset)
|
||||
$display ("DM_Run_Control.write: dmcontrol 0x%08h: clearing ndmreset", dm_word);
|
||||
|
||||
// Hart reset
|
||||
if (hartreset) begin
|
||||
if (verbosity != 0)
|
||||
$display ("DM_Run_Control.write: dmcontrol 0x%08h: hartreset=1: resetting hart",
|
||||
dm_word);
|
||||
f_hart0_reset_reqs.enq (?);
|
||||
end
|
||||
else begin
|
||||
// Deassert hart reset
|
||||
if ((verbosity != 0) && rg_dmcontrol_hartreset)
|
||||
$display ("DM_Run_Control.write: dmcontrol 0x%08h: clearing hartreset", dm_word);
|
||||
|
||||
if (hasel)
|
||||
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: 'hasel' is not supported",
|
||||
dm_word);
|
||||
|
||||
if (hartsel != 0)
|
||||
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: hartsel 0x%0h not supported",
|
||||
dm_word, hartsel);
|
||||
|
||||
if (haltreq && resumereq) begin
|
||||
$display ("DM_Run_Control.write: ERROR: dmcontrol 0x%08h: haltreq=1 and resumereq=1",
|
||||
dm_word);
|
||||
$display (" This behavior is 'undefined' in the spec; ignoring");
|
||||
end
|
||||
// Resume hart(s) if not running
|
||||
else if (resumereq && (! rg_hart0_running)) begin
|
||||
f_hart0_run_halt_reqs.enq (True);
|
||||
rg_dmstatus_allresumeack <= False;
|
||||
$display ("DM_Run_Control.write: hart0 resume request");
|
||||
end
|
||||
// Halt hart(s)
|
||||
else if (haltreq && !rg_dmcontrol_haltreq) begin
|
||||
f_hart0_run_halt_reqs.enq (False);
|
||||
$display ("DM_Run_Control.write: hart0 halt request");
|
||||
end
|
||||
end
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Unimplemented
|
||||
|
||||
// dm_addr_hartinfo
|
||||
// dm_addr_hawindowsel
|
||||
// dm_addr_hawindow
|
||||
// dm_addr_devtreeaddr0..3
|
||||
// dm_addr_authdata
|
||||
// dm_addr_haltregion1..31
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// rg_verbosity: non-standard
|
||||
|
||||
Reg #(Bit #(4)) rg_verbosity <- mkRegU;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
rule rl_hart0_run_rsp;
|
||||
let x = f_hart0_run_halt_rsps.first;
|
||||
f_hart0_run_halt_rsps.deq;
|
||||
|
||||
rg_hart0_running <= x;
|
||||
if (x) begin
|
||||
rg_dmstatus_allresumeack <= True;
|
||||
$display ("DM_Run_Control: hart0 running");
|
||||
end
|
||||
else begin
|
||||
$display ("DM_Run_Control: hart0 halted");
|
||||
end
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// INTERFACE
|
||||
|
||||
method Bool dmactive;
|
||||
return rg_dmcontrol_dmactive;
|
||||
endmethod
|
||||
|
||||
method Action reset;
|
||||
f_ndm_reset_reqs.clear;
|
||||
|
||||
rg_hart0_running <= False; // Must be same as initial state of CPU
|
||||
f_hart0_reset_reqs.clear;
|
||||
f_hart0_run_halt_reqs.clear;
|
||||
f_hart0_run_halt_rsps.clear;
|
||||
|
||||
rg_dmcontrol_haltreq <= False;
|
||||
rg_dmcontrol_hartreset <= False;
|
||||
rg_dmcontrol_ndmreset <= False;
|
||||
rg_dmcontrol_dmactive <= True; // DM module is now active
|
||||
|
||||
rg_dmstatus_allresumeack <= False;
|
||||
|
||||
rg_verbosity <= 0;
|
||||
|
||||
if (verbosity != 0)
|
||||
$display ("DM_Run_Control: reset");
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// DMI facing GDB/host
|
||||
|
||||
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
|
||||
actionvalue
|
||||
DM_Word dm_word = case (dm_addr)
|
||||
dm_addr_dmcontrol: virt_rg_dmcontrol;
|
||||
dm_addr_dmstatus: virt_rg_dmstatus;
|
||||
dm_addr_haltsum: haltsum;
|
||||
dm_addr_haltregion0: haltregion0;
|
||||
dm_addr_verbosity: extend (rg_verbosity);
|
||||
endcase;
|
||||
|
||||
if (verbosity != 0)
|
||||
$display ("DM_Run_Control.av_read: [", fshow_dm_addr (dm_addr), "] => 0x%08h", dm_word);
|
||||
|
||||
return dm_word;
|
||||
endactionvalue
|
||||
endmethod
|
||||
|
||||
method Action write (DM_Addr dm_addr, DM_Word dm_word);
|
||||
action
|
||||
if (verbosity != 0)
|
||||
$display ("DM_Run_Control.write: [", fshow_dm_addr (dm_addr), "] <= 0x%08h", dm_word);
|
||||
|
||||
case (dm_addr)
|
||||
dm_addr_dmcontrol: fa_rg_dmcontrol_write (dm_word);
|
||||
dm_addr_verbosity: begin
|
||||
rg_verbosity <= truncate (dm_word);
|
||||
f_hart0_other_reqs.enq (truncate (dm_word));
|
||||
end
|
||||
default: noAction;
|
||||
endcase
|
||||
endaction
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Facing Hart: Reset, Run-control, etc.
|
||||
interface Get hart0_get_reset_req = toGet (f_hart0_reset_reqs);
|
||||
interface Client hart0_client_run_halt = toGPClient (f_hart0_run_halt_reqs, f_hart0_run_halt_rsps);
|
||||
interface Get hart0_get_other_req = toGet (f_hart0_other_reqs);
|
||||
|
||||
// ----------------
|
||||
// Facing Platform: Non-Debug-Module Reset (reset all except DM)
|
||||
interface Get get_ndm_reset_req = toGet (f_ndm_reset_reqs);
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
663
src_Core/Debug_Module/DM_System_Bus.bsv
Normal file
663
src_Core/Debug_Module/DM_System_Bus.bsv
Normal file
@@ -0,0 +1,663 @@
|
||||
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package DM_System_Bus;
|
||||
|
||||
// ================================================================
|
||||
// This package implements the 'System Bus Access' part of the RISC-V
|
||||
// Debug Module, i.e., read/write access to RISC-V system memory.
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import FIFOF :: *;
|
||||
|
||||
// ----------------
|
||||
// Other library imports
|
||||
|
||||
import Semi_FIFOF :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project Imports
|
||||
|
||||
import ISA_Decls :: *;
|
||||
import DM_Common :: *;
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
// ================================================================
|
||||
// Interface
|
||||
|
||||
interface DM_System_Bus_IFC;
|
||||
method Action reset;
|
||||
|
||||
// ----------------
|
||||
// DMI facing GDB/host
|
||||
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr);
|
||||
method Action write (DM_Addr dm_addr, DM_Word dm_word);
|
||||
|
||||
// ----------------
|
||||
// Facing System
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
// Local definitions
|
||||
|
||||
// ----------------
|
||||
// Convert DM code for access size to AXI4 code for access size
|
||||
|
||||
function AXI4_Size fn_DM_sbaccess_to_AXI4_Size (DM_sbaccess sbaccess);
|
||||
AXI4_Size axi4_size = case (sbaccess)
|
||||
DM_SBACCESS_8_BIT: axsize_1;
|
||||
DM_SBACCESS_16_BIT: axsize_2;
|
||||
DM_SBACCESS_32_BIT: axsize_4;
|
||||
DM_SBACCESS_64_BIT: axsize_8;
|
||||
endcase;
|
||||
return axi4_size;
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// Extract bytes from raw word read from fabric.
|
||||
// The bytes of interest are offset according to LSBs of addr.
|
||||
// Arguments:
|
||||
// - a DM_sbaccess (indicating size of access)
|
||||
// - a byte-address
|
||||
// - a load-word from fabric
|
||||
// result:
|
||||
// - word with correct byte(s) shifted into LSBs and zero extended
|
||||
|
||||
function Bit #(64) fn_extract_and_extend_bytes (DM_sbaccess sbaccess,
|
||||
Bit #(64) read_addr,
|
||||
Bit #(64) word64);
|
||||
Bit #(3) addr_lsbs = read_addr [2:0];
|
||||
if (valueOf (Wd_Data) == 32)
|
||||
addr_lsbs = (addr_lsbs & 'h3);
|
||||
|
||||
Bit #(64) result = 0;
|
||||
case (sbaccess)
|
||||
DM_SBACCESS_8_BIT: case (addr_lsbs)
|
||||
'h0: result = zeroExtend (word64 [ 7: 0]);
|
||||
'h1: result = zeroExtend (word64 [15: 8]);
|
||||
'h2: result = zeroExtend (word64 [23:16]);
|
||||
'h3: result = zeroExtend (word64 [31:24]);
|
||||
'h4: result = zeroExtend (word64 [39:32]);
|
||||
'h5: result = zeroExtend (word64 [47:40]);
|
||||
'h6: result = zeroExtend (word64 [55:48]);
|
||||
'h7: result = zeroExtend (word64 [63:56]);
|
||||
endcase
|
||||
|
||||
DM_SBACCESS_16_BIT: case (addr_lsbs)
|
||||
'h0: result = zeroExtend (word64 [15: 0]);
|
||||
'h2: result = zeroExtend (word64 [31:16]);
|
||||
'h4: result = zeroExtend (word64 [47:32]);
|
||||
'h6: result = zeroExtend (word64 [63:48]);
|
||||
endcase
|
||||
|
||||
DM_SBACCESS_32_BIT: case (addr_lsbs)
|
||||
'h0: result = zeroExtend (word64 [31: 0]);
|
||||
'h4: result = zeroExtend (word64 [63:32]);
|
||||
endcase
|
||||
|
||||
DM_SBACCESS_64_BIT: case (addr_lsbs)
|
||||
'h0: result = word64;
|
||||
endcase
|
||||
endcase
|
||||
return result;
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// Compute address, data and strobe (byte-enables) for writes to fabric
|
||||
|
||||
function Tuple4 #(Fabric_Addr, // addr is 32b- or 64b-aligned
|
||||
Fabric_Data, // data is lane-aligned
|
||||
Fabric_Strb, // strobe
|
||||
AXI4_Size) // 8 for 8-byte writes, else 4
|
||||
fn_to_fabric_write_fields (DM_sbaccess sbaccess, // size of access
|
||||
Bit #(64) addr,
|
||||
Bit #(64) word64); // data is in lsbs
|
||||
|
||||
// First compute addr, data and strobe for a 64b-wide fabric
|
||||
Bit #(8) strobe64 = 0;
|
||||
Bit #(3) shift_bytes = addr [2:0];
|
||||
Bit #(6) shift_bits = { shift_bytes, 3'b0 };
|
||||
AXI4_Size axsize = axsize_128; // Will be updated in 'case' below
|
||||
|
||||
case (sbaccess)
|
||||
DM_SBACCESS_8_BIT: begin
|
||||
word64 = (word64 << shift_bits);
|
||||
strobe64 = ('b_1 << shift_bytes);
|
||||
axsize = axsize_1;
|
||||
end
|
||||
DM_SBACCESS_16_BIT: begin
|
||||
word64 = (word64 << shift_bits);
|
||||
strobe64 = ('b_11 << shift_bytes);
|
||||
axsize = axsize_2;
|
||||
end
|
||||
DM_SBACCESS_32_BIT: begin
|
||||
word64 = (word64 << shift_bits);
|
||||
strobe64 = ('b_1111 << shift_bytes);
|
||||
axsize = axsize_4;
|
||||
end
|
||||
DM_SBACCESS_64_BIT: begin
|
||||
strobe64 = 'b_1111_1111;
|
||||
axsize = axsize_8;
|
||||
end
|
||||
endcase
|
||||
|
||||
// Adjust for 32b fabrics
|
||||
if ((valueOf (Wd_Data) == 32) && (addr [2] == 1'b1)) begin
|
||||
word64 = { 32'h0, word64 [63:32] };
|
||||
strobe64 = { 4'h0, strobe64 [7:4] };
|
||||
end
|
||||
|
||||
// Finally, create fabric addr/data/strobe
|
||||
Fabric_Addr fabric_addr = truncate (addr);
|
||||
Fabric_Data fabric_data = truncate (word64);
|
||||
Fabric_Strb fabric_strobe = truncate (strobe64);
|
||||
|
||||
return tuple4 (fabric_addr, fabric_data, fabric_strobe, axsize);
|
||||
endfunction: fn_to_fabric_write_fields
|
||||
|
||||
// ----------------
|
||||
// System Bus access states
|
||||
|
||||
typedef enum {SB_NOTBUSY,
|
||||
SB_READ_FINISH,
|
||||
SB_WRITE_FINISH
|
||||
} SB_State
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
// ================================================================
|
||||
// Module implementation
|
||||
|
||||
(* synthesize *)
|
||||
module mkDM_System_Bus (DM_System_Bus_IFC);
|
||||
|
||||
Integer verbosity = 0; // Normally 0; non-zero for debugging
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
// Interface to memory fabric
|
||||
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor_2;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// System Bus state
|
||||
|
||||
Reg #(SB_State) rg_sb_state <- mkRegU;
|
||||
Bool sbbusy = (rg_sb_state != SB_NOTBUSY);
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// rg_sbaddress0,1 (2 not implemented)
|
||||
// rg_sbdata0 (1, 2, 3 not implemented)
|
||||
// Support for RV64. Instead of defining in terms of XLEN, defining using
|
||||
// DM_Word which is always Bit#(32). 64-bit addressing supported for RV64 but
|
||||
// only 32-bit data accesses are supported from debugger
|
||||
|
||||
Reg #(DM_Word) rg_sbaddress0 <- mkReg (0);
|
||||
Reg #(DM_Word) rg_sbaddress1 <- mkReg (0); // Will always be zero for RV32
|
||||
|
||||
// Saved address during a read rg_sbaddress0/1 may be autoincremented,
|
||||
// but we need original addr byte-lane extraction from response
|
||||
Reg #(Bit #(64)) rg_sbaddress_reading <- mkRegU;
|
||||
|
||||
Bit #(64) sbaddress = { rg_sbaddress1, rg_sbaddress0 };
|
||||
|
||||
Reg #(DM_Word) rg_sbdata0 <- mkRegU;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// rg_sbcs
|
||||
|
||||
Reg #(Bool) rg_sbcs_sbbusyerror <- mkRegU;
|
||||
Reg #(Bool) rg_sbcs_sbreadonaddr <- mkRegU;
|
||||
Reg #(DM_sbaccess) rg_sbcs_sbaccess <- mkRegU;
|
||||
Reg #(Bool) rg_sbcs_sbautoincrement <- mkRegU;
|
||||
Reg #(Bool) rg_sbcs_sbreadondata <- mkRegU;
|
||||
Reg #(DM_sberror) rg_sbcs_sberror <- mkRegU;
|
||||
|
||||
UInt #(3) sbversion = 1;
|
||||
|
||||
DM_Word virt_rg_sbcs = {pack (sbversion),
|
||||
6'b0,
|
||||
pack (rg_sbcs_sbbusyerror),
|
||||
pack (sbbusy),
|
||||
pack (rg_sbcs_sbreadonaddr),
|
||||
pack (rg_sbcs_sbaccess),
|
||||
pack (rg_sbcs_sbautoincrement),
|
||||
pack (rg_sbcs_sbreadondata),
|
||||
pack (rg_sbcs_sberror),
|
||||
`ifdef RV64
|
||||
7'd64, // sbasize -- address size
|
||||
`endif
|
||||
`ifdef RV32
|
||||
7'd32, // sbasize -- address size
|
||||
`endif
|
||||
1'b0, // sbaccess128
|
||||
1'b0, // sbaccess64
|
||||
1'b1, // sbaccess32
|
||||
1'b1, // sbaccess16
|
||||
1'b1}; // sbaccess8
|
||||
|
||||
// ----------------
|
||||
// Local defs and help functions
|
||||
|
||||
Integer addr_incr = fn_sbaccess_to_addr_incr (rg_sbcs_sbaccess);
|
||||
|
||||
function Action fa_sbaddress_incr (Bit #(64) addr64);
|
||||
action
|
||||
Bit #(64) next_sbaddress = addr64 + fromInteger (addr_incr);
|
||||
`ifdef RV64
|
||||
rg_sbaddress1 <= next_sbaddress [63:32];
|
||||
`else
|
||||
rg_sbaddress1 <= 0;
|
||||
`endif
|
||||
rg_sbaddress0 <= next_sbaddress [31:0];
|
||||
|
||||
if (verbosity != 0)
|
||||
$display (" Increment sbaddr 0x%08h -> 0x%08h", addr64, next_sbaddress);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// Construction and sending of fabric read-requests
|
||||
|
||||
function Action fa_fabric_send_read_req (Bit #(64) addr64);
|
||||
action
|
||||
Fabric_Addr fabric_addr = truncate (addr64);
|
||||
let rda = AXI4_Rd_Addr {arid: fabric_default_id,
|
||||
araddr: fabric_addr,
|
||||
arlen: 0, // burst len = arlen+1
|
||||
arsize: fn_DM_sbaccess_to_AXI4_Size (rg_sbcs_sbaccess),
|
||||
arburst: fabric_default_burst,
|
||||
arlock: fabric_default_lock,
|
||||
arcache: fabric_default_arcache,
|
||||
arprot: fabric_default_prot,
|
||||
arqos: fabric_default_qos,
|
||||
arregion: fabric_default_region,
|
||||
aruser: fabric_default_user};
|
||||
master_xactor.i_rd_addr.enq (rda);
|
||||
|
||||
// Save read-address for byte-lane extraction from later response
|
||||
// (since rg_sbaddress may be incremented by then).
|
||||
rg_sbaddress_reading <= addr64;
|
||||
|
||||
rg_sb_state <= SB_READ_FINISH;
|
||||
|
||||
if (verbosity != 0) begin
|
||||
$display (" DM_System_Bus.fa_fabric_send_read_req, and => SB_READ_FINISH ");
|
||||
$display (" ", fshow (rda));
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// Construction and sending of fabric write-requests
|
||||
|
||||
function Action fa_fabric_send_write_req (Bit #(64) data64);
|
||||
action
|
||||
match {.fabric_addr,
|
||||
.fabric_data,
|
||||
.fabric_strb,
|
||||
.fabric_size} = fn_to_fabric_write_fields (rg_sbcs_sbaccess, sbaddress, data64);
|
||||
|
||||
// fabric_addr is always fabric-data-width aligned
|
||||
// fabric_data is properly lane-adjusted
|
||||
// fabric_strb identifies the lanes to be written
|
||||
// awsize is always the fabric width
|
||||
|
||||
let wra = AXI4_Wr_Addr {awid: fabric_default_id,
|
||||
awaddr: fabric_addr,
|
||||
awlen: 0, // burst len = awlen+1
|
||||
awsize: fabric_size,
|
||||
awburst: fabric_default_burst,
|
||||
awlock: fabric_default_lock,
|
||||
awcache: fabric_default_awcache,
|
||||
awprot: fabric_default_prot,
|
||||
awqos: fabric_default_qos,
|
||||
awregion: fabric_default_region,
|
||||
awuser: fabric_default_user};
|
||||
master_xactor.i_wr_addr.enq (wra);
|
||||
|
||||
let wrd = AXI4_Wr_Data {wid: fabric_default_id,
|
||||
wdata: fabric_data,
|
||||
wstrb: fabric_strb,
|
||||
wlast: True,
|
||||
wuser: fabric_default_user};
|
||||
master_xactor.i_wr_data.enq (wrd);
|
||||
|
||||
if (verbosity != 0) begin
|
||||
$display (" DM_System_Bus.fa_fabric_send_write_req:");
|
||||
$display (" ", fshow (wra));
|
||||
$display (" ", fshow (wrd));
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Writes to sbcs
|
||||
|
||||
function Action fa_rg_sbcs_write (DM_Word dm_word);
|
||||
action
|
||||
Bool sbbusyerror = unpack (dm_word [22]);
|
||||
Bool sbreadonaddr = unpack (dm_word [20]);
|
||||
DM_sbaccess sbaccess = unpack (dm_word [19:17]);
|
||||
Bool sbautoincrement = unpack (dm_word [16]);
|
||||
Bool sbreadondata = unpack (dm_word [15]);
|
||||
DM_sberror sberror = unpack (dm_word [14:12]);
|
||||
|
||||
// No-op if not clearing existing sberror
|
||||
if ((rg_sbcs_sberror != DM_SBERROR_NONE) && (sberror == DM_SBERROR_NONE)) begin
|
||||
// Existing error is not being cleared
|
||||
$display ("DM_System_Bus.sbcs_write <= 0x%08h: ERROR", dm_word);
|
||||
$display (" ERROR: existing sberror (0x%0h) is not being cleared.", rg_sbcs_sberror);
|
||||
$display (" Must be cleared to re-enable system bus access.");
|
||||
end
|
||||
|
||||
// No-op if not clearing existing sbbusyerror
|
||||
else if (rg_sbcs_sbbusyerror && (! sbbusyerror)) begin
|
||||
$display ("DM_System_Bus.sbcs_write <= 0x%08h: ERROR", dm_word);
|
||||
$display (" ERROR: existing sbbusyerror (%0d) is not being cleared.", rg_sbcs_sbbusyerror);
|
||||
$display (" Must be cleared to re-enable system bus access.");
|
||||
end
|
||||
|
||||
// Check that requested access size is supported
|
||||
else if ( (sbaccess == DM_SBACCESS_128_BIT)
|
||||
|| (sbaccess == DM_SBACCESS_64_BIT))
|
||||
begin
|
||||
rg_sbcs_sberror <= DM_SBERROR_OTHER;
|
||||
$display ("DM_System_Bus.sbcs_write <= 0x%08h: ERROR", dm_word);
|
||||
$display (" ERROR: sbaccess ", fshow (sbaccess), " not supported");
|
||||
end
|
||||
|
||||
// Ok
|
||||
else begin
|
||||
if (verbosity != 0) begin
|
||||
$display (" DM_System_Bus.sbcs_write: ", fshow_sbcs (dm_word));
|
||||
if (rg_sbcs_sberror != DM_SBERROR_NONE)
|
||||
$display (" Clearing sbcs.sberror");
|
||||
if (rg_sbcs_sbbusyerror)
|
||||
$display (" Clearing sbcs.sbbusyerror");
|
||||
end
|
||||
|
||||
rg_sbcs_sbbusyerror <= False;
|
||||
rg_sbcs_sbreadonaddr <= sbreadonaddr;
|
||||
rg_sbcs_sbaccess <= sbaccess;
|
||||
rg_sbcs_sbautoincrement <= sbautoincrement;
|
||||
rg_sbcs_sbreadondata <= sbreadondata;
|
||||
rg_sbcs_sberror <= DM_SBERROR_NONE;
|
||||
end
|
||||
endaction
|
||||
endfunction: fa_rg_sbcs_write
|
||||
|
||||
// ================================================================
|
||||
// rg_sbaddress0, rg_sbaddress1 writes
|
||||
|
||||
function Action fa_rg_sbaddress_write (DM_Addr dm_addr, DM_Word dm_word);
|
||||
action
|
||||
// Debug announce
|
||||
if (verbosity != 0) begin
|
||||
$write ("DM_System_Bus.sbaddress.write: [0x%08h] <= 0x%08h", dm_addr, dm_word);
|
||||
if (rg_sbcs_sbreadonaddr) begin
|
||||
$write ("; readonaddr");
|
||||
if (rg_sbcs_sbautoincrement)
|
||||
$write ("; autoincrement");
|
||||
end
|
||||
$display ("");
|
||||
end
|
||||
|
||||
if (sbbusy) begin
|
||||
$display ("DM_System_Bus.sbaddress.write: busy, setting sbbusyerror");
|
||||
rg_sbcs_sbbusyerror <= True;
|
||||
end
|
||||
|
||||
else if (rg_sbcs_sbbusyerror)
|
||||
$display ("DM_System_Bus.sbaddress.write: ignoring due to sbbusyerror");
|
||||
|
||||
else if (rg_sbcs_sberror != DM_SBERROR_NONE)
|
||||
$display ("DM_System_Bus.sbaddress.write: ignoring due to sberror = 0x%0h",
|
||||
rg_sbcs_sberror);
|
||||
|
||||
else if (dm_addr == dm_addr_sbaddress0) begin
|
||||
Bit #(64) addr64 = { rg_sbaddress1, dm_word };
|
||||
if (rg_sbcs_sbreadonaddr) begin
|
||||
fa_fabric_send_read_req (addr64);
|
||||
if (rg_sbcs_sbautoincrement)
|
||||
fa_sbaddress_incr (addr64);
|
||||
else
|
||||
rg_sbaddress0 <= dm_word;
|
||||
end
|
||||
else
|
||||
rg_sbaddress0 <= dm_word;
|
||||
end
|
||||
|
||||
else begin // (dm_addr == dm_addr_sbaddress1)
|
||||
`ifdef RV32
|
||||
rg_sbaddress1 <= 0;
|
||||
if (verbosity != 0)
|
||||
$display ("DM_System_Bus.write: [sbaddress1] <= 0 (RV32: ignoring arg value 0x%08h)",
|
||||
dm_word);
|
||||
`else
|
||||
rg_sbaddress1 <= dm_word;
|
||||
if (verbosity != 0)
|
||||
$display ("DM_System_Bus.write: [sbaddress1] <= 0x%08h", dm_word);
|
||||
`endif
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// rg_sbdata0, rg_sbdata1 reads
|
||||
|
||||
function ActionValue #(DM_Word) fav_rg_sbdata_read (DM_Addr dm_addr);
|
||||
actionvalue
|
||||
DM_Word result = 0;
|
||||
if (sbbusy) begin
|
||||
$display ("DM_System_Bus.sbdata.read: busy, setting sbbusyerror");
|
||||
rg_sbcs_sbbusyerror <= True;
|
||||
end
|
||||
|
||||
else if (rg_sbcs_sbbusyerror)
|
||||
$display ("DM_System_Bus.sbdata.read: ignoring due to sbbusyerror");
|
||||
|
||||
else if (rg_sbcs_sberror != DM_SBERROR_NONE)
|
||||
$display ("DM_System_Bus.sbdata.read: ignoring due to sberror = 0x%0h", rg_sbcs_sberror);
|
||||
|
||||
else begin
|
||||
if (dm_addr == dm_addr_sbdata0)
|
||||
result = rg_sbdata0;
|
||||
/* FUTURE: when supporting DM_SBACCESS_64_BIT
|
||||
else if (dm_addr == dm_addr_sbdata1)
|
||||
result = rg_sbdata1;
|
||||
*/
|
||||
|
||||
// Increment sbaddress if needed
|
||||
if (rg_sbcs_sbautoincrement)
|
||||
fa_sbaddress_incr (sbaddress);
|
||||
|
||||
// Auto-read next data if needed
|
||||
if (rg_sbcs_sbreadondata && (dm_addr == dm_addr_sbdata0))
|
||||
fa_fabric_send_read_req (sbaddress);
|
||||
end
|
||||
return result;
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// Finish read request (handle fabric response)
|
||||
|
||||
(* descending_urgency = "rl_sb_read_finish, reset" *)
|
||||
(* descending_urgency = "rl_sb_read_finish, write" *)
|
||||
rule rl_sb_read_finish ( (rg_sb_state == SB_READ_FINISH)
|
||||
&& (rg_sbcs_sberror == DM_SBERROR_NONE));
|
||||
let rdr <- pop_o (master_xactor.o_rd_data);
|
||||
if (verbosity != 0)
|
||||
$display ("DM_System_Bus.rule_sb_read_finish: rdr = ", fshow (rdr));
|
||||
|
||||
// Extract relevant bytes from fabric data
|
||||
Bit #(64) rdata64 = zeroExtend (rdr.rdata);
|
||||
Bit #(64) data = fn_extract_and_extend_bytes (rg_sbcs_sbaccess, rg_sbaddress_reading, rdata64);
|
||||
|
||||
if (rdr.rresp != axi4_resp_okay) begin
|
||||
$display ("DM_System_Bus.rule_sb_read_finish: setting rg_sbcs_sberror to DM_SBERROR_OTHER\n");
|
||||
$display (" rdr = ", fshow (rdr));
|
||||
rg_sbcs_sberror <= DM_SBERROR_OTHER;
|
||||
end
|
||||
|
||||
rg_sbdata0 <= data [31:0];
|
||||
/* FUTURE: when supporting DM_SBACCESS_64_BIT
|
||||
rg_sbdata1 <= data [63:32];
|
||||
*/
|
||||
|
||||
if (verbosity != 0) begin
|
||||
$display ("DM_System_Bus.rule_sb_read_finish: addr 0x%0h, sbaccess %0d (%0d bytes)",
|
||||
rg_sbaddress_reading, rg_sbcs_sbaccess, addr_incr);
|
||||
$display (" rg_sbdata0 <= 0x%0h", data);
|
||||
$display (" module state => SB_NOTBUSY");
|
||||
end
|
||||
|
||||
rg_sb_state <= SB_NOTBUSY;
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// rg_sbdata0, rg_sbdata1 writes
|
||||
|
||||
function Action fa_rg_sbdata_write (DM_Addr dm_addr, DM_Word dm_word);
|
||||
action
|
||||
if (sbbusy) begin
|
||||
$display ("DM_System_Bus.sbdata.write: busy, setting sbbusyerror");
|
||||
rg_sbcs_sbbusyerror <= True;
|
||||
end
|
||||
|
||||
else if (rg_sbcs_sbbusyerror) begin
|
||||
$display ("DM_System_Bus.sbdata.write: ignoring due to sbbusyerror");
|
||||
end
|
||||
|
||||
else if (rg_sbcs_sberror != DM_SBERROR_NONE) begin
|
||||
$display ("DM_System_Bus.sbdata.write: ignoring due to sberror = 0x%0h",
|
||||
rg_sbcs_sberror);
|
||||
end
|
||||
|
||||
else begin
|
||||
if (verbosity != 0)
|
||||
$display (" DM_System_Bus.fa_rg_sbdata_write: dm_addr 0x%08h dm_word 0x%08h",
|
||||
dm_addr, dm_word);
|
||||
|
||||
if (dm_addr == dm_addr_sbdata0)
|
||||
rg_sbdata0 <= dm_word;
|
||||
/* FUTURE: when supporting DM_SBACCESS_64_BIT
|
||||
else if (dm_addr == dm_addr_sbdata1)
|
||||
rg_sbdata1 <= dm_word;
|
||||
*/
|
||||
|
||||
// Initiate system bus write if writing to sbdata0
|
||||
if (dm_addr == dm_addr_sbdata0) begin
|
||||
fa_fabric_send_write_req (zeroExtend (dm_word));
|
||||
|
||||
// Increment sbaddr ifneeded
|
||||
if (rg_sbcs_sbautoincrement)
|
||||
fa_sbaddress_incr (sbaddress);
|
||||
end
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// Consume write-responses
|
||||
|
||||
rule rl_sb_write_response;
|
||||
let wrr <- pop_o (master_xactor.o_wr_resp);
|
||||
if (wrr.bresp != axi4_resp_okay)
|
||||
rg_sbcs_sberror <= DM_SBERROR_OTHER;
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
method Action reset;
|
||||
// master_xactor.reset; // TODO: introduces a scheduling cycle: fix this
|
||||
|
||||
rg_sb_state <= SB_NOTBUSY;
|
||||
|
||||
rg_sbcs_sbbusyerror <= False;
|
||||
rg_sbcs_sbreadonaddr <= False;
|
||||
rg_sbcs_sbaccess <= DM_SBACCESS_32_BIT;
|
||||
rg_sbcs_sbautoincrement <= False;
|
||||
rg_sbcs_sbreadondata <= False;
|
||||
rg_sbcs_sberror <= DM_SBERROR_NONE;
|
||||
|
||||
rg_sbaddress0 <= 0;
|
||||
rg_sbaddress1 <= 0;
|
||||
rg_sbdata0 <= 0;
|
||||
|
||||
if (verbosity != 0)
|
||||
$display ("DM_System_Bus: reset");
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// DMI facing GDB/host
|
||||
|
||||
// The predicate on read allows communication flow control to
|
||||
// throttle requests. This achieves better performance, but is not
|
||||
// workable for a true JTAG transport.
|
||||
method ActionValue #(DM_Word) av_read (DM_Addr dm_addr) if (!sbbusy);
|
||||
actionvalue
|
||||
DM_Word dm_word = 0;
|
||||
|
||||
if (dm_addr == dm_addr_sbcs) begin
|
||||
dm_word = virt_rg_sbcs;
|
||||
if (verbosity != 0)
|
||||
$display ("DM_System_Bus.read: [sbcs] => ", fshow_sbcs (dm_word));
|
||||
end
|
||||
|
||||
else if (dm_addr == dm_addr_sbaddress0) begin
|
||||
dm_word = rg_sbaddress0;
|
||||
if (verbosity != 0)
|
||||
$display ("DM_System_Bus.read: [sbaddress0] => 0x%08h", dm_word);
|
||||
end
|
||||
|
||||
else if (dm_addr == dm_addr_sbaddress1) begin
|
||||
dm_word = rg_sbaddress1;
|
||||
if (verbosity != 0)
|
||||
$display ("DM_System_Bus.read: [sbaddress1] => 0x%08h", dm_word);
|
||||
end
|
||||
|
||||
else if (dm_addr == dm_addr_sbdata0) begin
|
||||
dm_word <- fav_rg_sbdata_read (dm_addr_sbdata0);
|
||||
end
|
||||
|
||||
else begin
|
||||
// Unsupported dm address
|
||||
dm_word = 0;
|
||||
$display ("DM_System_Bus.read: [", fshow_dm_addr (dm_addr), "] not supported");
|
||||
end
|
||||
return dm_word;
|
||||
endactionvalue
|
||||
endmethod
|
||||
|
||||
method Action write (DM_Addr dm_addr, DM_Word dm_word);
|
||||
action
|
||||
if (dm_addr == dm_addr_sbcs)
|
||||
fa_rg_sbcs_write (dm_word);
|
||||
|
||||
else if ((dm_addr == dm_addr_sbaddress0) || (dm_addr == dm_addr_sbaddress1))
|
||||
fa_rg_sbaddress_write (dm_addr, dm_word);
|
||||
|
||||
else if (dm_addr == dm_addr_sbdata0) // FUTURE: || (dm_addr == dm_addr_sbdata1)
|
||||
fa_rg_sbdata_write (dm_addr, dm_word);
|
||||
|
||||
else begin
|
||||
// Unsupported dm_addr
|
||||
let addr_name = fshow_dm_addr (dm_addr);
|
||||
$display ("DM_System_Bus.write: [", addr_name, "] <= 0x%08h; addr not supported", dm_word);
|
||||
end
|
||||
endaction
|
||||
endmethod
|
||||
|
||||
// ----------------
|
||||
// Facing System
|
||||
interface AXI4_Master_IFC master = master_xactor.axi_side;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
287
src_Core/Debug_Module/Debug_Module.bsv
Normal file
287
src_Core/Debug_Module/Debug_Module.bsv
Normal file
@@ -0,0 +1,287 @@
|
||||
// Copyright (c) 2017-2019 Bluespec, Inc. All Rights Reserved.
|
||||
|
||||
package Debug_Module;
|
||||
|
||||
// ================================================================
|
||||
// This is the top-level package of a collection that implements the
|
||||
// "Debug Module" specified in:
|
||||
//
|
||||
// "RISC-V External Debug Support"
|
||||
// Version 0.13
|
||||
// 7c760b0151e43523ab3d2180e7852cd6f27d942c
|
||||
// Mon Jul 3 17:04:59 2017 -0700
|
||||
|
||||
// Note: the spec actually represents three (almost) completely
|
||||
// independent functionalities:
|
||||
// Run Control: to start/stop harts, query their start/stop status, etc.
|
||||
// Abstract Commands: to read/write RISC-V registers and RISC-V CSRs
|
||||
// System Bus Access: to read/write RISC-V memory/devices
|
||||
|
||||
// The only exception to complete independence is that the Run Control
|
||||
// part has a 'reset' for the Debug Module itself, which includes all
|
||||
// three parts.
|
||||
|
||||
// Unfortunately the spec is not written to make this three-part
|
||||
// organization clear--aspects of the three parts are completely
|
||||
// intermingled. Even the address map mixes registers from the three
|
||||
// parts.
|
||||
|
||||
// Here, this top-level package is merely a wrapper that dispatches
|
||||
// DMI requests to the three packages that implement the three parts:
|
||||
// DM_Run_Control
|
||||
// DM_Abstract_Commands
|
||||
// DM_System_Bus
|
||||
|
||||
// DM_Common contains common spec-level (implementation-independent) definitions.
|
||||
|
||||
// ================================================================
|
||||
// Our Debug Module (DM) has a two sides:
|
||||
// - GDB/Host-facing
|
||||
// - CPU/Platform-facing
|
||||
|
||||
// The GDB/Host-facing side is called DMI (Debug Module Interface) in
|
||||
// the spec, and is a simple memory-like read/write interface, into a
|
||||
// debug module address space (unrelated to the RISC-V memory address
|
||||
// space).
|
||||
|
||||
// The CPU/Platform-facing side has request/response interfaces for
|
||||
// CPU run-control, CPU register/CSR access and RISC-V system memory
|
||||
// access.
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import Memory :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import SpecialFIFOs :: *;
|
||||
|
||||
// ----------------
|
||||
// Other library imports
|
||||
|
||||
import Semi_FIFOF :: *;
|
||||
import Cur_Cycle :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import ISA_Decls :: *;
|
||||
import AXI4_Types :: *;
|
||||
import Fabric_Defs :: *;
|
||||
|
||||
import DM_Common :: *;
|
||||
import DM_Run_Control :: *;
|
||||
import DM_Abstract_Commands :: *;
|
||||
import DM_System_Bus :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
export DM_Common :: *;
|
||||
export Debug_Module_IFC (..);
|
||||
export mkDebug_Module;
|
||||
|
||||
// ================================================================
|
||||
// Interface to the Debug Module
|
||||
|
||||
interface Debug_Module_IFC;
|
||||
// ----------------
|
||||
// DMI (Debug Module Interface) facing remote debugger
|
||||
|
||||
interface DMI dmi;
|
||||
|
||||
// ----------------
|
||||
// Facing CPU
|
||||
// This section replicated for additional harts.
|
||||
|
||||
// Reset and run-control
|
||||
interface Get #(Token) hart0_get_reset_req;
|
||||
interface Client #(Bool, Bool) hart0_client_run_halt;
|
||||
interface Get #(Bit #(4)) hart0_get_other_req;
|
||||
|
||||
// GPR access
|
||||
interface MemoryClient #(5, XLEN) hart0_gpr_mem_client;
|
||||
|
||||
// CSR access
|
||||
interface MemoryClient #(12, XLEN) hart0_csr_mem_client;
|
||||
|
||||
// ----------------
|
||||
// Facing Platform
|
||||
|
||||
// Non-Debug-Module Reset (reset all except DM)
|
||||
interface Get #(Token) get_ndm_reset_req;
|
||||
|
||||
// Read/Write RISC-V memory
|
||||
interface AXI4_Master_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkDebug_Module (Debug_Module_IFC);
|
||||
|
||||
// The three parts
|
||||
DM_Run_Control_IFC dm_run_control <- mkDM_Run_Control;
|
||||
DM_Abstract_Commands_IFC dm_abstract_commands <- mkDM_Abstract_Commands;
|
||||
DM_System_Bus_IFC dm_system_bus <- mkDM_System_Bus;
|
||||
|
||||
FIFOF#(DM_Addr) f_read_addr <- mkBypassFIFOF;
|
||||
|
||||
// ================================================================
|
||||
// Reset all three parts when dm_run_control.dmactive is low
|
||||
|
||||
rule rl_reset (! dm_run_control.dmactive);
|
||||
$display ("%0d: Debug_Module reset", cur_cycle);
|
||||
dm_run_control.reset;
|
||||
dm_abstract_commands.reset;
|
||||
dm_system_bus.reset;
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// ----------------
|
||||
// Facing GDB/DMI (Debug Module Interface)
|
||||
|
||||
interface DMI dmi;
|
||||
method Action read_addr (DM_Addr dm_addr);
|
||||
f_read_addr.enq(dm_addr);
|
||||
endmethod
|
||||
|
||||
method ActionValue #(DM_Word) read_data;
|
||||
let dm_addr = f_read_addr.first;
|
||||
f_read_addr.deq;
|
||||
|
||||
DM_Word dm_word = ?;
|
||||
|
||||
if ( (dm_addr == dm_addr_dmcontrol)
|
||||
|| (dm_addr == dm_addr_dmstatus)
|
||||
|| (dm_addr == dm_addr_hartinfo)
|
||||
|| (dm_addr == dm_addr_haltsum)
|
||||
|| (dm_addr == dm_addr_hawindowsel)
|
||||
|| (dm_addr == dm_addr_hawindow)
|
||||
|| (dm_addr == dm_addr_devtreeaddr0)
|
||||
|| (dm_addr == dm_addr_authdata)
|
||||
|| (dm_addr == dm_addr_haltregion0)
|
||||
|| (dm_addr == dm_addr_haltregion31)
|
||||
|| (dm_addr == dm_addr_verbosity))
|
||||
|
||||
dm_word <- dm_run_control.av_read (dm_addr);
|
||||
|
||||
else if ( (dm_addr == dm_addr_abstractcs)
|
||||
|| (dm_addr == dm_addr_command)
|
||||
|| (dm_addr == dm_addr_data0)
|
||||
|| (dm_addr == dm_addr_data1)
|
||||
|| (dm_addr == dm_addr_data2)
|
||||
|| (dm_addr == dm_addr_data3)
|
||||
|| (dm_addr == dm_addr_data4)
|
||||
|| (dm_addr == dm_addr_data5)
|
||||
|| (dm_addr == dm_addr_data6)
|
||||
|| (dm_addr == dm_addr_data7)
|
||||
|| (dm_addr == dm_addr_data8)
|
||||
|| (dm_addr == dm_addr_data9)
|
||||
|| (dm_addr == dm_addr_data10)
|
||||
|| (dm_addr == dm_addr_data11)
|
||||
|| (dm_addr == dm_addr_abstractauto)
|
||||
|| (dm_addr == dm_addr_progbuf0))
|
||||
|
||||
dm_word <- dm_abstract_commands.av_read (dm_addr);
|
||||
|
||||
else if ( (dm_addr == dm_addr_sbcs)
|
||||
|| (dm_addr == dm_addr_sbaddress0)
|
||||
|| (dm_addr == dm_addr_sbaddress1)
|
||||
|| (dm_addr == dm_addr_sbaddress2)
|
||||
|| (dm_addr == dm_addr_sbdata0)
|
||||
|| (dm_addr == dm_addr_sbdata1)
|
||||
|| (dm_addr == dm_addr_sbdata2)
|
||||
|| (dm_addr == dm_addr_sbdata3))
|
||||
|
||||
dm_word <- dm_system_bus.av_read (dm_addr);
|
||||
|
||||
else begin
|
||||
// TODO: set error status?
|
||||
dm_word = 0;
|
||||
end
|
||||
|
||||
return dm_word;
|
||||
endmethod
|
||||
|
||||
method Action write (DM_Addr dm_addr, DM_Word dm_word);
|
||||
if ( (dm_addr == dm_addr_dmcontrol)
|
||||
|| (dm_addr == dm_addr_dmstatus)
|
||||
|| (dm_addr == dm_addr_hartinfo)
|
||||
|| (dm_addr == dm_addr_haltsum)
|
||||
|| (dm_addr == dm_addr_hawindowsel)
|
||||
|| (dm_addr == dm_addr_hawindow)
|
||||
|| (dm_addr == dm_addr_devtreeaddr0)
|
||||
|| (dm_addr == dm_addr_authdata)
|
||||
|| (dm_addr == dm_addr_haltregion0)
|
||||
|| (dm_addr == dm_addr_haltregion31)
|
||||
|| (dm_addr == dm_addr_verbosity))
|
||||
|
||||
dm_run_control.write (dm_addr, dm_word);
|
||||
|
||||
else if ( (dm_addr == dm_addr_abstractcs)
|
||||
|| (dm_addr == dm_addr_command)
|
||||
|| (dm_addr == dm_addr_data0)
|
||||
|| (dm_addr == dm_addr_data1)
|
||||
|| (dm_addr == dm_addr_data2)
|
||||
|| (dm_addr == dm_addr_data3)
|
||||
|| (dm_addr == dm_addr_data4)
|
||||
|| (dm_addr == dm_addr_data5)
|
||||
|| (dm_addr == dm_addr_data6)
|
||||
|| (dm_addr == dm_addr_data7)
|
||||
|| (dm_addr == dm_addr_data8)
|
||||
|| (dm_addr == dm_addr_data9)
|
||||
|| (dm_addr == dm_addr_data10)
|
||||
|| (dm_addr == dm_addr_data11)
|
||||
|| (dm_addr == dm_addr_abstractauto)
|
||||
|| (dm_addr == dm_addr_progbuf0))
|
||||
|
||||
dm_abstract_commands.write (dm_addr, dm_word);
|
||||
|
||||
else if ( (dm_addr == dm_addr_sbcs)
|
||||
|| (dm_addr == dm_addr_sbaddress0)
|
||||
|| (dm_addr == dm_addr_sbaddress1)
|
||||
|| (dm_addr == dm_addr_sbaddress2)
|
||||
|| (dm_addr == dm_addr_sbdata0)
|
||||
|| (dm_addr == dm_addr_sbdata1)
|
||||
|| (dm_addr == dm_addr_sbdata2)
|
||||
|| (dm_addr == dm_addr_sbdata3))
|
||||
|
||||
dm_system_bus.write (dm_addr, dm_word);
|
||||
|
||||
else begin
|
||||
// TODO: set error status?
|
||||
noAction;
|
||||
end
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
// ----------------
|
||||
// Facing CPU/hart0
|
||||
|
||||
// Reset and run-control
|
||||
interface Get hart0_get_reset_req = dm_run_control.hart0_get_reset_req;
|
||||
interface Client hart0_client_run_halt = dm_run_control.hart0_client_run_halt;
|
||||
interface Get hart0_get_other_req = dm_run_control.hart0_get_other_req;
|
||||
|
||||
// GPR access
|
||||
interface MemoryClient hart0_gpr_mem_client = dm_abstract_commands.hart0_gpr_mem_client;
|
||||
|
||||
// CSR access
|
||||
interface MemoryClient hart0_csr_mem_client = dm_abstract_commands.hart0_csr_mem_client;
|
||||
|
||||
// ----------------
|
||||
// Facing Platform
|
||||
|
||||
// Non-Debug-Module Reset (reset all except DM)
|
||||
interface Get get_ndm_reset_req = dm_run_control.get_ndm_reset_req;
|
||||
|
||||
// Read/Write RISC-V memory
|
||||
interface AXI4_Master_IFC master = dm_system_bus.master;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
162
src_Core/Debug_Module/README.txt
Normal file
162
src_Core/Debug_Module/README.txt
Normal file
@@ -0,0 +1,162 @@
|
||||
'Debug_Module' implements a Debug Module for RISC-V processors in
|
||||
accordance with the RISC-V standard "External Debug Support" spec:
|
||||
|
||||
RISC-V External Debug Support
|
||||
Version 0.13-DRAFT
|
||||
dd8d8714184970031fa447a452068223f257b51c
|
||||
Mon Dec 18 13:54:14 2017 -0800
|
||||
|
||||
Note: the spec is independent of any particular RISC-V CPU
|
||||
implementation. It just specifies the standard registers in the Debug
|
||||
Module that can be read and written by an external debugger (such as
|
||||
GDB). It specifies the address map of these registers, and the
|
||||
semantics, i.e., what happens when one reads or writes these
|
||||
registers. The spec does not say anything about how this spec is
|
||||
implemented.
|
||||
|
||||
Please see comments in Debug_Module.bsv for more details on our
|
||||
implementation of the Debug Module spec. This implementation is also
|
||||
not specific to any particular CPU implementation. We use it in
|
||||
Bluespec RISC-V CPUs, but it could be used with other CPUs as well.
|
||||
We use this Debug Module in the Bluespec RISC-V Verification Factory
|
||||
(BRVF).
|
||||
|
||||
// ================================================================
|
||||
What follows is a concise cheat-sheet on the registers in the Debug
|
||||
Module based on the spec.
|
||||
|
||||
// ----------------
|
||||
// Run Control
|
||||
|
||||
DM_Addr dm_addr_dmcontrol = 'h10;
|
||||
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
|
||||
| | | | |------------10--------------| | |dmactive
|
||||
| | | | |hartsel |ndmreset
|
||||
| | | |hasel
|
||||
| | | 0: Single hart selected (hartsel)
|
||||
| | | 1: Multiple harts selected (hartsel + hart array mask)
|
||||
| | |hartreset
|
||||
| |resumereq
|
||||
|haltreq
|
||||
|
||||
DM_Addr dm_addr_dmstatus = 'h11;
|
||||
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
|
||||
| | | | | | | | | | | | | | | | | | | | | | | | |--4--|version
|
||||
| | | | | | | | | | | | | | | | | | | | | | | | | 0: no DM present
|
||||
| | | | | | | | | | | | | | | | | | | | | | | | | 1: DM v011
|
||||
| | | | | | | | | | | | | | | | | | | | | | | | | 2: DM v013
|
||||
| | | | | | | | | | | | | | | | | | | | | | | | | 15: DM vUnknown
|
||||
| | | | | | | | | | | | | | | | | | | | | | | |devtreevalid
|
||||
| | | | | | | | | | | | | | | | | | | | | | |0
|
||||
| | | | | | | | | | | | | | | | | | | | | |authbusy
|
||||
| | | | | | | | | | | | | | | | | | | | |authenticated
|
||||
| | | | | | | | | | | | | | | | | | | |anyhalted
|
||||
| | | | | | | | | | | | | | | | | | |allhalted
|
||||
| | | | | | | | | | | | | | | | | |anyrunning
|
||||
| | | | | | | | | | | | | | | | |allrunning
|
||||
| | | | | | | | | | | | | | | |anyunavail
|
||||
| | | | | | | | | | | | | | |allunavail
|
||||
| | | | | | | | | | | | | |anynonexistent
|
||||
| | | | | | | | | | | | |allnonexistent
|
||||
| | | | | | | | | | | |anyresumeack
|
||||
| | | | | | | | | | |allresumeack
|
||||
| | | | | | | | | |anyhavereset
|
||||
| | | | | | | | |allhavereset
|
||||
| | | | | | |--|0
|
||||
| | | | | |impebreak
|
||||
| | | | | 0 No implicit EBREAK at end of PB
|
||||
| | | | | 1 Implicit EBREAK at end of PB
|
||||
| | | | |0
|
||||
| | |--3--|dmerr
|
||||
| | 0 No error
|
||||
| | 1 bad addr
|
||||
| | 7 other error
|
||||
|----- 5-----|0
|
||||
|
||||
DM_Addr dm_addr_hartinfo = 'h12;
|
||||
DM_Addr dm_addr_haltsum = 'h13;
|
||||
DM_Addr dm_addr_hawindowsel = 'h14;
|
||||
DM_Addr dm_addr_hawindow = 'h15;
|
||||
DM_Addr dm_addr_devtreeaddr0 = 'h19;
|
||||
DM_Addr dm_addr_authdata = 'h30;
|
||||
DM_Addr dm_addr_haltregion0 = 'h40;
|
||||
DM_Addr dm_addr_haltregion31 = 'h5F;
|
||||
|
||||
// ----------------
|
||||
// Abstract commands (read/write RISC-V registers and RISC-V CSRs)
|
||||
|
||||
DM_Addr dm_addr_abstractcs = 'h16;
|
||||
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
|
||||
|-----5------|progsize |busy |-3-|cmderr |----5---|datacount
|
||||
|
||||
DM_Addr dm_addr_command = 'h17;
|
||||
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
|
||||
| | | | | |-----------------16------------------|regno
|
||||
| | | | | | 0x0000-0x0FFF CSRs (dpc => PC)
|
||||
| | | | | | 0x1000-0x101F GPRs
|
||||
| | | | | | 0x1020-0x103F Floating Point Regs
|
||||
| | | | | | 0xC000-0xFFFF Reserved
|
||||
| | | | |write
|
||||
| | | | 0: specified reg -> arg0 of data
|
||||
| | | | 1: specified reg <- arg0 of data
|
||||
| | | |transfer
|
||||
| | | 0 Don't do the 'write' op
|
||||
| | | 1 Do the 'write' op
|
||||
| | | Allows exec of PB without valid vals in 'size' and 'regno'
|
||||
| | |postexec
|
||||
| | 1 exec Program Buffer exactly once after the xfer
|
||||
| |--3--|size
|
||||
| 2 Lowest 32b of reg
|
||||
| 3 Lowest 64b of reg
|
||||
| 4 Lowest 128b of reg
|
||||
|---------8-----------|cmdtype
|
||||
0 ACCESS_REG
|
||||
1 QUICK_ACCESS
|
||||
|
||||
DM_Addr dm_addr_data0 = 'h04;
|
||||
DM_Addr dm_addr_data1 = 'h05;
|
||||
DM_Addr dm_addr_data2 = 'h06;
|
||||
DM_Addr dm_addr_data3 = 'h07;
|
||||
DM_Addr dm_addr_data4 = 'h08;
|
||||
DM_Addr dm_addr_data5 = 'h09;
|
||||
DM_Addr dm_addr_data6 = 'h0a;
|
||||
DM_Addr dm_addr_data7 = 'h0b;
|
||||
DM_Addr dm_addr_data8 = 'h0c;
|
||||
DM_Addr dm_addr_data9 = 'h0d;
|
||||
DM_Addr dm_addr_data10 = 'h0d;
|
||||
DM_Addr dm_addr_data11 = 'h0f;
|
||||
|
||||
DM_Addr dm_addr_abstractauto = 'h18;
|
||||
DM_Addr dm_addr_progbuf0 = 'h20;
|
||||
|
||||
// ----------------
|
||||
// System Bus access (read/write RISC-V memory/devices)
|
||||
|
||||
DM_Addr dm_addr_sbcs = 'h38;
|
||||
31.30.29.28| 27.26.25.24| 23.22.21.20| 19.18.17.16| 15.14.13.12| 11.10.9.8| 7.6.5.4| 3.2.1.0|
|
||||
| | | | | | | | | | | |sbaccess8
|
||||
| | | | | | | | | | |sbaccess16
|
||||
| | | | | | | | | |sbaccess32
|
||||
| | | | | | | | |sbaccess64
|
||||
| | | | | | | |sbaccess128
|
||||
| | | | | | |-----7-------|sbasize
|
||||
| | | | |--3--|sberror
|
||||
| | | | | 0: no bus err
|
||||
| | | | | 1: timeout
|
||||
| | | | | 2: bad addr
|
||||
| | | | | 3: other err (e.g., alignment)
|
||||
| | | | | 4: busy
|
||||
| | | |sbautoread
|
||||
| | |sbautoincrement
|
||||
| |--3--|sbaccess
|
||||
| | 0:8b, 1:16b, 2:32b, 3:64b, 4:128b
|
||||
|singleread
|
||||
1 triggers single read at sbaddress of size sbaccess
|
||||
|
||||
DM_Addr dm_addr_sbaddress0 = 'h39;
|
||||
DM_Addr dm_addr_sbaddress1 = 'h3a;
|
||||
DM_Addr dm_addr_sbaddress2 = 'h3b;
|
||||
DM_Addr dm_addr_sbdata0 = 'h3c;
|
||||
DM_Addr dm_addr_sbdata1 = 'h3d;
|
||||
DM_Addr dm_addr_sbdata2 = 'h3e;
|
||||
DM_Addr dm_addr_sbdata3 = 'h3f;
|
||||
94
src_Core/Debug_Module/Test/Makefile
Normal file
94
src_Core/Debug_Module/Test/Makefile
Normal file
@@ -0,0 +1,94 @@
|
||||
default: compile link
|
||||
all: compile link simulate
|
||||
|
||||
TOP = Testbench
|
||||
|
||||
TOPFILE = $(TOP).bsv
|
||||
TOPMODULE = mk$(TOP)
|
||||
|
||||
# BSCFLAGS = -keep-fires -aggressive-conditions -no-warn-action-shadowing -no-inline-rwire
|
||||
# BSCFLAGS = -keep-fires -aggressive-conditions -no-inline-rwire -show-range-conflict -show-schedule
|
||||
BSCFLAGS = -D RV32 \
|
||||
-keep-fires \
|
||||
-aggressive-conditions \
|
||||
-suppress-warnings G0020 \
|
||||
-show-schedule
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# FOR BLUESIM
|
||||
|
||||
ISA_DECLS_DIR = $(HOME)/Projects/RISCV/Bluespec_RISCV/ISA
|
||||
TRX_DIR = $(HOME)/Projects/RISCV/Bluespec_RISCV/Fabrics/TRX
|
||||
ADDL_LIBS_DIR = $(HOME)/Projects/RISCV/Bluespec_RISCV/BSV_Additional_Libs/BSV
|
||||
|
||||
BSCDIRS_BSIM = -simdir build_bsim -bdir build -info-dir build
|
||||
BSCPATH_BSIM = -p .:..:$(ISA_DECLS_DIR):$(TRX_DIR):$(ADDL_LIBS_DIR):%/Prelude:%/Libraries
|
||||
|
||||
build_bsim:
|
||||
mkdir -p $@
|
||||
|
||||
build:
|
||||
mkdir -p $@
|
||||
|
||||
.PHONY: compile
|
||||
compile: build_bsim build
|
||||
@echo Compiling...
|
||||
bsc -u -sim $(BSCDIRS_BSIM) $(BSCFLAGS) $(BSCPATH_BSIM) $(TOPFILE)
|
||||
@echo Compilation finished
|
||||
|
||||
.PHONY: link
|
||||
link:
|
||||
@echo Linking...
|
||||
bsc -e $(TOPMODULE) $(BSCFLAGS) -parallel-sim-link 8 -sim -o ./$(TOP)_bsim_exe $(BSCDIRS_BSIM) $(BSCPATH_BSIM)
|
||||
@echo Linking finished
|
||||
|
||||
.PHONY: simulate
|
||||
simulate:
|
||||
@echo Simulation...
|
||||
logsave bsim.log ./$(TOP)_bsim_exe -V
|
||||
@echo Simulation finished
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
# FOR VERILOG
|
||||
|
||||
BSCDIRS_V = -vdir verilog -bdir build_v -info-dir build_v
|
||||
BSCPATH_V = -p .:./$(SRC_BSV):%/Prelude:%/Libraries:%/Libraries/TLM3
|
||||
|
||||
# Set VSIM to desired Verilog simulator
|
||||
# VSIM = modelsim
|
||||
VSIM ?= cvc
|
||||
# VSIM ?= iverilog
|
||||
|
||||
build_v:
|
||||
mkdir -p $@
|
||||
|
||||
verilog:
|
||||
mkdir -p $@
|
||||
|
||||
.PHONY: rtl
|
||||
rtl: build_v verilog
|
||||
@echo Verilog generation ...
|
||||
bsc -u -elab -verilog $(BSCDIRS_V) $(BSCFLAGS) $(BSCPATH_V) $(TOPFILE)
|
||||
@echo Verilog generation finished
|
||||
|
||||
.PHONY: vlink
|
||||
vlink:
|
||||
bsc -v -e $(TOPMODULE) -verilog -o ./out_v -vdir verilog -vsim $(VSIM) -keep-fires \
|
||||
verilog/$(TOPMODULE).v
|
||||
|
||||
.PHONY: vsim
|
||||
vsim:
|
||||
@echo Simulation...
|
||||
./out_v
|
||||
@echo Simulation finished
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f *~ src_*/*~ src_*/*.o build/* build_bsim/* build_v/* *.cxx *.h *.o
|
||||
|
||||
.PHONY: full_clean
|
||||
full_clean: clean
|
||||
rm -r -f *_bsim_exe *.so out_v verilog build build_bsim dump.vcd bsim.log
|
||||
597
src_Core/Debug_Module/Test/Testbench.bsv
Normal file
597
src_Core/Debug_Module/Test/Testbench.bsv
Normal file
@@ -0,0 +1,597 @@
|
||||
package Testbench;
|
||||
|
||||
// ================================================================
|
||||
// Testbench for basic sanity-check testing of the Debug Module.
|
||||
|
||||
|
||||
// ================================================================
|
||||
// BSV library imports
|
||||
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
import StmtFSM :: *;
|
||||
|
||||
// ----------------
|
||||
// Other library imports
|
||||
|
||||
import Semi_FIFOF :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
import ISA_Decls :: *;
|
||||
import TRX :: *;
|
||||
|
||||
import Debug_Module :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
Integer csr_addr_dcsr = 'h7b0;
|
||||
Integer csr_addr_dpc = 'h7b1;
|
||||
Integer csr_addr_dscratch0 = 'h7b2;
|
||||
Integer csr_addr_dscratch1 = 'h7b3;
|
||||
|
||||
// ================================================================
|
||||
|
||||
(* synthesize *)
|
||||
module mkTestbench (Empty);
|
||||
|
||||
// ================================================================
|
||||
// Cycle-counter and cycle-limit termination
|
||||
|
||||
Reg #(Bit #(32)) rg_cycle <- mkReg (0);
|
||||
|
||||
Integer cycle_limit = 100;
|
||||
|
||||
rule rl_count_cycles;
|
||||
if (rg_cycle == fromInteger (100)) begin
|
||||
$display ("Testench: stopping at cycle %0d", cycle_limit);
|
||||
$finish (0);
|
||||
end
|
||||
rg_cycle <= rg_cycle +1;
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// The Debug Module
|
||||
|
||||
Debug_Module_IFC dm <- mkDebug_Module;
|
||||
|
||||
// ================================================================
|
||||
// Model of a hart, and connections to dm
|
||||
|
||||
Hart_DM_IFC hart0 <- mkHart_Model (0);
|
||||
|
||||
// Reset
|
||||
mkConnection (dm.hart0_reset_req, hart0.hart_reset_req);
|
||||
|
||||
// Run-control
|
||||
mkConnection (dm.hart0_run_req_rsp, hart0.hart_run_req_rsp);
|
||||
|
||||
// GPR access
|
||||
mkConnection (dm.master_for_gprs, hart0.slave_for_gprs);
|
||||
|
||||
// CSR access
|
||||
mkConnection (dm.master_for_csrs, hart0.slave_for_csrs);
|
||||
|
||||
// ================================================================
|
||||
// Over-simplified model of platform reset (all except DM)
|
||||
|
||||
rule rl_ndm_reset;
|
||||
let x <- dm.ndm_reset_req.get;
|
||||
$display ("Testbench.rl_ndm_reset: Resetting all platform except Debug Module");
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Over-simplified model of system (RISC-V) memory
|
||||
// On reads, return addr + 2.
|
||||
|
||||
rule rl_mem_read;
|
||||
let rda <- pop_o (dm.master.fo_rda);
|
||||
let data = rda.addr + 2; // Bogus data, for now
|
||||
let rdr = TRX_RdR {trans_id: rda.trans_id,
|
||||
status : TRX_OKAY,
|
||||
data : data};
|
||||
dm.master.fi_rdr.enq (rdr);
|
||||
$display ("Testbench: memory read [0x%08h] => 0x%08h", rda.addr, data);
|
||||
endrule
|
||||
|
||||
rule rl_mem_write;
|
||||
let wra <- pop_o (dm.master.fo_wra);
|
||||
let wrd <- pop_o (dm.master.fo_wrd);
|
||||
let wrr = TRX_WrR {trans_id: wra.trans_id,
|
||||
status : TRX_OKAY};
|
||||
dm.master.fi_wrr.enq (wrr);
|
||||
$display ("Testbench: memory write [0x%08h] <= 0x%08h", wra.addr, wrd.data);
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Abstract command sequences (read/write GPR/CSR)
|
||||
|
||||
Reg #(Bit #(32)) rg_abstractcs <- mkRegU;
|
||||
|
||||
// Read a register
|
||||
function Stmt fn_stmt_read_reg (Bit #(16) regno);
|
||||
return
|
||||
seq
|
||||
$display ("----------------\nRead RISC-V reg");
|
||||
// Clear any prior error status
|
||||
dm.write (dm_addr_abstractcs, fn_mk_abstractcs (dm_cmderr_w1c));
|
||||
// Perform the read
|
||||
dm.write (dm_addr_command,
|
||||
fn_mk_command_access_reg (DM_COMMAND_ACCESS_REG_SIZE_LOWER32,
|
||||
False, // postexec
|
||||
True, // transfer
|
||||
False, // write
|
||||
regno));
|
||||
// Read status to check no error
|
||||
action
|
||||
let x <- dm.av_read (dm_addr_abstractcs);
|
||||
rg_abstractcs <= x;
|
||||
endaction
|
||||
while (fn_abstractcs_busy (rg_abstractcs)) seq
|
||||
$display ("Testbench: read reg: busy");
|
||||
action
|
||||
let x <- dm.av_read (dm_addr_abstractcs);
|
||||
rg_abstractcs <= x;
|
||||
endaction
|
||||
endseq
|
||||
if (fn_abstractcs_cmderr (rg_abstractcs) != DM_ABSTRACTCS_CMDERR_NONE)
|
||||
$display ("Testbench: read reg => ", fshow (fn_abstractcs_cmderr (rg_abstractcs)));
|
||||
else action
|
||||
let x <- dm.av_read (dm_addr_data0);
|
||||
$display ("Testbench: read reg => 0x%08h", x);
|
||||
endaction
|
||||
endseq;
|
||||
endfunction
|
||||
|
||||
// Write a register
|
||||
function Stmt fn_stmt_write_reg (Bit #(16) regno, Bit #(32) data);
|
||||
return
|
||||
seq
|
||||
$display ("----------------\nWrite RISC-V reg");
|
||||
// Clear any prior error status
|
||||
dm.write (dm_addr_abstractcs, fn_mk_abstractcs (dm_cmderr_w1c));
|
||||
// Write data0
|
||||
dm.write (dm_addr_data0, data);
|
||||
// Perform the write
|
||||
dm.write (dm_addr_command,
|
||||
fn_mk_command_access_reg (DM_COMMAND_ACCESS_REG_SIZE_LOWER32,
|
||||
False, // postexec
|
||||
True, // transfer
|
||||
True, // write
|
||||
regno));
|
||||
// Read status to check no error
|
||||
action
|
||||
let x <- dm.av_read (dm_addr_abstractcs);
|
||||
rg_abstractcs <= x;
|
||||
endaction
|
||||
while (fn_abstractcs_busy (rg_abstractcs)) seq
|
||||
$display ("Testbench: write reg: busy");
|
||||
action
|
||||
let x <- dm.av_read (dm_addr_abstractcs);
|
||||
rg_abstractcs <= x;
|
||||
endaction
|
||||
endseq
|
||||
$display ("Testbench: write reg => ", fshow (fn_abstractcs_cmderr (rg_abstractcs)));
|
||||
endseq;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// System Bus access sequences (read/write RISC-V memory)
|
||||
|
||||
Reg #(Bool) rg_busy <- mkRegU;
|
||||
|
||||
Reg #(Bit #(32)) rg_j <- mkRegU;
|
||||
Reg #(Bit #(32)) rg_addr <- mkRegU;
|
||||
Reg #(Bit #(32)) rg_data <- mkRegU;
|
||||
|
||||
Stmt stmt_wait_for_sb_nonbusy = (
|
||||
seq
|
||||
rg_busy <= True;
|
||||
while (rg_busy) seq
|
||||
delay (1);
|
||||
action
|
||||
let x <- dm.av_read (dm_addr_sbcs);
|
||||
let sberror = fn_sbcs_sberror (x);
|
||||
rg_busy <= (sberror == DM_SBERROR_BUSY_STALE);
|
||||
if ( (sberror != DM_SBERROR_NONE)
|
||||
&& (sberror != DM_SBERROR_BUSY_STALE))
|
||||
begin
|
||||
$display ("Testbench: stmt_wait_for_sb_nonbusy: ", fshow (sberror));
|
||||
$finish (1);
|
||||
end
|
||||
endaction
|
||||
endseq
|
||||
endseq);
|
||||
|
||||
// Do a single-read from memory
|
||||
Stmt stmt_mem_read_1 = (
|
||||
seq
|
||||
dm.write (dm_addr_sbaddress0, 'h1_0000);
|
||||
dm.write (dm_addr_sbcs, fn_mk_sbcs (True, // sbsingleread
|
||||
DM_SBACCESS_32_BIT,
|
||||
False, // sbautoincrement
|
||||
False, // sbautoread
|
||||
DM_SBERROR_UNDEF7_W1C)); // clear sberror
|
||||
stmt_wait_for_sb_nonbusy;
|
||||
action
|
||||
let x <- dm.av_read (dm_addr_sbdata0);
|
||||
$display ("stmt_mem_read_1: read-data = 0x%08h", x);
|
||||
endaction
|
||||
endseq);
|
||||
|
||||
// Do a multiple-read from memory
|
||||
Stmt stmt_mem_read_4 = (
|
||||
seq
|
||||
dm.write (dm_addr_sbaddress0, 'h1_0000);
|
||||
dm.write (dm_addr_sbcs, fn_mk_sbcs (True, // sbsingleread
|
||||
DM_SBACCESS_32_BIT,
|
||||
True, // sbautoincrement
|
||||
True, // sbautoread
|
||||
DM_SBERROR_UNDEF7_W1C)); // clear sberror
|
||||
for (rg_j <= 0; rg_j < 3; rg_j <= rg_j + 1) seq
|
||||
stmt_wait_for_sb_nonbusy;
|
||||
action
|
||||
let x <- dm.av_read (dm_addr_sbdata0);
|
||||
$display ("stmt_mem_read_4: read-data [%0d] = 0x%08h", rg_j, x);
|
||||
endaction
|
||||
endseq
|
||||
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
|
||||
DM_SBACCESS_32_BIT,
|
||||
False, // sbautoincrement
|
||||
False, // sbautoread
|
||||
DM_SBERROR_UNDEF7_W1C)); // clear sberror
|
||||
stmt_wait_for_sb_nonbusy;
|
||||
action
|
||||
let x <- dm.av_read (dm_addr_sbdata0);
|
||||
$display ("stmt_mem_read_4: read-data [%0d] = 0x%08h", rg_j, x);
|
||||
endaction
|
||||
endseq);
|
||||
|
||||
// Do a single-write to memory
|
||||
Stmt stmt_mem_write_1 = (
|
||||
seq
|
||||
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
|
||||
DM_SBACCESS_32_BIT,
|
||||
False, // sbautoincrement
|
||||
False, // sbautoread
|
||||
DM_SBERROR_UNDEF7_W1C)); // clear sberror
|
||||
stmt_wait_for_sb_nonbusy;
|
||||
dm.write (dm_addr_sbaddress0, 'h1_0000);
|
||||
dm.write (dm_addr_sbdata0, 'h_BEEF);
|
||||
endseq);
|
||||
|
||||
// Do a multiple-write to memory
|
||||
Stmt stmt_mem_write_4 = (
|
||||
seq
|
||||
dm.write (dm_addr_sbcs, fn_mk_sbcs (False, // sbsingleread
|
||||
DM_SBACCESS_32_BIT,
|
||||
True, // sbautoincrement
|
||||
False, // sbautoread
|
||||
DM_SBERROR_UNDEF7_W1C)); // clear sberror
|
||||
stmt_wait_for_sb_nonbusy;
|
||||
action
|
||||
rg_addr <= 'h_2000;
|
||||
rg_data <= 'h_DAFA_0000;
|
||||
endaction
|
||||
dm.write (dm_addr_sbaddress0, rg_addr);
|
||||
for (rg_j <= 0; rg_j < 4; rg_j <= rg_j + 1) seq
|
||||
stmt_wait_for_sb_nonbusy;
|
||||
action
|
||||
$display ("stmt_mem_write_4: [0x%08h] x = 0x%08h", rg_addr + rg_j, rg_data);
|
||||
dm.write (dm_addr_sbdata0, rg_data);
|
||||
rg_data <= rg_data + 1;
|
||||
endaction
|
||||
endseq
|
||||
endseq);
|
||||
|
||||
// ================================================================
|
||||
// Run-control test sequences (reset, run, halt, single-step)
|
||||
|
||||
let dmcontrol_dm_reset
|
||||
= fn_mk_dmcontrol (False, // haltreq
|
||||
False, // resumereq
|
||||
False, // hartreset
|
||||
False, // hasel
|
||||
0, // hartsel,
|
||||
False, // ndmreset
|
||||
False); // dmactive; assert reset
|
||||
|
||||
let dmcontrol_ndmreset
|
||||
= fn_mk_dmcontrol (False, // haltreq
|
||||
False, // resumereq
|
||||
False, // hartreset
|
||||
False, // hasel,
|
||||
0, // hartsel
|
||||
True, // ndmreset
|
||||
True); // dmactive
|
||||
|
||||
let dmcontrol_err_hasel
|
||||
= fn_mk_dmcontrol (False, // haltreq
|
||||
False, // resumereq
|
||||
False, // hartreset
|
||||
True, // hasel,
|
||||
0, // hartsel
|
||||
False, // ndmreset
|
||||
True); // dmactive
|
||||
|
||||
let dmcontrol_err_hartsel
|
||||
= fn_mk_dmcontrol (False, // haltreq
|
||||
False, // resumereq
|
||||
False, // hartreset
|
||||
False, // hasel,
|
||||
3, // hartsel
|
||||
False, // ndmreset
|
||||
True); // dmactive
|
||||
|
||||
let dmcontrol_hartreset
|
||||
= fn_mk_dmcontrol (False, // haltreq
|
||||
False, // resumereq
|
||||
True, // hartreset
|
||||
False, // hasel,
|
||||
0, // hartsel
|
||||
False, // ndmreset
|
||||
True); // dmactive
|
||||
|
||||
let dmcontrol_err_haltreq_resumereq
|
||||
= fn_mk_dmcontrol (True, // haltreq
|
||||
True, // resumereq
|
||||
False, // hartreset
|
||||
False, // hasel,
|
||||
0, // hartsel
|
||||
False, // ndmreset
|
||||
True); // dmactive
|
||||
|
||||
let dmcontrol_haltreq
|
||||
= fn_mk_dmcontrol (True, // haltreq
|
||||
False, // resumereq
|
||||
False, // hartreset
|
||||
False, // hasel,
|
||||
0, // hartsel
|
||||
False, // ndmreset
|
||||
True); // dmactive
|
||||
|
||||
let dmcontrol_resumereq
|
||||
= fn_mk_dmcontrol (False, // haltreq
|
||||
True, // resumereq
|
||||
False, // hartreset
|
||||
False, // hasel,
|
||||
0, // hartsel
|
||||
False, // ndmreset
|
||||
True); // dmactive
|
||||
|
||||
function Stmt fn_stmt_run_control (DM_Word dm_word);
|
||||
return seq
|
||||
dm.write (dm_addr_dmcontrol, dm_word);
|
||||
delay (5);
|
||||
// Check and show status
|
||||
action
|
||||
let x <- dm.av_read (dm_addr_dmstatus);
|
||||
$display (" ", fshow_dmstatus (x));
|
||||
endaction
|
||||
endseq;
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// For single-step, set 'step' bit in DCSR, then run
|
||||
|
||||
let dcsr_step = {4'h4, // xdebugver
|
||||
12'b0,
|
||||
1'b0, // ebreakm
|
||||
1'b0,
|
||||
1'b0, // ebreaks
|
||||
1'b0, // ebreaku
|
||||
1'b0, // stepie
|
||||
1'b0, // stepcount
|
||||
1'b0, // steptime
|
||||
3'b0, // cause
|
||||
3'b0,
|
||||
1'b1, // step
|
||||
2'h3};
|
||||
|
||||
Stmt stmt_single_step = (
|
||||
seq
|
||||
// set 'step' in dcsr
|
||||
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_csr_0 + csr_addr_dcsr),
|
||||
dcsr_step); // priv
|
||||
fn_stmt_run_control (dmcontrol_resumereq);
|
||||
endseq);
|
||||
|
||||
// ================================================================
|
||||
// Top-level test. Comment/Uncomment desired parts.
|
||||
|
||||
Stmt test = seq
|
||||
// Reset DM
|
||||
$display ("----------------\n'Testbench: Reset DM'");
|
||||
fn_stmt_run_control (dmcontrol_dm_reset);
|
||||
|
||||
/*
|
||||
$display ("----------------\n'Testbench: Reset Platform'");
|
||||
fn_stmt_run_control (dmcontrol_ndmreset);
|
||||
|
||||
$display ("----------------\n'Testbench: Err hasel'");
|
||||
fn_stmt_run_control (dmcontrol_err_hasel);
|
||||
|
||||
$display ("----------------\n'Testbench: Err hartsel'");
|
||||
fn_stmt_run_control (dmcontrol_err_hartsel);
|
||||
|
||||
$display ("----------------\n'Testbench: Reset hart'");
|
||||
fn_stmt_run_control (dmcontrol_hartreset);
|
||||
|
||||
$display ("----------------\n'Testbench: Err haltreq and resumereq'");
|
||||
fn_stmt_run_control (dmcontrol_err_haltreq_resumereq);
|
||||
|
||||
$display ("----------------\n'Testbench: Continue'");
|
||||
fn_stmt_run_control (dmcontrol_resumereq);
|
||||
|
||||
$display ("----------------\n'Testbench: Halt'");
|
||||
fn_stmt_run_control (dmcontrol_haltreq);
|
||||
|
||||
$display ("----------------\n'Testbench: Single step'");
|
||||
stmt_single_step;
|
||||
*/
|
||||
|
||||
$display ("----------------\n'Testbench: Read GPR'");
|
||||
fn_stmt_read_reg (fromInteger (dm_command_access_reg_regno_gpr_0 + 5));
|
||||
$display ("----------------\n'Testbench: Read CSR'");
|
||||
fn_stmt_read_reg (fromInteger (dm_command_access_reg_regno_csr_0 + 3));
|
||||
|
||||
$display ("----------------\n'Testbench: Write GPR'");
|
||||
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_gpr_0 + 5), 'h_AAAA_0005);
|
||||
$display ("----------------\n'Testbench: Write CSR'");
|
||||
fn_stmt_write_reg (fromInteger (dm_command_access_reg_regno_csr_0 + 3), 'h_CCCC_0003);
|
||||
|
||||
/*
|
||||
$display ("----------------\n'Testbench: Read 1'");
|
||||
stmt_mem_read_1;
|
||||
|
||||
$display ("----------------\n'Testbench: Write 1'");
|
||||
stmt_mem_write_1;
|
||||
|
||||
$display ("----------------\n'Testbench: Read 4'");
|
||||
stmt_mem_read_4;
|
||||
|
||||
$display ("----------------\n'Testbench: Write 4'");
|
||||
stmt_mem_write_4;
|
||||
*/
|
||||
|
||||
await (False);
|
||||
endseq;
|
||||
|
||||
mkAutoFSM (test);
|
||||
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
// Over-simplified model of a hart (reset, run/halt, read/write GPR/CSR)
|
||||
|
||||
interface Hart_DM_IFC;
|
||||
// Reset
|
||||
interface Put #(Token) hart_reset_req;
|
||||
|
||||
// Run-control
|
||||
interface Server #(Bool, Bool) hart_run_req_rsp;
|
||||
|
||||
// GPR access
|
||||
interface TRX_Slave_IFC #(5,32,0) slave_for_gprs;
|
||||
|
||||
// CSR access
|
||||
interface TRX_Slave_IFC #(12,32,0) slave_for_csrs;
|
||||
endinterface
|
||||
|
||||
|
||||
(* synthesize *)
|
||||
module mkHart_Model #(parameter Bit #(10) hart_id) (Hart_DM_IFC);
|
||||
|
||||
Reg #(Bool) rg_hart_running <- mkReg (False);
|
||||
|
||||
FIFOF #(Token) f_hart_reset_reqs <- mkFIFOF;
|
||||
|
||||
FIFOF #(Bool) f_hart_run_reqs <- mkFIFOF;
|
||||
FIFOF #(Bool) f_hart_run_rsps <- mkFIFOF;
|
||||
|
||||
// TRX interface to gprs
|
||||
TRX_Buffer_IFC #(5,32,0) trx_buf_gprs <- mkTRX_Buffer;
|
||||
|
||||
// TRX interface to crs
|
||||
TRX_Buffer_IFC #(12,32,0) trx_buf_csrs <- mkTRX_Buffer;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// BEHAVIOR
|
||||
|
||||
// ----------------
|
||||
// Reset
|
||||
|
||||
rule rl_hart_reset;
|
||||
let x = f_hart_reset_reqs.first;
|
||||
f_hart_reset_reqs.deq;
|
||||
|
||||
$display ("Testbench.hart [%0d]: reset", hart_id);
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// Run-control
|
||||
|
||||
rule rl_resume_hart (f_hart_run_reqs.first);
|
||||
f_hart_run_reqs.deq;
|
||||
rg_hart_running <= True;
|
||||
|
||||
if (rg_hart_running)
|
||||
$display ("Testbench.hart [%0d].rl_resume_hart: already running", hart_id);
|
||||
else
|
||||
$display ("Testbench.hart [%0d].rl_resume_hart: resuming", hart_id);
|
||||
|
||||
f_hart_run_rsps.enq (True);
|
||||
endrule
|
||||
|
||||
rule rl_halt_hart (! f_hart_run_reqs.first);
|
||||
f_hart_run_reqs.deq;
|
||||
rg_hart_running <= False;
|
||||
|
||||
if (rg_hart_running)
|
||||
$display ("Testbench.hart [%0d].rl_halt_hart: halting", hart_id);
|
||||
else
|
||||
$display ("Testbench.hart [%0d].rl_halt_hart: already halted", hart_id);
|
||||
|
||||
f_hart_run_rsps.enq (False);
|
||||
endrule
|
||||
|
||||
// ----------------
|
||||
// GPR access
|
||||
|
||||
rule rl_read_gpr;
|
||||
let rda <- pop_o (trx_buf_gprs.master.fo_rda);
|
||||
Bit #(32) data = extend (rda.addr) + 'h1000;
|
||||
let rdr = TRX_RdR {trans_id: rda.trans_id,
|
||||
status: TRX_OKAY,
|
||||
data: data};
|
||||
trx_buf_gprs.master.fi_rdr.enq (rdr);
|
||||
$display ("Testbench.hart [%0d]: Read GPR [%0h] => 0x%08h", hart_id, rda.addr, data);
|
||||
endrule
|
||||
|
||||
rule rl_read_csr;
|
||||
let rda <- pop_o (trx_buf_csrs.master.fo_rda);
|
||||
Bit #(32) data = extend (rda.addr) + 'h2000;
|
||||
let rdr = TRX_RdR {trans_id: rda.trans_id,
|
||||
status: TRX_OKAY,
|
||||
data: data};
|
||||
trx_buf_csrs.master.fi_rdr.enq (rdr);
|
||||
$display ("Testbench.hart [%0d]: Read CSR [%0h] => 0x%08h", hart_id, rda.addr, data);
|
||||
endrule
|
||||
|
||||
rule rl_write_gpr;
|
||||
let wra <- pop_o (trx_buf_gprs.master.fo_wra);
|
||||
let wrd <- pop_o (trx_buf_gprs.master.fo_wrd);
|
||||
let wrr = TRX_WrR {trans_id: wra.trans_id, status: TRX_OKAY};
|
||||
trx_buf_gprs.master.fi_wrr.enq (wrr);
|
||||
$display ("Testbench.hart [%0d]: Write GPR [%0h] <= 0x%08h", hart_id, wra.addr, wrd.data);
|
||||
endrule
|
||||
|
||||
rule rl_write_csr;
|
||||
let wra <- pop_o (trx_buf_csrs.master.fo_wra);
|
||||
let wrd <- pop_o (trx_buf_csrs.master.fo_wrd);
|
||||
let wrr = TRX_WrR {trans_id: wra.trans_id, status: TRX_OKAY};
|
||||
trx_buf_csrs.master.fi_wrr.enq (wrr);
|
||||
$display ("Testbench.hart [%0d]: Write CSR [%0h] <= 0x%08h", hart_id, wra.addr, wrd.data);
|
||||
endrule
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// INTERFACE
|
||||
|
||||
// Reset
|
||||
interface Put hart_reset_req = toPut (f_hart_reset_reqs);
|
||||
|
||||
// Run-control
|
||||
interface Server hart_run_req_rsp = toGPServer (f_hart_run_reqs, f_hart_run_rsps);
|
||||
|
||||
// GPR access
|
||||
interface TRX_Slave_IFC slave_for_gprs = trx_buf_gprs.slave;
|
||||
|
||||
// CSR access
|
||||
interface TRX_Slave_IFC slave_for_csrs = trx_buf_csrs.slave;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
1125
src_Core/ISA/ISA_Decls.bsv
Normal file
1125
src_Core/ISA/ISA_Decls.bsv
Normal file
File diff suppressed because it is too large
Load Diff
179
src_Core/ISA/ISA_Decls_C.bsv
Normal file
179
src_Core/ISA/ISA_Decls_C.bsv
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
// ================================================================
|
||||
// This is an 'include' file, not a separate BSV package
|
||||
//
|
||||
// Contains RISC-V ISA defs for the 'C' ("compressed") extension
|
||||
// i.e., 16-bit instructions
|
||||
//
|
||||
// ================================================================
|
||||
// Instruction field encodings
|
||||
|
||||
typedef Bit #(16) Instr_C;
|
||||
|
||||
Bit #(2) opcode_C0 = 2'b00;
|
||||
Bit #(2) opcode_C1 = 2'b01;
|
||||
Bit #(2) opcode_C2 = 2'b10;
|
||||
|
||||
Bit #(3) funct3_C_LWSP = 3'b_010;
|
||||
Bit #(3) funct3_C_LDSP = 3'b_011; // RV64 and RV128
|
||||
Bit #(3) funct3_C_LQSP = 3'b_001; // RV128
|
||||
Bit #(3) funct3_C_FLWSP = 3'b_011; // RV32FC
|
||||
Bit #(3) funct3_C_FLDSP = 3'b_001; // RV32DC, RV64DC
|
||||
|
||||
Bit #(3) funct3_C_SWSP = 3'b_110;
|
||||
|
||||
Bit #(3) funct3_C_SQSP = 3'b_101; // RV128
|
||||
Bit #(3) funct3_C_FSDSP = 3'b_101; // RV32DC, RV64DC
|
||||
|
||||
Bit #(3) funct3_C_SDSP = 3'b_111; // RV64 and RV128
|
||||
Bit #(3) funct3_C_FSWSP = 3'b_111; // RV32FC
|
||||
|
||||
Bit #(3) funct3_C_LQ = 3'b_001; // RV128
|
||||
Bit #(3) funct3_C_FLD = 3'b_001; // RV32DC, RV64DC
|
||||
|
||||
Bit #(3) funct3_C_LW = 3'b_010;
|
||||
|
||||
Bit #(3) funct3_C_LD = 3'b_011; // RV64 and RV128
|
||||
Bit #(3) funct3_C_FLW = 3'b_011; // RV32FC
|
||||
|
||||
Bit #(3) funct3_C_FSD = 3'b_101; // RV32DC, RV64DC
|
||||
Bit #(3) funct3_C_SQ = 3'b_101; // RV128
|
||||
|
||||
Bit #(3) funct3_C_SW = 3'b_110;
|
||||
|
||||
Bit #(3) funct3_C_SD = 3'b_111; // RV64 and RV128
|
||||
Bit #(3) funct3_C_FSW = 3'b_111; // RV32FC
|
||||
|
||||
Bit #(3) funct3_C_JAL = 3'b_001; // RV32
|
||||
Bit #(3) funct3_C_J = 3'b_101;
|
||||
Bit #(3) funct3_C_BEQZ = 3'b_110;
|
||||
Bit #(3) funct3_C_BNEZ = 3'b_111;
|
||||
|
||||
Bit #(4) funct4_C_JR = 4'b_1000;
|
||||
Bit #(4) funct4_C_JALR = 4'b_1001;
|
||||
|
||||
Bit #(3) funct3_C_LI = 3'b_010;
|
||||
Bit #(3) funct3_C_LUI = 3'b_011; // RV64 and RV128
|
||||
|
||||
Bit #(3) funct3_C_NOP = 3'b_000;
|
||||
Bit #(3) funct3_C_ADDI = 3'b_000;
|
||||
Bit #(3) funct3_C_ADDIW = 3'b_001;
|
||||
Bit #(3) funct3_C_ADDI16SP = 3'b_011;
|
||||
Bit #(3) funct3_C_ADDI4SPN = 3'b_000;
|
||||
Bit #(3) funct3_C_SLLI = 3'b_000;
|
||||
|
||||
Bit #(3) funct3_C_SRLI = 3'b_100;
|
||||
Bit #(2) funct2_C_SRLI = 2'b_00;
|
||||
|
||||
Bit #(3) funct3_C_SRAI = 3'b_100;
|
||||
Bit #(2) funct2_C_SRAI = 2'b_01;
|
||||
|
||||
Bit #(3) funct3_C_ANDI = 3'b_100;
|
||||
Bit #(2) funct2_C_ANDI = 2'b_10;
|
||||
|
||||
Bit #(4) funct4_C_MV = 4'b_1000;
|
||||
Bit #(4) funct4_C_ADD = 4'b_1001;
|
||||
|
||||
Bit #(6) funct6_C_AND = 6'b_100_0_11;
|
||||
Bit #(2) funct2_C_AND = 2'b_11;
|
||||
|
||||
Bit #(6) funct6_C_OR = 6'b_100_0_11;
|
||||
Bit #(2) funct2_C_OR = 2'b_10;
|
||||
|
||||
Bit #(6) funct6_C_XOR = 6'b_100_0_11;
|
||||
Bit #(2) funct2_C_XOR = 2'b_01;
|
||||
|
||||
Bit #(6) funct6_C_SUB = 6'b_100_0_11;
|
||||
Bit #(2) funct2_C_SUB = 2'b_00;
|
||||
|
||||
Bit #(6) funct6_C_ADDW = 6'b_100_1_11;
|
||||
Bit #(2) funct2_C_ADDW = 2'b_01;
|
||||
|
||||
Bit #(6) funct6_C_SUBW = 6'b_100_1_11;
|
||||
Bit #(2) funct2_C_SUBW = 2'b_00;
|
||||
|
||||
Bit #(4) funct4_C_EBREAK = 4'b_1001;
|
||||
|
||||
// ================================================================
|
||||
// Functions to extract instruction fields from 'C' (compressed) instructions
|
||||
|
||||
function Tuple4 #(Bit #(4), RegName, RegName, Bit #(2)) fv_ifields_CR_type (Instr_C instr);
|
||||
let funct4 = instr [15:12];
|
||||
let rd_rs1 = instr [11: 7];
|
||||
let rs2 = instr [ 6: 2];
|
||||
let op = instr [ 1: 0];
|
||||
return tuple4 (funct4, rd_rs1, rs2, op);
|
||||
endfunction
|
||||
|
||||
function Tuple5 #(Bit #(3), Bit #(1), Bit #(5), Bit #(5), Bit #(2)) fv_ifields_CI_type (Instr_C instr);
|
||||
let funct3 = instr [15:13];
|
||||
let imm_at_12 = instr [12:12];
|
||||
let rd_rs1 = instr [11: 7];
|
||||
let imm_at_6_2 = instr [ 6: 2];
|
||||
let op = instr [ 1: 0];
|
||||
return tuple5 (funct3, imm_at_12, rd_rs1, imm_at_6_2, op);
|
||||
endfunction
|
||||
|
||||
function Tuple4 #(Bit #(3), Bit #(6), RegName, Bit #(2)) fv_ifields_CSS_type (Instr_C instr);
|
||||
let funct3 = instr [15:13];
|
||||
let imm_at_12_7 = instr [12: 7];
|
||||
let rs2 = instr [ 6: 2];
|
||||
let op = instr [ 1: 0];
|
||||
return tuple4 (funct3, imm_at_12_7, rs2, op);
|
||||
endfunction
|
||||
|
||||
function Tuple4 #(Bit #(3), Bit #(8), RegName, Bit #(2)) fv_ifields_CIW_type (Instr_C instr);
|
||||
let funct3 = instr [15:13];
|
||||
let imm_at_12_5 = instr [12: 5];
|
||||
let rd = {2'b01, instr [4:2]};
|
||||
let op = instr [ 1: 0];
|
||||
return tuple4 (funct3, imm_at_12_5, rd, op);
|
||||
endfunction
|
||||
|
||||
function Tuple6 #(Bit #(3), Bit #(3), RegName, Bit #(2), RegName, Bit #(2)) fv_ifields_CL_type (Instr_C instr);
|
||||
let funct3 = instr [15:13];
|
||||
let imm_at_12_10 = instr [12:10];
|
||||
let rs1 = {2'b01, instr [9:7]};
|
||||
let imm_at_6_5 = instr [ 6: 5];
|
||||
let rd = {2'b01, instr [4:2]};
|
||||
let op = instr [ 1: 0];
|
||||
return tuple6 (funct3, imm_at_12_10, rs1, imm_at_6_5, rd, op);
|
||||
endfunction
|
||||
|
||||
function Tuple6 #(Bit #(3), Bit #(3), RegName, Bit #(2), RegName, Bit #(2)) fv_ifields_CS_type (Instr_C instr);
|
||||
let funct3 = instr [15:13];
|
||||
let imm_at_12_10 = instr [12:10];
|
||||
let rs1 = {2'b01, instr [9:7]};
|
||||
let imm_at_6_5 = instr [ 6: 5];
|
||||
let rs2 = {2'b01, instr [4:2]};
|
||||
let op = instr [ 1: 0];
|
||||
return tuple6 (funct3, imm_at_12_10, rs1, imm_at_6_5, rs2, op);
|
||||
endfunction
|
||||
|
||||
function Tuple5 #(Bit #(6), RegName, Bit #(2), RegName, Bit #(2)) fv_ifields_CA_type (Instr_C instr);
|
||||
let funct6 = instr [15:10];
|
||||
let rd_rs1 = {2'b01, instr [9:7]};
|
||||
let funct2 = instr [ 6: 5];
|
||||
let rs2 = {2'b01, instr [4:2]};
|
||||
let op = instr [ 1: 0];
|
||||
return tuple5 (funct6, rd_rs1, funct2, rs2, op);
|
||||
endfunction
|
||||
|
||||
function Tuple5 #(Bit #(3), Bit #(3), RegName, Bit #(5), Bit #(2)) fv_ifields_CB_type (Instr_C instr);
|
||||
let funct3 = instr [15:13];
|
||||
let imm_at_12_10 = instr [12:10];
|
||||
let rs1 = {2'b01, instr [9:7]};
|
||||
let imm_at_6_2 = instr [ 6: 2];
|
||||
let op = instr [ 1: 0];
|
||||
return tuple5 (funct3, imm_at_12_10, rs1, imm_at_6_2, op);
|
||||
endfunction
|
||||
|
||||
function Tuple3 #(Bit #(3), Bit #(11), Bit #(2)) fv_ifields_CJ_type (Instr_C instr);
|
||||
let funct3 = instr [15:13];
|
||||
let imm_at_12_2 = instr [12: 2];
|
||||
let op = instr [ 1: 0];
|
||||
return tuple3 (funct3, imm_at_12_2, op);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
716
src_Core/ISA/ISA_Decls_Priv_M.bsv
Normal file
716
src_Core/ISA/ISA_Decls_Priv_M.bsv
Normal file
@@ -0,0 +1,716 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
// ================================================================
|
||||
// This is an 'include' file, not a separate BSV package
|
||||
//
|
||||
// Contains RISC-V Machine-Level ISA defs
|
||||
//
|
||||
// ================================================================
|
||||
|
||||
// ================================================================
|
||||
// Utility functions
|
||||
|
||||
// In these functions, 'bitpos' is Bit #(6) which is enough to index
|
||||
// 64-bit words in RV64.
|
||||
|
||||
function Bit #(n) fv_assign_bit (Bit #(n) x, Bit #(6) bitpos, Bit #(1) b)
|
||||
provisos (Add #(a__, 1, n));
|
||||
Bit #(n) mask = (1 << bitpos);
|
||||
Bit #(n) val = (extend (b) << bitpos);
|
||||
return ((x & (~ mask)) | val);
|
||||
endfunction
|
||||
|
||||
function Bit #(n) fv_assign_bits (Bit #(n) x, Bit #(6) bitpos, Bit #(w) bs)
|
||||
provisos (Add #(a__, w, n));
|
||||
Bit #(n) mask = (((1 << valueOf (w)) - 1) << bitpos);
|
||||
Bit #(n) val = (extend (bs) << bitpos);
|
||||
return ((x & (~ mask)) | val);
|
||||
endfunction
|
||||
|
||||
function Bit #(w) fv_get_bits (Bit #(n) x, Bit #(6) bitpos)
|
||||
provisos (Add #(a__, w, n));
|
||||
Bit #(n) mask = ((1 << valueOf (w)) - 1);
|
||||
return truncate ((x >> bitpos) & mask);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Machine-level CSRs
|
||||
|
||||
CSR_Addr csr_addr_mvendorid = 12'hF11; // Vendor ID
|
||||
CSR_Addr csr_addr_marchid = 12'hF12; // Architecture ID
|
||||
CSR_Addr csr_addr_mimpid = 12'hF13; // Implementation ID
|
||||
CSR_Addr csr_addr_mhartid = 12'hF14; // Hardware thread ID
|
||||
|
||||
CSR_Addr csr_addr_mstatus = 12'h300; // Machine status
|
||||
CSR_Addr csr_addr_misa = 12'h301; // ISA and extensions
|
||||
CSR_Addr csr_addr_medeleg = 12'h302; // Machine exception delegation
|
||||
CSR_Addr csr_addr_mideleg = 12'h303; // Machine interrupt delegation
|
||||
CSR_Addr csr_addr_mie = 12'h304; // Machine interrupt-enable
|
||||
CSR_Addr csr_addr_mtvec = 12'h305; // Machine trap handler base address
|
||||
CSR_Addr csr_addr_mcounteren = 12'h306; // Machine counter enable
|
||||
|
||||
CSR_Addr csr_addr_mscratch = 12'h340; // Scratch reg for machine trap handlers
|
||||
CSR_Addr csr_addr_mepc = 12'h341; // Machine exception program counter
|
||||
CSR_Addr csr_addr_mcause = 12'h342; // Machine trap cause
|
||||
CSR_Addr csr_addr_mtval = 12'h343; // Machine bad address
|
||||
CSR_Addr csr_addr_mip = 12'h344; // Machine interrupt pending
|
||||
|
||||
CSR_Addr csr_addr_pmpcfg0 = 12'h3A0; // PMP Config
|
||||
CSR_Addr csr_addr_pmpcfg1 = 12'h3A1; // PMP Config
|
||||
CSR_Addr csr_addr_pmpcfg2 = 12'h3A2; // PMP Config
|
||||
CSR_Addr csr_addr_pmpcfg3 = 12'h3A3; // PMP Config
|
||||
CSR_Addr csr_addr_pmpaddr0 = 12'h3B0; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr1 = 12'h3B1; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr2 = 12'h3B2; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr3 = 12'h3B3; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr4 = 12'h3B4; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr5 = 12'h3B5; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr6 = 12'h3B6; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr7 = 12'h3B7; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr8 = 12'h3B8; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr9 = 12'h3B9; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr10 = 12'h3BA; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr11 = 12'h3BB; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr12 = 12'h3BC; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr13 = 12'h3BD; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr14 = 12'h3BE; // PMP address register
|
||||
CSR_Addr csr_addr_pmpaddr15 = 12'h3BF; // PMP address register
|
||||
|
||||
CSR_Addr csr_addr_mcycle = 12'hB00; // Machine cycle counter
|
||||
CSR_Addr csr_addr_minstret = 12'hB02; // Machine Instructions retired counter
|
||||
|
||||
CSR_Addr csr_addr_mhpmcounter3 = 12'hB03; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter4 = 12'hB04; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter5 = 12'hB05; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter6 = 12'hB06; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter7 = 12'hB07; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter8 = 12'hB08; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter9 = 12'hB09; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter10 = 12'hB0A; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter11 = 12'hB0B; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter12 = 12'hB0C; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter13 = 12'hB0D; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter14 = 12'hB0E; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter15 = 12'hB0F; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter16 = 12'hB10; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter17 = 12'hB11; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter18 = 12'hB12; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter19 = 12'hB13; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter20 = 12'hB14; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter21 = 12'hB15; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter22 = 12'hB16; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter23 = 12'hB17; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter24 = 12'hB18; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter25 = 12'hB19; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter26 = 12'hB1A; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter27 = 12'hB1B; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter28 = 12'hB1C; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter29 = 12'hB1D; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter30 = 12'hB1E; // Machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter31 = 12'hB1F; // Machine performance-monitoring counter
|
||||
|
||||
CSR_Addr csr_addr_mcycleh = 12'hB80; // Upper 32 bits of csr_mcycle (RV32I only)
|
||||
CSR_Addr csr_addr_minstreth = 12'hB82; // Upper 32 bits of csr_minstret (RV32I only)
|
||||
|
||||
CSR_Addr csr_addr_mhpmcounter3h = 12'hB83; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter4h = 12'hB84; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter5h = 12'hB85; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter6h = 12'hB86; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter7h = 12'hB87; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter8h = 12'hB88; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter9h = 12'hB89; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter10h = 12'hB8A; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter11h = 12'hB8B; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter12h = 12'hB8C; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter13h = 12'hB8D; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter14h = 12'hB8E; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter15h = 12'hB8F; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter16h = 12'hB90; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter17h = 12'hB91; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter18h = 12'hB92; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter19h = 12'hB93; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter20h = 12'hB94; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter21h = 12'hB95; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter22h = 12'hB96; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter23h = 12'hB97; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter24h = 12'hB98; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter25h = 12'hB99; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter26h = 12'hB9A; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter27h = 12'hB9B; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter28h = 12'hB9C; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter29h = 12'hB9D; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter30h = 12'hB9E; // Upper 32 bits of machine performance-monitoring counter
|
||||
CSR_Addr csr_addr_mhpmcounter31h = 12'hB9F; // Upper 32 bits of machine performance-monitoring counter
|
||||
|
||||
CSR_Addr csr_addr_mhpmevent3 = 12'h323; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent4 = 12'h324; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent5 = 12'h325; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent6 = 12'h326; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent7 = 12'h327; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent8 = 12'h328; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent9 = 12'h329; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent10 = 12'h32A; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent11 = 12'h32B; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent12 = 12'h32C; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent13 = 12'h32D; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent14 = 12'h32E; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent15 = 12'h32F; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent16 = 12'h330; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent17 = 12'h331; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent18 = 12'h332; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent19 = 12'h333; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent20 = 12'h334; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent21 = 12'h335; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent22 = 12'h336; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent23 = 12'h337; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent24 = 12'h338; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent25 = 12'h339; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent26 = 12'h33A; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent27 = 12'h33B; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent28 = 12'h33C; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent29 = 12'h33D; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent30 = 12'h33E; // Machine performance-monitoring event selector
|
||||
CSR_Addr csr_addr_mhpmevent31 = 12'h33F; // Machine performance-monitoring event selector
|
||||
|
||||
CSR_Addr csr_addr_tselect = 12'h7A0; // Debug/Trace trigger register select
|
||||
CSR_Addr csr_addr_tdata1 = 12'h7A1; // First Debug/Trace trigger data
|
||||
CSR_Addr csr_addr_tdata2 = 12'h7A2; // Secont Debug/Trace trigger data
|
||||
CSR_Addr csr_addr_tdata3 = 12'h7A3; // Third Debug/Trace trigger data
|
||||
|
||||
CSR_Addr csr_addr_dcsr = 12'h7B0; // Debug control and status
|
||||
CSR_Addr csr_addr_dpc = 12'h7B1; // Debug PC
|
||||
CSR_Addr csr_addr_dscratch0 = 12'h7B2; // Debug scratch0
|
||||
CSR_Addr csr_addr_dscratch1 = 12'h7B3; // Debug scratch1
|
||||
|
||||
// ================================================================
|
||||
// MISA
|
||||
|
||||
typedef struct {
|
||||
Bit #(2) mxl;
|
||||
Bit #(1) z; Bit #(1) y;
|
||||
Bit #(1) x; Bit #(1) w; Bit #(1) v; Bit #(1) u; Bit #(1) t; Bit #(1) s; Bit #(1) r; Bit #(1) q;
|
||||
Bit #(1) p; Bit #(1) o; Bit #(1) n; Bit #(1) m; Bit #(1) l; Bit #(1) k; Bit #(1) j; Bit #(1) i;
|
||||
Bit #(1) h; Bit #(1) g; Bit #(1) f; Bit #(1) e; Bit #(1) d; Bit #(1) c; Bit #(1) b; Bit #(1) a;
|
||||
} MISA
|
||||
deriving (Bits);
|
||||
|
||||
Bit #(2) misa_mxl_zero = 0;
|
||||
Bit #(2) misa_mxl_32 = 1;
|
||||
Bit #(2) misa_mxl_64 = 2;
|
||||
Bit #(2) misa_mxl_128 = 3;
|
||||
|
||||
function WordXL misa_to_word (MISA ms);
|
||||
return {ms.mxl,
|
||||
0, // expands appropriately for RV32 and RV64
|
||||
ms.z, ms.y,
|
||||
ms.x, ms.w, ms.v, ms.u, ms.t, ms.s, ms.r, ms.q,
|
||||
ms.p, ms.o, ms.n, ms.m, ms.l, ms.k, ms.j, ms.i,
|
||||
ms.h, ms.g, ms.f, ms.e, ms.d, ms.c, ms.b, ms.a};
|
||||
endfunction
|
||||
|
||||
function MISA word_to_misa (WordXL x);
|
||||
return MISA {mxl: x [xlen-1:xlen-2],
|
||||
z: x [25], y: x [24],
|
||||
x: x [23], w: x [22], v: x [21], u: x [20], t: x [19], s: x [18], r: x [17], q: x [16],
|
||||
p: x [15], o: x [14], n: x [13], m: x [12], l: x [11], k: x [10], j: x [9], i: x [8],
|
||||
h: x [7], g: x [6], f: x [5], e: x [4], d: x [3], c: x [2], b: x [1], a: x [0]};
|
||||
endfunction
|
||||
|
||||
instance FShow #(MISA);
|
||||
function Fmt fshow (MISA misa);
|
||||
let fmt_mxl = case (misa.mxl)
|
||||
1: $format ("mxl 32");
|
||||
2: $format ("mxl 64");
|
||||
3: $format ("mxl 128");
|
||||
default: $format ("mxl unknown %0d", misa.mxl);
|
||||
endcase;
|
||||
return ( fmt_mxl
|
||||
+ $format ((misa.z == 1'b1) ? "Z" : "")
|
||||
+ $format ((misa.y == 1'b1) ? "Y" : "")
|
||||
+ $format ((misa.x == 1'b1) ? "X" : "")
|
||||
+ $format ((misa.w == 1'b1) ? "W" : "")
|
||||
+ $format ((misa.v == 1'b1) ? "V" : "")
|
||||
+ $format ((misa.u == 1'b1) ? "U" : "")
|
||||
+ $format ((misa.t == 1'b1) ? "T" : "")
|
||||
+ $format ((misa.s == 1'b1) ? "S" : "")
|
||||
+ $format ((misa.r == 1'b1) ? "R" : "")
|
||||
+ $format ((misa.q == 1'b1) ? "Q" : "")
|
||||
+ $format ((misa.p == 1'b1) ? "P" : "")
|
||||
+ $format ((misa.o == 1'b1) ? "O" : "")
|
||||
+ $format ((misa.n == 1'b1) ? "N" : "")
|
||||
+ $format ((misa.m == 1'b1) ? "M" : "")
|
||||
+ $format ((misa.l == 1'b1) ? "L" : "")
|
||||
+ $format ((misa.k == 1'b1) ? "K" : "")
|
||||
+ $format ((misa.j == 1'b1) ? "J" : "")
|
||||
+ $format ((misa.i == 1'b1) ? "I" : "")
|
||||
+ $format ((misa.h == 1'b1) ? "H" : "")
|
||||
+ $format ((misa.g == 1'b1) ? "G" : "")
|
||||
+ $format ((misa.f == 1'b1) ? "F" : "")
|
||||
+ $format ((misa.d == 1'b1) ? "E" : "")
|
||||
+ $format ((misa.d == 1'b1) ? "D" : "")
|
||||
+ $format ((misa.c == 1'b1) ? "C" : "")
|
||||
+ $format ((misa.b == 1'b1) ? "B" : "")
|
||||
+ $format ((misa.a == 1'b1) ? "A" : ""));
|
||||
endfunction
|
||||
endinstance
|
||||
|
||||
// ================================================================
|
||||
// MSTATUS
|
||||
|
||||
Integer mstatus_sd_bitpos = xlen - 1;
|
||||
|
||||
Integer mstatus_sxl_bitpos = 34;
|
||||
Integer mstatus_uxl_bitpos = 32;
|
||||
|
||||
Integer mstatus_tsr_bitpos = 22;
|
||||
Integer mstatus_tw_bitpos = 21;
|
||||
Integer mstatus_tvm_bitpos = 20;
|
||||
|
||||
Integer mstatus_mxr_bitpos = 19;
|
||||
Integer mstatus_sum_bitpos = 18;
|
||||
Integer mstatus_mprv_bitpos = 17;
|
||||
|
||||
Integer mstatus_xs_bitpos = 15;
|
||||
Integer mstatus_fs_bitpos = 13;
|
||||
|
||||
Integer mstatus_mpp_bitpos = 11;
|
||||
Integer mstatus_WPRI_9_bitpos = 9;
|
||||
Integer mstatus_spp_bitpos = 8;
|
||||
|
||||
Integer mstatus_mpie_bitpos = 7;
|
||||
Integer mstatus_WPRI_6_bitpos = 6;
|
||||
Integer mstatus_spie_bitpos = 5;
|
||||
Integer mstatus_upie_bitpos = 4;
|
||||
|
||||
Integer mstatus_mie_bitpos = 3;
|
||||
Integer mstatus_WPRI_2_bitpos = 2;
|
||||
Integer mstatus_sie_bitpos = 1;
|
||||
Integer mstatus_uie_bitpos = 0;
|
||||
|
||||
// Values for FS and XS
|
||||
|
||||
Bit #(2) fs_xs_off = 2'h0;
|
||||
Bit #(2) fs_xs_initial = 2'h1;
|
||||
Bit #(2) fs_xs_clean = 2'h2;
|
||||
Bit #(2) fs_xs_dirty = 2'h3;
|
||||
|
||||
// Extract MSTATUS.FS field
|
||||
function Bit #(2) fv_mstatus_fs (WordXL mstatus);
|
||||
return (fv_get_bits (mstatus, fromInteger (mstatus_fs_bitpos)));
|
||||
endfunction
|
||||
|
||||
// Virtual field SD is computed from FS and XS
|
||||
function Bit #(1) fv_mstatus_sd (WordXL mstatus);
|
||||
Bit #(2) xs = fv_get_bits (mstatus, fromInteger (mstatus_xs_bitpos));
|
||||
Bit #(2) fs = fv_get_bits (mstatus, fromInteger (mstatus_fs_bitpos));
|
||||
return (((fs == fs_xs_dirty) || (xs == fs_xs_dirty)) ? 1 : 0);
|
||||
endfunction
|
||||
|
||||
function Fmt fshow_mstatus (MISA misa, WordXL mstatus);
|
||||
Bit #(2) sxl = ((misa.mxl == misa_mxl_64) ? fv_get_bits (mstatus, fromInteger (mstatus_sxl_bitpos)) : 0);
|
||||
Bit #(2) uxl = ((misa.mxl == misa_mxl_64) ? fv_get_bits (mstatus, fromInteger (mstatus_uxl_bitpos)) : 0);
|
||||
Bit #(2) xs = fv_get_bits (mstatus, fromInteger (mstatus_xs_bitpos));
|
||||
Bit #(2) fs = fv_get_bits (mstatus, fromInteger (mstatus_fs_bitpos));
|
||||
Bit #(2) mpp = fv_get_bits (mstatus, fromInteger (mstatus_mpp_bitpos));
|
||||
|
||||
return ( $format ("MStatus{")
|
||||
+ $format ("sd:%0d", fv_mstatus_sd (mstatus))
|
||||
|
||||
+ ((misa.mxl == misa_mxl_64) ? $format (" sxl:%0d uxl:%0d", sxl, uxl) : $format (""))
|
||||
|
||||
+ $format (" tsr:%0d", mstatus [mstatus_tsr_bitpos])
|
||||
+ $format (" tw:%0d", mstatus [mstatus_tw_bitpos])
|
||||
+ $format (" tvm:%0d", mstatus [mstatus_tvm_bitpos])
|
||||
+ $format (" mxr:%0d", mstatus [mstatus_mxr_bitpos])
|
||||
+ $format (" sum:%0d", mstatus [mstatus_sum_bitpos])
|
||||
+ $format (" mprv:%0d", mstatus [mstatus_mprv_bitpos])
|
||||
|
||||
+ $format (" xs:%0d", xs)
|
||||
+ $format (" fs:%0d", fs)
|
||||
|
||||
+ $format (" mpp:%0d", mpp)
|
||||
+ $format (" spp:%0d", mstatus [mstatus_spp_bitpos])
|
||||
|
||||
+ $format (" pies:%0d_%0d%0d",
|
||||
mstatus [mstatus_mpie_bitpos], mstatus [mstatus_spie_bitpos], mstatus [mstatus_upie_bitpos])
|
||||
|
||||
+ $format (" ies:%0d_%0d%0d",
|
||||
mstatus [mstatus_mie_bitpos], mstatus [mstatus_sie_bitpos], mstatus [mstatus_uie_bitpos])
|
||||
+ $format ("}")
|
||||
);
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// Help functions to manipulate mstatus on traps and trap-returns
|
||||
|
||||
function Priv_Mode fv_new_priv_on_exception (MISA misa,
|
||||
Priv_Mode from_priv,
|
||||
Bool interrupt,
|
||||
Exc_Code exc_code,
|
||||
Bit #(16) medeleg,
|
||||
Bit #(12) mideleg,
|
||||
Bit #(16) sedeleg,
|
||||
Bit #(12) sideleg);
|
||||
Priv_Mode to_priv = m_Priv_Mode;
|
||||
Bit #(1) deleg_bit = 1'b0;
|
||||
|
||||
// If the current priv mode is M, it cannot be delegated.
|
||||
if (from_priv < m_Priv_Mode) begin
|
||||
// If S is supported
|
||||
if (misa.s == 1'b1) begin
|
||||
// Look in medeleg/mideleg for the cause bit; if set, delegate.
|
||||
if (interrupt)
|
||||
deleg_bit = mideleg [exc_code];
|
||||
else
|
||||
deleg_bit = medeleg [exc_code];
|
||||
if (deleg_bit == 1'b1) begin
|
||||
// If the current priv mode is S, then delegate to S.
|
||||
to_priv = s_Priv_Mode;
|
||||
// If the current priv mode is U, and user mode traps are supported,
|
||||
// then consult sedeleg/sideleg to determine if delegated to U mode.
|
||||
if ((from_priv == u_Priv_Mode) && (misa.n == 1'b1)) begin
|
||||
if (interrupt)
|
||||
deleg_bit = sideleg [exc_code];
|
||||
else
|
||||
deleg_bit = sedeleg [exc_code];
|
||||
if (deleg_bit == 1'b1)
|
||||
to_priv = u_Priv_Mode;
|
||||
end
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// S is not supported
|
||||
// If user mode traps are supported,
|
||||
// then consult medele/mideleg to determine if delegated to U mode.
|
||||
if (misa.n == 1'b1) begin
|
||||
// Look in medeleg/mideleg for the cause bit; if set, delegate.
|
||||
if (interrupt)
|
||||
deleg_bit = mideleg [exc_code];
|
||||
else
|
||||
deleg_bit = medeleg [exc_code];
|
||||
if (deleg_bit == 1'b1)
|
||||
to_priv = u_Priv_Mode;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return to_priv;
|
||||
endfunction
|
||||
|
||||
function WordXL fv_new_mstatus_on_exception (WordXL mstatus, Priv_Mode from_y, Priv_Mode to_x);
|
||||
Bit #(6) ie_to_x = extend (to_x);
|
||||
Bit #(6) pie_to_x = fromInteger (mstatus_upie_bitpos) + extend (to_x);
|
||||
// xPIE = xIE
|
||||
mstatus = fv_assign_bit (mstatus, pie_to_x, mstatus [ie_to_x]);
|
||||
// xIE = 0
|
||||
mstatus = fv_assign_bit (mstatus, ie_to_x, 1'b0);
|
||||
|
||||
// xPP = y Assert: (to_x == m_Priv_Mode) || (to_x == s_Priv_Mode)
|
||||
mstatus = ( (to_x == m_Priv_Mode)
|
||||
? fv_assign_bits (mstatus, fromInteger (mstatus_mpp_bitpos), from_y)
|
||||
: fv_assign_bit (mstatus, fromInteger (mstatus_spp_bitpos), from_y [0]));
|
||||
return mstatus;
|
||||
endfunction
|
||||
|
||||
function Tuple2 #(WordXL, Priv_Mode) fv_new_mstatus_on_ret (MISA misa,
|
||||
WordXL mstatus,
|
||||
Priv_Mode from_x);
|
||||
Bit #(6) ie_from_x = extend (from_x);
|
||||
Bit #(6) pie_from_x = fromInteger (mstatus_upie_bitpos) + extend (from_x);
|
||||
|
||||
// Pop the interrupt-enable stack
|
||||
// (set xIE = xPIE)
|
||||
mstatus = fv_assign_bit (mstatus, ie_from_x, mstatus [pie_from_x]);
|
||||
|
||||
// Enable interrupt at from_x
|
||||
// (set xPIE = 1)
|
||||
mstatus = fv_assign_bit (mstatus, pie_from_x, 1'b1);
|
||||
|
||||
// Pop the previous privilege mode
|
||||
// which empties the one-element stack, revealing the default value
|
||||
// (set xPP to U -- or M if U is not supported)
|
||||
Priv_Mode to_y;
|
||||
Priv_Mode default_pp = ((misa.u == 1'b1) ? u_Priv_Mode : m_Priv_Mode);
|
||||
if (from_x == m_Priv_Mode) begin
|
||||
to_y = fv_get_bits (mstatus, fromInteger (mstatus_mpp_bitpos));
|
||||
mstatus = fv_assign_bits (mstatus, fromInteger (mstatus_mpp_bitpos), default_pp);
|
||||
end
|
||||
else begin //if (from_x == s_Priv_Mode)
|
||||
to_y = {1'b0, mstatus [mstatus_spp_bitpos]};
|
||||
mstatus = fv_assign_bit (mstatus, fromInteger (mstatus_spp_bitpos), default_pp [0]);
|
||||
end
|
||||
|
||||
return tuple2 (mstatus, to_y);
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Logical view of csr_mtvec register
|
||||
|
||||
typedef enum {DIRECT, VECTORED} MTVEC_Mode
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Bit #(XLEN_MINUS_2) base;
|
||||
MTVEC_Mode mode;
|
||||
} MTVec
|
||||
deriving (Bits, FShow);
|
||||
|
||||
function WordXL mtvec_to_word (MTVec mv);
|
||||
return {mv.base,
|
||||
1'b0,
|
||||
pack (mv.mode)};
|
||||
endfunction
|
||||
|
||||
function MTVec word_to_mtvec (WordXL x);
|
||||
return MTVec {base: truncate (x >> 2),
|
||||
mode: unpack (x[0])};
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Logical view of csr_mcounteren register
|
||||
typedef struct {
|
||||
Bit#(1) ir;
|
||||
Bit#(1) tm;
|
||||
Bit#(1) cy;
|
||||
} MCounteren
|
||||
deriving (Bits, FShow);
|
||||
|
||||
function WordXL mcounteren_to_word (MCounteren mc);
|
||||
return {0,
|
||||
mc.ir,
|
||||
mc.tm,
|
||||
mc.cy};
|
||||
endfunction
|
||||
|
||||
function MCounteren word_to_mcounteren (WordXL x);
|
||||
return MCounteren {ir: x[2],
|
||||
tm: x[1],
|
||||
cy: x[0]};
|
||||
endfunction
|
||||
|
||||
function MCounteren mcounteren_reset_value;
|
||||
return MCounteren {ir: 1'b0,
|
||||
tm: 1'b0,
|
||||
cy: 1'b0};
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// MIP and MIE fields (interrupt pending, interrupt enable)
|
||||
|
||||
Integer mip_usip_bitpos = 0;
|
||||
Integer mip_ssip_bitpos = 1;
|
||||
Integer mip_msip_bitpos = 3;
|
||||
|
||||
Integer mip_utip_bitpos = 4;
|
||||
Integer mip_stip_bitpos = 5;
|
||||
Integer mip_mtip_bitpos = 7;
|
||||
|
||||
Integer mip_ueip_bitpos = 8;
|
||||
Integer mip_seip_bitpos = 9;
|
||||
Integer mip_meip_bitpos = 11;
|
||||
|
||||
// ================================================================
|
||||
// MCAUSE (reason for exception)
|
||||
|
||||
typedef struct {
|
||||
Bit #(1) interrupt;
|
||||
Exc_Code exc_code;
|
||||
} MCause
|
||||
deriving (Bits);
|
||||
|
||||
instance FShow #(MCause);
|
||||
function Fmt fshow (MCause mc);
|
||||
if (mc.interrupt == 1)
|
||||
return fshow_interrupt_Exc_Code (mc.exc_code);
|
||||
else
|
||||
return fshow_trap_Exc_Code (mc.exc_code);
|
||||
endfunction
|
||||
endinstance
|
||||
|
||||
function WordXL mcause_to_word (MCause mc);
|
||||
return {mc.interrupt, 0, mc.exc_code};
|
||||
endfunction
|
||||
|
||||
function MCause word_to_mcause (WordXL x);
|
||||
return MCause {interrupt: msb (x),
|
||||
exc_code: truncate (x)};
|
||||
endfunction
|
||||
|
||||
// Exception Codes in mcause
|
||||
|
||||
typedef Bit #(4) Exc_Code;
|
||||
|
||||
// When Interrupt = 1 (interrupt)
|
||||
|
||||
Exc_Code exc_code_USER_SW_INTERRUPT = 0;
|
||||
Exc_Code exc_code_SUPERVISOR_SW_INTERRUPT = 1;
|
||||
Exc_Code exc_code_HYPERVISOR_SW_INTERRUPT = 2;
|
||||
Exc_Code exc_code_MACHINE_SW_INTERRUPT = 3;
|
||||
|
||||
Exc_Code exc_code_USER_TIMER_INTERRUPT = 4;
|
||||
Exc_Code exc_code_SUPERVISOR_TIMER_INTERRUPT = 5;
|
||||
Exc_Code exc_code_HYPERVISOR_TIMER_INTERRUPT = 6;
|
||||
Exc_Code exc_code_MACHINE_TIMER_INTERRUPT = 7;
|
||||
|
||||
Exc_Code exc_code_USER_EXTERNAL_INTERRUPT = 8;
|
||||
Exc_Code exc_code_SUPERVISOR_EXTERNAL_INTERRUPT = 9;
|
||||
Exc_Code exc_code_HYPERVISOR_EXTERNAL_INTERRUPT = 10;
|
||||
Exc_Code exc_code_MACHINE_EXTERNAL_INTERRUPT = 11;
|
||||
|
||||
// When Interrupt = 0 (trap)
|
||||
|
||||
Exc_Code exc_code_INSTR_ADDR_MISALIGNED = 0;
|
||||
Exc_Code exc_code_INSTR_ACCESS_FAULT = 1;
|
||||
Exc_Code exc_code_ILLEGAL_INSTRUCTION = 2;
|
||||
Exc_Code exc_code_BREAKPOINT = 3;
|
||||
|
||||
Exc_Code exc_code_LOAD_ADDR_MISALIGNED = 4;
|
||||
Exc_Code exc_code_LOAD_ACCESS_FAULT = 5;
|
||||
|
||||
Exc_Code exc_code_STORE_AMO_ADDR_MISALIGNED = 6;
|
||||
Exc_Code exc_code_STORE_AMO_ACCESS_FAULT = 7;
|
||||
|
||||
Exc_Code exc_code_ECALL_FROM_U = 8;
|
||||
Exc_Code exc_code_ECALL_FROM_S = 9;
|
||||
Exc_Code exc_code_RESERVED_10 = 10;
|
||||
Exc_Code exc_code_ECALL_FROM_M = 11;
|
||||
|
||||
Exc_Code exc_code_INSTR_PAGE_FAULT = 12;
|
||||
Exc_Code exc_code_LOAD_PAGE_FAULT = 13;
|
||||
Exc_Code exc_code_RESERVED_14 = 14;
|
||||
Exc_Code exc_code_STORE_AMO_PAGE_FAULT = 15;
|
||||
|
||||
|
||||
function Fmt fshow_interrupt_Exc_Code (Exc_Code exc_code);
|
||||
return case (exc_code)
|
||||
exc_code_USER_SW_INTERRUPT: $format ("USER_SW_INTERRUPT");
|
||||
exc_code_SUPERVISOR_SW_INTERRUPT: $format ("SUPERVISOR_SW_INTERRUPT");
|
||||
exc_code_HYPERVISOR_SW_INTERRUPT: $format ("HYPERVISOR_SW_INTERRUPT");
|
||||
exc_code_MACHINE_SW_INTERRUPT: $format ("MACHINE_SW_INTERRUPT");
|
||||
|
||||
exc_code_USER_TIMER_INTERRUPT: $format ("USER_TIMER_INTERRUPT");
|
||||
exc_code_SUPERVISOR_TIMER_INTERRUPT: $format ("SUPERVISOR_TIMER_INTERRUPT");
|
||||
exc_code_HYPERVISOR_TIMER_INTERRUPT: $format ("HYPERVISOR_TIMER_INTERRUPT");
|
||||
exc_code_MACHINE_TIMER_INTERRUPT: $format ("MACHINE_TIMER_INTERRUPT");
|
||||
|
||||
exc_code_USER_EXTERNAL_INTERRUPT: $format ("USER_EXTERNAL_INTERRUPT");
|
||||
exc_code_SUPERVISOR_EXTERNAL_INTERRUPT: $format ("SUPERVISOR_EXTERNAL_INTERRUPT");
|
||||
exc_code_HYPERVISOR_EXTERNAL_INTERRUPT: $format ("HYPERVISOR_EXTERNAL_INTERRUPT");
|
||||
exc_code_MACHINE_EXTERNAL_INTERRUPT: $format ("MACHINE_EXTERNAL_INTERRUPT");
|
||||
default: $format ("unknown interrupt Exc_Code %d", exc_code);
|
||||
endcase;
|
||||
endfunction
|
||||
|
||||
function Fmt fshow_trap_Exc_Code (Exc_Code exc_code);
|
||||
return case (exc_code)
|
||||
exc_code_INSTR_ADDR_MISALIGNED: $format ("INSTRUCTION_ADDR_MISALIGNED");
|
||||
exc_code_INSTR_ACCESS_FAULT: $format ("INSTRUCTION_ACCESS_FAULT");
|
||||
exc_code_ILLEGAL_INSTRUCTION: $format ("ILLEGAL_INSTRUCTION");
|
||||
exc_code_BREAKPOINT: $format ("BREAKPOINT");
|
||||
|
||||
exc_code_LOAD_ADDR_MISALIGNED: $format ("LOAD_ADDR_MISALIGNED");
|
||||
exc_code_LOAD_ACCESS_FAULT: $format ("LOAD_ACCESS_FAULT");
|
||||
|
||||
exc_code_STORE_AMO_ADDR_MISALIGNED: $format ("STORE_AMO_ADDR_MISALIGNED");
|
||||
exc_code_STORE_AMO_ACCESS_FAULT: $format ("STORE_AMO_ACCESS_FAULT");
|
||||
|
||||
exc_code_ECALL_FROM_U: $format ("ECALL_FROM_U");
|
||||
exc_code_ECALL_FROM_S: $format ("ECALL_FROM_S");
|
||||
exc_code_ECALL_FROM_M: $format ("ECALL_FROM_M");
|
||||
|
||||
exc_code_INSTR_PAGE_FAULT: $format ("INSTRUCTION_PAGE_FAULT");
|
||||
exc_code_LOAD_PAGE_FAULT: $format ("LOAD_PAGE_FAULT");
|
||||
exc_code_STORE_AMO_PAGE_FAULT: $format ("STORE_AMO_PAGE_FAULT");
|
||||
|
||||
default: $format ("unknown trap Exc_Code %d", exc_code);
|
||||
endcase;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Function from various CSRs and current privilege to:
|
||||
// whether or not an interrupt is pending,
|
||||
// and if so, corresponding exception code
|
||||
|
||||
function Maybe #(Exc_Code) fv_interrupt_pending (MISA misa,
|
||||
WordXL mstatus,
|
||||
WordXL mip,
|
||||
WordXL mie,
|
||||
Bit #(12) mideleg,
|
||||
Bit #(12) sideleg,
|
||||
Priv_Mode cur_priv);
|
||||
|
||||
function Maybe #(Exc_Code) fv_interrupt_i_pending (Exc_Code i);
|
||||
Bool intr_pending = ((mip [i] == 1) && (mie [i] == 1));
|
||||
Priv_Mode handler_priv;
|
||||
if (mideleg [i] == 1)
|
||||
if (misa.u == 1)
|
||||
if (misa.s == 1)
|
||||
// System with M, S, U
|
||||
if (sideleg [i] == 1)
|
||||
if (misa.n == 1)
|
||||
// M->S->U delegation
|
||||
handler_priv = u_Priv_Mode;
|
||||
else
|
||||
// Error: SIDELEG [i] should not be 1 if MISA.N is 0
|
||||
handler_priv = m_Priv_Mode;
|
||||
else
|
||||
// M->S delegation
|
||||
handler_priv = s_Priv_Mode;
|
||||
else
|
||||
// System with M, U
|
||||
if (misa.n == 1)
|
||||
// M->U delegation
|
||||
handler_priv = u_Priv_Mode;
|
||||
else
|
||||
// Error: MIDELEG [i] should not be 1 if MISA.N is 0
|
||||
handler_priv = m_Priv_Mode;
|
||||
else
|
||||
// Error: System with M only; MIDELEG [i] should not be 1
|
||||
handler_priv = m_Priv_Mode;
|
||||
else
|
||||
// no delegation
|
||||
handler_priv = m_Priv_Mode;
|
||||
|
||||
Bool xie;
|
||||
if (cur_priv == u_Priv_Mode)
|
||||
xie = (mstatus [mstatus_uie_bitpos] == 1);
|
||||
else if (cur_priv == s_Priv_Mode)
|
||||
xie = (mstatus [mstatus_sie_bitpos] == 1);
|
||||
else if (cur_priv == m_Priv_Mode)
|
||||
xie = (mstatus [mstatus_mie_bitpos] == 1);
|
||||
else
|
||||
// Error: unexpected mode
|
||||
xie = False;
|
||||
|
||||
Bool glob_enabled = ( (cur_priv < handler_priv)
|
||||
|| ((cur_priv == handler_priv) && xie));
|
||||
|
||||
return ((intr_pending && glob_enabled) ? (tagged Valid i) : (tagged Invalid));
|
||||
endfunction
|
||||
|
||||
// Check all interrupts in the following decreasing priority order
|
||||
Maybe #(Exc_Code) m_ec;
|
||||
m_ec = fv_interrupt_i_pending (exc_code_MACHINE_EXTERNAL_INTERRUPT);
|
||||
if (m_ec matches tagged Invalid)
|
||||
m_ec = fv_interrupt_i_pending (exc_code_MACHINE_SW_INTERRUPT);
|
||||
if (m_ec matches tagged Invalid)
|
||||
m_ec = fv_interrupt_i_pending (exc_code_MACHINE_TIMER_INTERRUPT);
|
||||
|
||||
if (m_ec matches tagged Invalid)
|
||||
m_ec = fv_interrupt_i_pending (exc_code_SUPERVISOR_EXTERNAL_INTERRUPT);
|
||||
if (m_ec matches tagged Invalid)
|
||||
m_ec = fv_interrupt_i_pending (exc_code_SUPERVISOR_SW_INTERRUPT);
|
||||
if (m_ec matches tagged Invalid)
|
||||
m_ec = fv_interrupt_i_pending (exc_code_SUPERVISOR_TIMER_INTERRUPT);
|
||||
|
||||
if (m_ec matches tagged Invalid)
|
||||
m_ec = fv_interrupt_i_pending (exc_code_USER_EXTERNAL_INTERRUPT);
|
||||
if (m_ec matches tagged Invalid)
|
||||
m_ec = fv_interrupt_i_pending (exc_code_USER_SW_INTERRUPT);
|
||||
if (m_ec matches tagged Invalid)
|
||||
m_ec = fv_interrupt_i_pending (exc_code_USER_TIMER_INTERRUPT);
|
||||
|
||||
return m_ec;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
421
src_Core/ISA/ISA_Decls_Priv_S.bsv
Normal file
421
src_Core/ISA/ISA_Decls_Priv_S.bsv
Normal file
@@ -0,0 +1,421 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
// ================================================================
|
||||
// WARNING: this is an 'include' file, not a separate BSV package!
|
||||
//
|
||||
// Contains RISC-V Supervisor-Level ISA defs, based on:
|
||||
// The RISC-V Instruction Set Manual"
|
||||
// Volume II: Privileged Architecture
|
||||
// Privileged Architecture Version 1.10
|
||||
// Document Version 1.10
|
||||
// May 7, 2017
|
||||
//
|
||||
// ================================================================
|
||||
|
||||
// Invariants on ifdefs:
|
||||
// - If RV32 is defined, we assume Sv32 for the VM system
|
||||
// - If RV64 is defined, one of SV39 or SV48 must also be defined for the VM system
|
||||
|
||||
// ================================================================
|
||||
// Supervisor-level CSRs
|
||||
|
||||
CSR_Addr csr_addr_sstatus = 12'h100; // Supervisor status
|
||||
CSR_Addr csr_addr_sedeleg = 12'h102; // Supervisor exception delegation
|
||||
CSR_Addr csr_addr_sideleg = 12'h103; // Supervisor interrupt delegation
|
||||
CSR_Addr csr_addr_sie = 12'h104; // Supervisor interrupt enable
|
||||
CSR_Addr csr_addr_stvec = 12'h105; // Supervisor trap handler base address
|
||||
CSR_Addr csr_addr_scounteren = 12'h106; // Supervisor counter enable
|
||||
|
||||
CSR_Addr csr_addr_sscratch = 12'h140; // Scratch reg for supervisor trap handlers
|
||||
CSR_Addr csr_addr_sepc = 12'h141; // Supervisor exception program counter
|
||||
CSR_Addr csr_addr_scause = 12'h142; // Supervisor trap cause
|
||||
CSR_Addr csr_addr_stval = 12'h143; // Supervisor bad address or instruction
|
||||
CSR_Addr csr_addr_sip = 12'h144; // Supervisor interrupt pending
|
||||
|
||||
CSR_Addr csr_addr_satp = 12'h180; // Supervisor address translation and protection
|
||||
|
||||
// ================================================================
|
||||
// SSTATUS
|
||||
|
||||
function Bit #(1) fn_sstatus_sd (WordXL sstatus_val); return sstatus_val [xlen-1]; endfunction
|
||||
|
||||
`ifdef RV64
|
||||
function Bit #(2) fn_sstatus_UXL (WordXL sstatus_val); return sstatus_val [33:32]; endfunction
|
||||
`endif
|
||||
|
||||
function Bit #(1) fn_sstatus_SUM (WordXL sstatus_val); return sstatus_val [19]; endfunction
|
||||
function Bit #(1) fn_sstatus_MXR (WordXL sstatus_val); return sstatus_val [18]; endfunction
|
||||
|
||||
function Bit #(2) fn_sstatus_xs (WordXL sstatus_val); return sstatus_val [16:15]; endfunction
|
||||
function Bit #(2) fn_sstatus_fs (WordXL sstatus_val); return sstatus_val [14:13]; endfunction
|
||||
|
||||
function Bit #(1) fn_sstatus_spp (WordXL sstatus_val); return sstatus_val [8]; endfunction
|
||||
|
||||
function Bit #(1) fn_sstatus_spie (WordXL sstatus_val); return sstatus_val [5]; endfunction
|
||||
function Bit #(1) fn_sstatus_upie (WordXL sstatus_val); return sstatus_val [4]; endfunction
|
||||
|
||||
function Bit #(1) fn_sstatus_sie (WordXL sstatus_val); return sstatus_val [1]; endfunction
|
||||
function Bit #(1) fn_sstatus_uie (WordXL sstatus_val); return sstatus_val [0]; endfunction
|
||||
|
||||
// ----------------
|
||||
// SCAUSE (reason for exception)
|
||||
|
||||
function Bit #(1) scause_interrupt (WordXL scause_val); return scause_val [xlen-1]; endfunction
|
||||
function Bit #(TSub #(XLEN,1)) scause_exception_code (WordXL scause_val); return scause_val [xlen-2:0]; endfunction
|
||||
|
||||
// ================================================================
|
||||
|
||||
`ifdef ISA_PRIV_S
|
||||
// ================================================================
|
||||
// SATP (supervisor address translation and protection)
|
||||
|
||||
// ----------------
|
||||
`ifdef RV32
|
||||
|
||||
typedef Bit #(1) VM_Mode;
|
||||
typedef Bit #(9) ASID;
|
||||
|
||||
function WordXL fn_mk_satp_val (VM_Mode mode, ASID asid, PA pa) = { mode, asid, pa [33:12] };
|
||||
function VM_Mode fn_satp_to_VM_Mode (Bit #(32) satp_val); return satp_val [31]; endfunction
|
||||
function ASID fn_satp_to_ASID (Bit #(32) satp_val); return satp_val [30:22]; endfunction
|
||||
function PPN fn_satp_to_PPN (Bit #(32) satp_val); return satp_val [21: 0]; endfunction
|
||||
|
||||
Bit #(1) satp_mode_RV32_bare = 1'h_0;
|
||||
Bit #(1) satp_mode_RV32_sv32 = 1'h_1;
|
||||
|
||||
`elsif RV64
|
||||
|
||||
typedef Bit #(4) VM_Mode;
|
||||
typedef Bit #(16) ASID;
|
||||
|
||||
function WordXL fn_mk_satp_val (VM_Mode mode, ASID asid, PA pa) = { mode, asid, pa [55:12] };
|
||||
function VM_Mode fn_satp_to_VM_Mode (Bit #(64) satp_val); return satp_val [63:60]; endfunction
|
||||
function ASID fn_satp_to_ASID (Bit #(64) satp_val); return satp_val [59:44]; endfunction
|
||||
function PPN fn_satp_to_PPN (Bit #(64) satp_val); return satp_val [43: 0]; endfunction
|
||||
|
||||
Bit #(4) satp_mode_RV64_bare = 4'd__0;
|
||||
Bit #(4) satp_mode_RV64_sv39 = 4'd__8;
|
||||
Bit #(4) satp_mode_RV64_sv48 = 4'd__9;
|
||||
Bit #(4) satp_mode_RV64_sv57 = 4'd_10;
|
||||
Bit #(4) satp_mode_RV64_sv64 = 4'd_11;
|
||||
|
||||
`endif
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Virtual and Physical addresses, page numbers, offsets
|
||||
// Page table (PT) fields and entries (PTEs)
|
||||
// For Sv32 and Sv39
|
||||
|
||||
|
||||
// ----------------
|
||||
// RV32.Sv32
|
||||
|
||||
`ifdef RV32
|
||||
|
||||
// Virtual addrs
|
||||
typedef 32 VA_sz;
|
||||
typedef 20 VPN_sz;
|
||||
typedef 10 VPN_J_sz;
|
||||
|
||||
// Physical addrs
|
||||
typedef 34 PA_sz;
|
||||
typedef 22 PPN_sz;
|
||||
typedef 12 PPN_1_sz;
|
||||
typedef 10 PPN_0_sz;
|
||||
|
||||
// Offsets within a page
|
||||
typedef 12 Offset_sz;
|
||||
|
||||
// PTNodes (nodes in the page-table tree)
|
||||
typedef 1024 PTNode_sz; // # of PTEs in a PTNode
|
||||
|
||||
// VAs, VPN selectors
|
||||
function VA fn_mkVA (VPN_J vpn1, VPN_J vpn0, Bit #(Offset_sz) offset) = { vpn1, vpn0, offset };
|
||||
function VPN fn_Addr_to_VPN (Bit #(n) addr) = addr [31:12];
|
||||
function VPN_J fn_Addr_to_VPN_1 (Bit #(n) addr) = addr [31:22];
|
||||
function VPN_J fn_Addr_to_VPN_0 (Bit #(n) addr) = addr [21:12];
|
||||
|
||||
// ----------------
|
||||
// RV64.Sv39
|
||||
|
||||
// ifdef RV32
|
||||
`elsif RV64
|
||||
|
||||
// ----------------
|
||||
// RV64.Sv39
|
||||
|
||||
// ifdef RV32 .. elsif RV64
|
||||
`ifdef SV39
|
||||
|
||||
// Virtual addrs
|
||||
typedef 39 VA_sz;
|
||||
typedef 27 VPN_sz;
|
||||
typedef 9 VPN_J_sz;
|
||||
|
||||
// Physical addrs
|
||||
typedef 64 PA_sz; // need 56b in Sv39 mode and 64b in Bare mode
|
||||
typedef 44 PPN_sz;
|
||||
typedef 26 PPN_2_sz;
|
||||
typedef 9 PPN_1_sz;
|
||||
typedef 9 PPN_0_sz;
|
||||
|
||||
// Offsets within a page
|
||||
typedef 12 Offset_sz;
|
||||
|
||||
// PTNodes (nodes in the page-table tree)
|
||||
typedef 512 PTNode_sz; // # of PTEs in a PTNode
|
||||
|
||||
// VAs, VPN selectors
|
||||
function VA fn_mkVA (VPN_J vpn2, VPN_J vpn1, VPN_J vpn0, Bit #(Offset_sz) offset) = { vpn2, vpn1, vpn0, offset };
|
||||
function VPN fn_Addr_to_VPN (Bit #(n) addr) = addr [38:12];
|
||||
function VPN_J fn_Addr_to_VPN_2 (Bit #(n) addr) = addr [38:30];
|
||||
function VPN_J fn_Addr_to_VPN_1 (Bit #(n) addr) = addr [29:21];
|
||||
function VPN_J fn_Addr_to_VPN_0 (Bit #(n) addr) = addr [20:12];
|
||||
|
||||
// ifdef RV32 .. elsif RV64 / ifdef SV39
|
||||
`else
|
||||
|
||||
// TODO: RV64.SV48 definitions
|
||||
|
||||
// ifdef RV32 .. elsif RV64 / ifdef SV39 .. else
|
||||
`endif
|
||||
|
||||
// ifdef RV32 .. elsif RV64
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// Derived types and values
|
||||
|
||||
// Physical addrs
|
||||
Integer pa_sz = valueOf (PA_sz); typedef Bit #(PA_sz) PA;
|
||||
|
||||
function PA fn_WordXL_to_PA (WordXL eaddr);
|
||||
`ifdef RV32
|
||||
return extend (eaddr);
|
||||
`elsif RV64
|
||||
return truncate (eaddr);
|
||||
`endif
|
||||
endfunction
|
||||
|
||||
// Virtual addrs -- derived types and values
|
||||
Integer va_sz = valueOf (VA_sz); typedef Bit #(VA_sz) VA;
|
||||
|
||||
function VA fn_WordXL_to_VA (WordXL eaddr);
|
||||
`ifdef RV32
|
||||
return eaddr;
|
||||
`elsif RV64
|
||||
return truncate (eaddr);
|
||||
`endif
|
||||
endfunction
|
||||
|
||||
// Page offsets
|
||||
function Offset fn_Addr_to_Offset (Bit #(n) addr);
|
||||
return addr [offset_sz - 1: 0];
|
||||
endfunction
|
||||
|
||||
// VPNs
|
||||
Integer vpn_sz = valueOf (VPN_sz); typedef Bit #(VPN_sz) VPN;
|
||||
Integer vpn_j_sz = valueOf (VPN_J_sz); typedef Bit #(VPN_J_sz) VPN_J;
|
||||
Integer offset_sz = valueOf (Offset_sz); typedef Bit #(Offset_sz) Offset;
|
||||
|
||||
// PPNs
|
||||
Integer ppn_sz = valueOf (PPN_sz); typedef Bit #(PPN_sz) PPN;
|
||||
`ifdef RV64
|
||||
Integer ppn_2_sz = valueOf (PPN_2_sz); typedef Bit #(PPN_2_sz) PPN_2;
|
||||
`endif
|
||||
Integer ppn_1_sz = valueOf (PPN_1_sz); typedef Bit #(PPN_1_sz) PPN_1;
|
||||
Integer ppn_0_sz = valueOf (PPN_0_sz); typedef Bit #(PPN_0_sz) PPN_0;
|
||||
|
||||
`ifdef RV32
|
||||
typedef Bit #(PPN_1_sz) PPN_MEGA;
|
||||
`elsif RV64
|
||||
typedef Bit #(TAdd #(PPN_2_sz, PPN_1_sz)) PPN_MEGA;
|
||||
typedef Bit #(PPN_2_sz) PPN_GIGA;
|
||||
`endif
|
||||
|
||||
function PPN fn_PA_to_PPN (PA pa);
|
||||
return pa [ppn_sz + offset_sz - 1: offset_sz];
|
||||
endfunction
|
||||
|
||||
function PA fn_PPN_and_Offset_to_PA (PPN ppn, Offset offset);
|
||||
`ifdef RV32
|
||||
return {ppn, offset};
|
||||
`elsif RV64
|
||||
return zeroExtend ({ppn, offset});
|
||||
`endif
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// PTNodes (nodes in the page-table tree)
|
||||
|
||||
Integer ptnode_sz = valueOf (PTNode_sz); // # of PTEs in a PTNode
|
||||
typedef TLog #(PTNode_sz) PTNode_Index_sz;
|
||||
typedef Bit #(PTNode_Index_sz) PTNode_Index;
|
||||
Integer ptnode_index_sz = valueOf (PTNode_Index_sz);
|
||||
|
||||
// ----------------
|
||||
// PTEs (Page Table Entries in PTNodes)
|
||||
|
||||
typedef WordXL PTE;
|
||||
|
||||
Integer pte_V_offset = 0; // Valid
|
||||
Integer pte_R_offset = 1; // Read permission
|
||||
Integer pte_W_offset = 2; // Write permission
|
||||
Integer pte_X_offset = 3; // Execute permission
|
||||
Integer pte_U_offset = 4; // Accessible-to-user-mode
|
||||
Integer pte_G_offset = 5; // Global mapping
|
||||
Integer pte_A_offset = 6; // Accessed
|
||||
Integer pte_D_offset = 7; // Dirty
|
||||
Integer pte_RSW_offset = 8; // Reserved for supervisor SW
|
||||
|
||||
`ifdef RV32
|
||||
Integer pte_PPN_0_offset = 10;
|
||||
Integer pte_PPN_1_offset = 20;
|
||||
`elsif RV64
|
||||
Integer pte_PPN_0_offset = 10;
|
||||
Integer pte_PPN_1_offset = 19;
|
||||
Integer pte_PPN_2_offset = 28;
|
||||
`endif
|
||||
|
||||
function Bit #(1) fn_PTE_to_V (PTE pte);
|
||||
return pte [pte_V_offset];
|
||||
endfunction
|
||||
|
||||
function Bit #(1) fn_PTE_to_R (PTE pte);
|
||||
return pte [pte_R_offset];
|
||||
endfunction
|
||||
|
||||
function Bit #(1) fn_PTE_to_W (PTE pte);
|
||||
return pte [pte_W_offset];
|
||||
endfunction
|
||||
|
||||
function Bit #(1) fn_PTE_to_X (PTE pte);
|
||||
return pte [pte_X_offset];
|
||||
endfunction
|
||||
|
||||
function Bit #(1) fn_PTE_to_U (PTE pte);
|
||||
return pte [pte_U_offset];
|
||||
endfunction
|
||||
|
||||
function Bit #(1) fn_PTE_to_G (PTE pte);
|
||||
return pte [pte_G_offset];
|
||||
endfunction
|
||||
|
||||
function Bit #(1) fn_PTE_to_A (PTE pte);
|
||||
return pte [pte_A_offset];
|
||||
endfunction
|
||||
|
||||
function Bit #(1) fn_PTE_to_D (PTE pte);
|
||||
return pte [pte_D_offset];
|
||||
endfunction
|
||||
|
||||
function PPN fn_PTE_to_PPN (PTE pte);
|
||||
return pte [ppn_sz + pte_PPN_0_offset - 1 : pte_PPN_0_offset];
|
||||
endfunction
|
||||
|
||||
function PPN_MEGA fn_PTE_to_PPN_mega (PTE pte);
|
||||
return pte [ppn_sz + pte_PPN_0_offset - 1 : pte_PPN_1_offset];
|
||||
endfunction
|
||||
|
||||
`ifdef RV64
|
||||
function PPN_GIGA fn_PTE_to_PPN_giga (PTE pte);
|
||||
return pte [ppn_sz + pte_PPN_0_offset - 1 : pte_PPN_2_offset];
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
function PPN_0 fn_PTE_to_PPN_0 (PTE pte);
|
||||
return pte [pte_PPN_1_offset - 1 : pte_PPN_0_offset];
|
||||
endfunction
|
||||
|
||||
function PPN_1 fn_PTE_to_PPN_1 (PTE pte);
|
||||
return pte [ppn_1_sz + pte_PPN_1_offset - 1 : pte_PPN_1_offset];
|
||||
endfunction
|
||||
|
||||
`ifdef RV64
|
||||
function PPN_2 fn_PTE_to_PPN_2 (PTE pte);
|
||||
return pte [ppn_2_sz + pte_PPN_2_offset - 1 : pte_PPN_2_offset];
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
// ----------------
|
||||
// Check if a PTE is invalid (V bit clear, or improper R/W bits)
|
||||
|
||||
function Bool is_invalid_pte (PTE pte);
|
||||
return ( (fn_PTE_to_V (pte) == 0)
|
||||
|| ( (fn_PTE_to_R (pte) == 0)
|
||||
&& (fn_PTE_to_W (pte) == 1)));
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// Check if PTE bits deny a virtual-mem access
|
||||
|
||||
function Bool is_pte_denial (Bool dmem_not_imem, // load-store or fetch?
|
||||
Bool read_not_write,
|
||||
Priv_Mode priv,
|
||||
Bit #(1) sstatus_SUM,
|
||||
Bit #(1) mstatus_MXR,
|
||||
PTE pte);
|
||||
|
||||
let pte_u = fn_PTE_to_U (pte);
|
||||
let pte_x = fn_PTE_to_X (pte);
|
||||
let pte_w = fn_PTE_to_W (pte);
|
||||
let pte_r = fn_PTE_to_R (pte);
|
||||
|
||||
Bool priv_deny = ( ((priv == u_Priv_Mode) && (pte_u == 1'b0))
|
||||
|| ((priv == s_Priv_Mode) && (pte_u == 1'b1) && (sstatus_SUM == 1'b0)));
|
||||
|
||||
Bool access_fetch = ((! dmem_not_imem) && read_not_write);
|
||||
Bool access_load = (dmem_not_imem && read_not_write);
|
||||
Bool access_store = (dmem_not_imem && (! read_not_write));
|
||||
|
||||
let pte_r_mxr = (pte_r | (mstatus_MXR & pte_x));
|
||||
|
||||
Bool access_ok = ( (access_fetch && (pte_x == 1'b1))
|
||||
|| (access_load && (pte_r_mxr == 1'b1))
|
||||
|| (access_store && (pte_w == 1'b1)));
|
||||
|
||||
|
||||
return (priv_deny || (! access_ok));
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// Check PTE A and D bits
|
||||
|
||||
function Bool is_pte_A_D_fault (Bool read_not_write, PTE pte);
|
||||
return ( (fn_PTE_to_A (pte) == 0)
|
||||
|| ((! read_not_write) && (fn_PTE_to_D (pte) == 0)));
|
||||
endfunction
|
||||
|
||||
// ----------------
|
||||
// Choose particular kind of page fault
|
||||
|
||||
function Exc_Code fn_page_fault_exc_code (Bool dmem_not_imem, Bool read_not_write);
|
||||
return ((! dmem_not_imem) ? exc_code_INSTR_PAGE_FAULT
|
||||
:(read_not_write ? exc_code_LOAD_PAGE_FAULT
|
||||
: exc_code_STORE_AMO_PAGE_FAULT));
|
||||
endfunction
|
||||
|
||||
`else // ifdef ISA_PRIV_S
|
||||
// The below definitions are valid for cases where there is no VM
|
||||
// Physical addrs -- without VM, PA is same as WordXL
|
||||
typedef XLEN PA_sz;
|
||||
|
||||
// Physical addrs
|
||||
Integer pa_sz = valueOf (PA_sz); typedef Bit #(PA_sz) PA;
|
||||
|
||||
function PA fn_WordXL_to_PA (WordXL eaddr);
|
||||
return eaddr;
|
||||
endfunction
|
||||
|
||||
`endif // else-ifdef ISA_PRIV_S
|
||||
|
||||
// ----------------
|
||||
// Choose particular kind of access fault
|
||||
|
||||
function Exc_Code fn_access_exc_code (Bool dmem_not_imem, Bool read_not_write);
|
||||
return ((! dmem_not_imem) ? exc_code_INSTR_ACCESS_FAULT
|
||||
:(read_not_write ? exc_code_LOAD_ACCESS_FAULT
|
||||
: exc_code_STORE_AMO_ACCESS_FAULT));
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
362
src_Core/ISA/TV_Info.bsv
Normal file
362
src_Core/ISA/TV_Info.bsv
Normal file
@@ -0,0 +1,362 @@
|
||||
// Copyright (c) 2013-2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
// ================================================================
|
||||
// Definition of Tandem Verifier Packets.
|
||||
// The CPU sends out such a packet for each instruction retired.
|
||||
// A Tandem Verifier contains a "golden model" simulator of the RISC-V
|
||||
// ISA, and verifies that the information in the packet is correct,
|
||||
// instruction by instruction.
|
||||
|
||||
// ================================================================
|
||||
|
||||
package TV_Info;
|
||||
|
||||
// ================================================================
|
||||
// Bluespec library imports
|
||||
|
||||
import DefaultValue :: *;
|
||||
import Vector :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import ISA_Decls :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
typedef enum {// These are not from instruction flow and do not have a PC or instruction
|
||||
TRACE_RESET,
|
||||
TRACE_GPR_WRITE,
|
||||
TRACE_FPR_WRITE,
|
||||
TRACE_CSR_WRITE,
|
||||
TRACE_MEM_WRITE,
|
||||
|
||||
// These are from instruction flow and have a PC and instruction
|
||||
TRACE_OTHER,
|
||||
TRACE_I_RD, TRACE_F_RD,
|
||||
TRACE_I_LOAD, TRACE_F_LOAD,
|
||||
TRACE_STORE,
|
||||
TRACE_AMO,
|
||||
TRACE_TRAP,
|
||||
TRACE_RET,
|
||||
TRACE_CSRRX,
|
||||
|
||||
// These are from an interrupt and has a PC but no instruction
|
||||
TRACE_INTR
|
||||
} Trace_Op
|
||||
deriving (Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Trace_Op op;
|
||||
WordXL pc;
|
||||
ISize instr_sz;
|
||||
Bit #(32) instr;
|
||||
RegName rd;
|
||||
WordXL word1;
|
||||
WordXL word2;
|
||||
Bit #(64) word3; // Wider than WordXL because can contain paddr (in RV32, paddr can be 34 bits)
|
||||
WordXL word4;
|
||||
} Trace_Data
|
||||
deriving (Bits);
|
||||
|
||||
// RESET
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x
|
||||
function Trace_Data mkTrace_RESET ();
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_RESET;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// GPR_WRITE
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x rdval
|
||||
function Trace_Data mkTrace_GPR_WRITE (RegName rd, WordXL rdval);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_GPR_WRITE;
|
||||
td.rd = rd;
|
||||
td.word1 = rdval;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// FPR_WRITE
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x rdval
|
||||
function Trace_Data mkTrace_FPR_WRITE (RegName rd, WordXL rdval);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_FPR_WRITE;
|
||||
td.rd = rd;
|
||||
td.word1 = rdval;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// CSR_WRITE
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x csraddr csrval
|
||||
function Trace_Data mkTrace_CSR_WRITE (CSR_Addr csraddr, WordXL csrval);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_CSR_WRITE;
|
||||
td.word3 = zeroExtend (csraddr);
|
||||
td.word4 = csrval;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// MEM_WRITE
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x sz stval paddr
|
||||
function Trace_Data mkTrace_MEM_WRITE (MemReqSize sz, WordXL stval, Bit #(64) paddr);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_MEM_WRITE;
|
||||
td.word1 = zeroExtend (sz);
|
||||
td.word2 = stval;
|
||||
td.word3 = paddr;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// OTHER
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x x x
|
||||
function Trace_Data mkTrace_OTHER (WordXL pc, ISize isize, Bit #(32) instr);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_OTHER;
|
||||
td.pc = pc;
|
||||
td.instr_sz = isize;
|
||||
td.instr = instr;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// I_RD
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x x x x rdval
|
||||
function Trace_Data mkTrace_I_RD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordXL rdval);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_I_RD;
|
||||
td.pc = pc;
|
||||
td.instr_sz = isize;
|
||||
td.instr = instr;
|
||||
td.rd = rd;
|
||||
td.word1 = rdval;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// F_RD
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x x x x rdval
|
||||
function Trace_Data mkTrace_F_RD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordXL rdval);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_F_RD;
|
||||
td.pc = pc;
|
||||
td.instr_sz = isize;
|
||||
td.instr = instr;
|
||||
td.rd = rd;
|
||||
td.word1 = rdval;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// I_LOAD
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x x x x rdval eaddr
|
||||
function Trace_Data mkTrace_I_LOAD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordXL rdval, WordXL eaddr);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_I_LOAD;
|
||||
td.pc = pc;
|
||||
td.instr_sz = isize;
|
||||
td.instr = instr;
|
||||
td.rd = rd;
|
||||
td.word1 = rdval;
|
||||
td.word3 = zeroExtend (eaddr);
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// F_LOAD
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x x x x rdval eaddr
|
||||
function Trace_Data mkTrace_F_LOAD (WordXL pc, ISize isize, Bit #(32) instr, RegName rd, WordXL rdval, WordXL eaddr);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_F_LOAD;
|
||||
td.pc = pc;
|
||||
td.instr_sz = isize;
|
||||
td.instr = instr;
|
||||
td.rd = rd;
|
||||
td.word1 = rdval;
|
||||
td.word3 = zeroExtend (eaddr);
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// STORE
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x x x stval eaddr
|
||||
function Trace_Data mkTrace_STORE (WordXL pc, ISize isize, Bit #(32) instr, WordXL stval, WordXL eaddr);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_STORE;
|
||||
td.pc = pc;
|
||||
td.instr_sz = isize;
|
||||
td.instr = instr;
|
||||
td.word2 = stval;
|
||||
td.word3 = zeroExtend (eaddr);
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// AMO
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x x x x rdval stval eaddr
|
||||
function Trace_Data mkTrace_AMO (WordXL pc, ISize isize, Bit #(32) instr,
|
||||
RegName rd, WordXL rdval, WordXL stval, WordXL eaddr);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_AMO;
|
||||
td.pc = pc;
|
||||
td.instr_sz = isize;
|
||||
td.instr = instr;
|
||||
td.rd = rd;
|
||||
td.word1 = rdval;
|
||||
td.word2 = stval;
|
||||
td.word3 = zeroExtend (eaddr);
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// TRAP
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x x x priv mstatus mcause mepc mtval
|
||||
function Trace_Data mkTrace_TRAP (WordXL pc, ISize isize, Bit #(32) instr,
|
||||
Priv_Mode priv, WordXL mstatus, WordXL mcause, WordXL mepc, WordXL mtval);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_TRAP;
|
||||
td.pc = pc;
|
||||
td.instr_sz = isize;
|
||||
td.instr = instr;
|
||||
td.rd = zeroExtend (priv);
|
||||
td.word1 = mstatus;
|
||||
td.word2 = mcause;
|
||||
td.word3 = zeroExtend (mepc);
|
||||
td.word4 = mtval;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// RET
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x x x priv mstatus
|
||||
function Trace_Data mkTrace_RET (WordXL pc, ISize isize, Bit #(32) instr, Priv_Mode priv, WordXL mstatus);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_RET;
|
||||
td.pc = pc;
|
||||
td.instr_sz = isize;
|
||||
td.instr = instr;
|
||||
td.rd = zeroExtend (priv);
|
||||
td.word1 = mstatus;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// CSRRX
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x x x x rdval csrvalid csraddr csrval
|
||||
function Trace_Data mkTrace_CSRRX (WordXL pc, ISize isize, Bit #(32) instr,
|
||||
RegName rd, WordXL rdval, Bool csrvalid, CSR_Addr csraddr, WordXL csrval);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_CSRRX;
|
||||
td.pc = pc;
|
||||
td.instr_sz = isize;
|
||||
td.instr = instr;
|
||||
td.rd = rd;
|
||||
td.word1 = rdval;
|
||||
td.word2 = (csrvalid ? 1 : 0);
|
||||
td.word3 = zeroExtend (csraddr);
|
||||
td.word4 = csrval;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// INTR
|
||||
// op pc instr_sz instr rd word1 word2 word3 word4
|
||||
// x x priv mstatus mcause mepc mtval
|
||||
function Trace_Data mkTrace_INTR (WordXL pc,
|
||||
Priv_Mode priv, WordXL mstatus, WordXL mcause, WordXL mepc, WordXL mtval);
|
||||
Trace_Data td = ?;
|
||||
td.op = TRACE_INTR;
|
||||
td.pc = pc;
|
||||
td.rd = zeroExtend (priv);
|
||||
td.word1 = mstatus;
|
||||
td.word2 = mcause;
|
||||
td.word3 = zeroExtend (mepc);
|
||||
td.word4 = mtval;
|
||||
return td;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Display of Trace_Data for debugging
|
||||
|
||||
instance FShow #(Trace_Data);
|
||||
function Fmt fshow (Trace_Data td);
|
||||
Fmt fmt = $format ("Trace_Data{", fshow (td.op));
|
||||
|
||||
if (td.op == TRACE_RESET) begin
|
||||
end
|
||||
|
||||
else if ((td.op == TRACE_GPR_WRITE) || (td.op == TRACE_FPR_WRITE))
|
||||
fmt = fmt + $format (" rd %0d rdval %0h", td.rd, td.word1);
|
||||
|
||||
else if (td.op == TRACE_CSR_WRITE)
|
||||
fmt = fmt + $format (" csraddr %0h csrval %0h", td.word3, td.word4);
|
||||
|
||||
else if (td.op == TRACE_MEM_WRITE)
|
||||
fmt = fmt + $format (" sz %0d stval %0h paddr %0h", td.word1, td.word2, td.word3);
|
||||
|
||||
else begin
|
||||
fmt = fmt + $format (" pc %0h", td.pc);
|
||||
|
||||
if (td.op != TRACE_INTR)
|
||||
fmt = fmt + $format (" instr.%0d %0h:", pack (td.instr_sz), td.instr);
|
||||
|
||||
if ((td.op == TRACE_I_RD) || (td.op == TRACE_F_RD))
|
||||
fmt = fmt + $format (" rd %0d rdval %0h", td.rd, td.word1);
|
||||
|
||||
else if ((td.op == TRACE_I_LOAD) || (td.op == TRACE_F_LOAD))
|
||||
fmt = fmt + $format (" rd %0d rdval %0h eaddr %0h",
|
||||
td.rd, td.word1, td.word3);
|
||||
|
||||
else if (td.op == TRACE_STORE)
|
||||
fmt = fmt + $format (" stval %0h eaddr %0h", td.word2, td.word3);
|
||||
|
||||
else if (td.op == TRACE_AMO)
|
||||
fmt = fmt + $format (" rd %0d rdval %0h stval %0h eaddr %0h",
|
||||
td.rd, td.word1, td.word2, td.word3);
|
||||
|
||||
else if (td.op == TRACE_CSRRX)
|
||||
fmt = fmt + $format (" rd %0d rdval %0h csraddr %0h csrval %0h",
|
||||
td.rd, td.word1, td.word3, td.word4);
|
||||
|
||||
else if ((td.op == TRACE_TRAP) || (td.op == TRACE_INTR))
|
||||
fmt = fmt + $format (" priv %0d mstatus %0h mcause %0h mepc %0h mtval %0h",
|
||||
td.rd, td.word1, td.word2, td.word3, td.word4);
|
||||
|
||||
else if (td.op == TRACE_RET)
|
||||
fmt = fmt + $format (" priv %0d mstatus %0h", td.rd, td.word1);
|
||||
end
|
||||
|
||||
fmt = fmt + $format ("}");
|
||||
return fmt;
|
||||
endfunction
|
||||
endinstance
|
||||
|
||||
// ================================================================
|
||||
// Trace_Data is encoded in module mkTV_Encode into vectors of bytes,
|
||||
// which are eventually streamed out to an on-line tandem verifier/
|
||||
// analyzer (or to a file for off-line tandem-verification/analysis).
|
||||
|
||||
// Various 'transactions' produce a Trace_Data struct (e.g., reset,
|
||||
// each instruction retirement, each GDB write to registers or memory,
|
||||
// etc.). Each struct is encoded into a vector of bytes; the number
|
||||
// of bytes depends on the kind of transaction and various encoding
|
||||
// choices.
|
||||
|
||||
typedef 72 TV_VB_SIZE; // max bytes needed for each transaction
|
||||
typedef Vector #(TV_VB_SIZE, Byte) TV_Vec_Bytes;
|
||||
|
||||
// ================================================================
|
||||
|
||||
typedef struct {
|
||||
Bit #(32) num_bytes;
|
||||
TV_Vec_Bytes vec_bytes;
|
||||
} Info_CPU_to_Verifier deriving (Bits, FShow);
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
41
src_Core/PLIC/Makefile
Normal file
41
src_Core/PLIC/Makefile
Normal file
@@ -0,0 +1,41 @@
|
||||
# Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
# Makefile to create Test_PLIC and PLIC into a standalone unit-test executable.
|
||||
|
||||
# Edit the StmtFSM in Test_PLIC to create different tests.
|
||||
|
||||
# ----------------
|
||||
|
||||
REPO = $(HOME)/GitHub/Flute
|
||||
|
||||
# ----------------
|
||||
# Top-level file and module
|
||||
|
||||
TOPFILE ?= $(REPO)/src_Core/PLIC/Test_PLIC.bsv
|
||||
TOPMODULE ?= mkTest_PLIC
|
||||
|
||||
# ================================================================
|
||||
# RISC-V config macros passed into Bluespec 'bsc' compiler
|
||||
|
||||
BSC_COMPILATION_FLAGS += \
|
||||
-D RV64 \
|
||||
-D ISA_PRIV_M -D ISA_PRIV_U -D ISA_PRIV_S \
|
||||
-D SV39 \
|
||||
-D ISA_I -D ISA_M -D ISA_A \
|
||||
-D SHIFT_BARREL \
|
||||
-D MULT_SYNTH \
|
||||
-D Near_Mem_Caches \
|
||||
-D FABRIC64 \
|
||||
|
||||
|
||||
# ================================================================
|
||||
# Common boilerplate rules
|
||||
|
||||
include $(REPO)/builds/Resources/Include_Common.mk
|
||||
|
||||
# ================================================================
|
||||
# Makefile rules for building for specific simulator: bluesim
|
||||
|
||||
include $(REPO)/builds/Resources/Include_bluesim.mk
|
||||
|
||||
# ================================================================
|
||||
623
src_Core/PLIC/PLIC.bsv
Normal file
623
src_Core/PLIC/PLIC.bsv
Normal file
@@ -0,0 +1,623 @@
|
||||
// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package PLIC;
|
||||
|
||||
// ================================================================
|
||||
// This package implements a PLIC (Platform-Level Interrupt Controller)
|
||||
// conforming to the RISC-V PLIC standard.
|
||||
// It is parameterized for:
|
||||
// - # of sources
|
||||
// - # of targets
|
||||
// - # of priorities
|
||||
//
|
||||
// ================================================================
|
||||
// Bluespec lib imports
|
||||
|
||||
import ConfigReg :: *;
|
||||
import Vector :: *;
|
||||
import FIFOF :: *;
|
||||
import ClientServer :: *;
|
||||
import Assert :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import GetPut_Aux :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import AXI4_Fabric :: *;
|
||||
import Fabric_Defs :: *; // for Wd_Id, Wd_Addr, Wd_Data, Wd_User
|
||||
|
||||
// ================================================================
|
||||
// Change bitwidth without requiring < or > constraints.
|
||||
|
||||
function Bit #(m) changeWidth (Bit #(n) x);
|
||||
Bit #(TAdd #(m, n)) y = zeroExtend (x);
|
||||
Bit #(m) z = y [valueOf (m)-1 : 0];
|
||||
return z;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Maximum supported sources, targets, ...
|
||||
|
||||
typedef 10 T_wd_source_id; // Max 1024 sources (source 0 is reserved for 'no interrupt')
|
||||
typedef 5 T_wd_target_id; // Max 32 targets
|
||||
|
||||
// ================================================================
|
||||
// Interfaces
|
||||
|
||||
// ----------------
|
||||
// Individual source interface
|
||||
|
||||
interface PLIC_Source_IFC;
|
||||
(* always_ready, always_enabled *)
|
||||
method Action m_interrupt_req (Bool set_not_clear);
|
||||
endinterface
|
||||
|
||||
// ----------------
|
||||
// Individual target interface
|
||||
|
||||
interface PLIC_Target_IFC;
|
||||
(* always_ready *)
|
||||
method Bool m_eip; // external interrupt pending
|
||||
endinterface
|
||||
|
||||
// ----------------
|
||||
// PLIC interface
|
||||
|
||||
interface PLIC_IFC #(numeric type t_n_external_sources,
|
||||
numeric type t_n_targets,
|
||||
numeric type t_max_priority);
|
||||
// Debugging
|
||||
method Action set_verbosity (Bit #(4) verbosity);
|
||||
method Action show_PLIC_state;
|
||||
|
||||
// Reset
|
||||
interface Server #(Bit #(0), Bit #(0)) server_reset;
|
||||
|
||||
// set_addr_map should be called after this module's reset
|
||||
method Action set_addr_map (Bit #(64) addr_base, Bit #(64) addr_lim);
|
||||
|
||||
// Memory-mapped access
|
||||
interface AXI4_Slave_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) axi4_slave;
|
||||
|
||||
// sources
|
||||
interface Vector #(t_n_external_sources, PLIC_Source_IFC) v_sources;
|
||||
|
||||
// targets EIPs (External Interrupt Pending)
|
||||
interface Vector #(t_n_targets, PLIC_Target_IFC) v_targets;
|
||||
endinterface
|
||||
|
||||
// ================================================================
|
||||
// PLIC module implementation
|
||||
|
||||
module mkPLIC (PLIC_IFC #(t_n_external_sources, t_n_targets, t_max_priority))
|
||||
provisos (Add #(1, t_n_external_sources, t_n_sources), // source 0 is reserved for 'no source'
|
||||
Add #(_any_0, TLog #(t_n_sources), T_wd_source_id),
|
||||
Add #(_any_1, TLog #(t_n_targets), T_wd_target_id),
|
||||
Log #(TAdd #(t_max_priority, 1), t_wd_priority));
|
||||
|
||||
Reg #(Bit #(4)) cfg_verbosity <- mkConfigReg (0);
|
||||
|
||||
// Source_Ids and Priorities are read and written over the memory interface
|
||||
// and should fit within the data bus width, currently 64 bits.
|
||||
staticAssert ((valueOf (TLog #(t_n_sources)) <= 64), "PLIC: t_n_sources parameter too large");
|
||||
staticAssert ((valueOf (TLog #(TAdd #(t_max_priority, 1))) <= 64), "PLIC: t_max_priority parameter too large");
|
||||
|
||||
Integer n_sources = valueOf (t_n_sources);
|
||||
Integer n_targets = valueOf (t_n_targets);
|
||||
|
||||
// ----------------
|
||||
// Soft reset requests and responses
|
||||
FIFOF #(Bit #(0)) f_reset_reqs <- mkFIFOF;
|
||||
FIFOF #(Bit #(0)) f_reset_rsps <- mkFIFOF;
|
||||
|
||||
// ----------------
|
||||
// Memory-mapped access
|
||||
|
||||
// Base and limit addrs for this memory-mapped block.
|
||||
Reg #(Bit #(64)) rg_addr_base <- mkRegU;
|
||||
Reg #(Bit #(64)) rg_addr_lim <- mkRegU;
|
||||
|
||||
// Connector to AXI4 fabric
|
||||
AXI4_Slave_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) slave_xactor <- mkAXI4_Slave_Xactor;
|
||||
|
||||
// ----------------
|
||||
// Per-interrupt source state
|
||||
|
||||
// Interrupt pending from source
|
||||
Vector #(t_n_sources, Reg #(Bool)) vrg_source_ip <- replicateM (mkConfigReg (False));
|
||||
// Interrupt claimed and being serviced by a hart
|
||||
Vector #(t_n_sources, Reg #(Bool)) vrg_source_busy <- replicateM (mkReg (False));
|
||||
// Priority for this source
|
||||
Vector #(t_n_sources, Reg #(Bit #(t_wd_priority))) vrg_source_prio <- replicateM (mkReg (0));
|
||||
|
||||
// ----------------
|
||||
// Per-target hart context state
|
||||
|
||||
// Threshold: interrupts at or below threshold should be masked out for target
|
||||
Vector #(t_n_targets, Reg #(Bit #(t_wd_priority))) vrg_target_threshold <- replicateM (mkReg ('1));
|
||||
// Target has claimed interrupt for source and is servicing it
|
||||
Vector #(t_n_targets, Reg #(Bit #(TLog #(t_n_sources)))) vrg_servicing_source <- replicateM (mkReg (0));
|
||||
|
||||
// ----------------
|
||||
// Per-target, per-source state
|
||||
|
||||
// Interrupt enables from source to target
|
||||
Vector #(t_n_targets,
|
||||
Vector #(t_n_sources, Reg #(Bool))) vvrg_ie <- replicateM (replicateM (mkReg (False)));
|
||||
|
||||
// ================================================================
|
||||
// Compute outputs for each target (combinational)
|
||||
|
||||
function Tuple2 #(Bit #(t_wd_priority), Bit #(TLog #(t_n_sources)))
|
||||
fn_target_max_prio_and_max_id (Bit #(T_wd_target_id) target_id);
|
||||
|
||||
Bit #(t_wd_priority) max_prio = 0;
|
||||
Bit #(TLog #(t_n_sources)) max_id = 0;
|
||||
|
||||
// Note: source_ids begin at 1, not 0.
|
||||
for (Integer source_id = 1; source_id < n_sources; source_id = source_id + 1)
|
||||
if ( vrg_source_ip [source_id]
|
||||
&& (vrg_source_prio [source_id] > max_prio)
|
||||
&& (vvrg_ie [target_id][source_id])) begin
|
||||
max_id = fromInteger (source_id);
|
||||
max_prio = vrg_source_prio [source_id];
|
||||
end
|
||||
// Assert: if any interrupt is pending (max_id > 0), then prio > 0
|
||||
return tuple2 (max_prio, max_id);
|
||||
endfunction
|
||||
|
||||
function Action fa_show_PLIC_state;
|
||||
action
|
||||
$display ("----------------");
|
||||
$write ("Src IPs :");
|
||||
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
|
||||
$write (" %0d", pack (vrg_source_ip [source_id]));
|
||||
$display ("");
|
||||
|
||||
$write ("Src Prios:");
|
||||
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
|
||||
$write (" %0d", vrg_source_prio [source_id]);
|
||||
$display ("");
|
||||
|
||||
$write ("Src busy :");
|
||||
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
|
||||
$write (" %0d", pack (vrg_source_busy [source_id]));
|
||||
$display ("");
|
||||
|
||||
for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1) begin
|
||||
$write ("T %0d IEs :", target_id);
|
||||
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
|
||||
$write (" %0d", vvrg_ie [target_id][source_id]);
|
||||
match { .max_prio, .max_id } = fn_target_max_prio_and_max_id (fromInteger (target_id));
|
||||
$display (" MaxPri %0d, Thresh %0d, MaxId %0d, Svcing %0d",
|
||||
max_prio, vrg_target_threshold [target_id], max_id, vrg_servicing_source [target_id]);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Soft reset
|
||||
|
||||
rule rl_reset;
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.rl_reset", cur_cycle);
|
||||
|
||||
let x <- pop (f_reset_reqs);
|
||||
|
||||
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1) begin
|
||||
vrg_source_ip [source_id] <= False;
|
||||
vrg_source_busy [source_id] <= False;
|
||||
vrg_source_prio [source_id] <= 0;
|
||||
end
|
||||
|
||||
for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1) begin
|
||||
// Mask all interrupts with highest threshold
|
||||
vrg_target_threshold [target_id] <= '1;
|
||||
vrg_servicing_source [target_id] <= 0;
|
||||
end
|
||||
|
||||
for (Integer target_id = 0; target_id < n_targets; target_id = target_id + 1)
|
||||
for (Integer source_id = 0; source_id < n_sources; source_id = source_id + 1)
|
||||
vvrg_ie [target_id][source_id] <= False;
|
||||
|
||||
slave_xactor.reset;
|
||||
|
||||
f_reset_rsps.enq (?);
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
// Bus interface for reading/writing control/status regs
|
||||
// Relative-address map is same as 'SiFive U54-MC Core Complex Manual v1p0'.
|
||||
// Accesses are 4-bytes wide, even though bus may be 64b wide.
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Handle memory-mapped read requests
|
||||
|
||||
rule rl_process_rd_req (! f_reset_reqs.notEmpty);
|
||||
|
||||
let rda <- pop_o (slave_xactor.o_rd_addr);
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: PLIC.rl_process_rd_req:", cur_cycle);
|
||||
$display (" ", fshow (rda));
|
||||
end
|
||||
|
||||
let addr_offset = rda.araddr - rg_addr_base;
|
||||
Bit #(64) rdata = 0;
|
||||
AXI4_Resp rresp = axi4_resp_okay;
|
||||
|
||||
if (rda.araddr < rg_addr_base) begin
|
||||
// Technically this should not happen: the fabric should
|
||||
// never have delivered such an addr to this IP.
|
||||
$display ("%0d: ERROR: PLIC.rl_process_rd_req: unrecognized addr", cur_cycle);
|
||||
$display (" ", fshow (rda));
|
||||
rresp = axi4_resp_decerr;
|
||||
end
|
||||
|
||||
// Source Priority
|
||||
else if (addr_offset < 'h1000) begin
|
||||
Bit #(T_wd_source_id) source_id = truncate (addr_offset [11:2]);
|
||||
if ((0 < source_id) && (source_id <= fromInteger (n_sources - 1))) begin
|
||||
rdata = changeWidth (vrg_source_prio [source_id]);
|
||||
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.rl_process_rd_req: reading Source Priority: source %0d = 0x%0h",
|
||||
cur_cycle, source_id, rdata);
|
||||
end
|
||||
else
|
||||
rresp = axi4_resp_slverr;
|
||||
end
|
||||
|
||||
// Source IPs (interrupt pending).
|
||||
// Return 32 consecutive IP bits starting with addr.
|
||||
else if (('h1000 <= addr_offset) && (addr_offset < 'h2000)) begin
|
||||
Bit #(T_wd_source_id) source_id_base = truncate ({ addr_offset [11:0], 5'h0 });
|
||||
|
||||
function Bool fn_ip_source_id (Integer source_id_offset);
|
||||
let source_id = source_id_base + fromInteger (source_id_offset);
|
||||
Bool ip_source_id = ( (source_id <= fromInteger (n_sources - 1))
|
||||
? vrg_source_ip [source_id]
|
||||
: False);
|
||||
return ip_source_id;
|
||||
endfunction
|
||||
|
||||
if (source_id_base <= fromInteger (n_sources - 1)) begin
|
||||
Bit #(32) v_ip = pack (genWith (fn_ip_source_id));
|
||||
rdata = changeWidth (v_ip);
|
||||
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.rl_process_rd_req: reading Intr Pending 32 bits from source %0d = 0x%0h",
|
||||
cur_cycle, source_id_base, rdata);
|
||||
end
|
||||
else
|
||||
rresp = axi4_resp_slverr;
|
||||
end
|
||||
|
||||
// Source IEs (interrupt enables) for a target
|
||||
// Return 32 consecutive IE bits starting with addr.
|
||||
// Target 0 addrs: 2000-207F, Target 1 addrs: 2080-20FF, ...
|
||||
else if (('h2000 <= addr_offset) && (addr_offset < 'h3000)) begin
|
||||
Bit #(T_wd_target_id) target_id = truncate (addr_offset [11:7]);
|
||||
Bit #(T_wd_source_id) source_id_base = truncate ({ addr_offset [6:0], 5'h0 });
|
||||
|
||||
function Bool fn_ie_source_id (Integer source_id_offset);
|
||||
let source_id = fromInteger (source_id_offset) + source_id_base;
|
||||
return ( (source_id <= fromInteger (n_sources - 1))
|
||||
? vvrg_ie [target_id][source_id]
|
||||
: False);
|
||||
endfunction
|
||||
|
||||
if ( (source_id_base <= fromInteger (n_sources - 1))
|
||||
&& (target_id <= fromInteger (n_targets - 1))) begin
|
||||
Bit #(32) v_ie = pack (genWith (fn_ie_source_id));
|
||||
rdata = changeWidth (v_ie);
|
||||
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.rl_process_rd_req: reading Intr Enable 32 bits from source %0d = 0x%0h",
|
||||
cur_cycle, source_id_base, rdata);
|
||||
end
|
||||
else
|
||||
rresp = axi4_resp_slverr;
|
||||
end
|
||||
|
||||
// Target threshold
|
||||
else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0000) begin
|
||||
Bit #(T_wd_target_id) target_id = truncate (addr_offset [20:12]);
|
||||
if (target_id <= fromInteger (n_targets - 1)) begin
|
||||
rdata = changeWidth (vrg_target_threshold [target_id]);
|
||||
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.rl_process_rd_req: reading Threshold for target %0d = 0x%0h",
|
||||
cur_cycle, target_id, rdata);
|
||||
end
|
||||
else
|
||||
rresp = axi4_resp_slverr;
|
||||
end
|
||||
|
||||
// Interrupt service claim by target
|
||||
else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0004) begin
|
||||
Bit #(T_wd_target_id) target_id = truncate (addr_offset [20:12]);
|
||||
match { .max_prio, .max_id } = fn_target_max_prio_and_max_id (target_id);
|
||||
Bool eip = (max_prio > vrg_target_threshold [target_id]);
|
||||
if (target_id <= fromInteger (n_targets - 1)) begin
|
||||
if (vrg_servicing_source [target_id] != 0) begin
|
||||
$display ("%0d: ERROR: PLIC: target %0d claiming without prior completion",
|
||||
cur_cycle, target_id);
|
||||
$display (" Still servicing interrupt from source %0d", vrg_servicing_source [target_id]);
|
||||
$display (" Trying to claim service for source %0d", max_id);
|
||||
$display (" Ignoring.");
|
||||
rresp = axi4_resp_slverr;
|
||||
end
|
||||
else begin
|
||||
if (max_id != 0) begin
|
||||
vrg_source_ip [max_id] <= False;
|
||||
vrg_source_busy [max_id] <= True;
|
||||
vrg_servicing_source [target_id] <= truncate (max_id);
|
||||
rdata = changeWidth (max_id);
|
||||
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.rl_process_rd_req: reading Claim for target %0d = 0x%0h",
|
||||
cur_cycle, target_id, rdata);
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
rresp = axi4_resp_slverr;
|
||||
end
|
||||
|
||||
else
|
||||
rresp = axi4_resp_slverr;
|
||||
|
||||
if (rresp != axi4_resp_okay) begin
|
||||
$display ("%0d: ERROR: PLIC.rl_process_rd_req: unrecognized addr", cur_cycle);
|
||||
$display (" ", fshow (rda));
|
||||
end
|
||||
|
||||
if ((valueOf (Wd_Data) == 64) && ((addr_offset & 'h7) == 'h4))
|
||||
rdata = { rdata [31:0], 32'h0 };
|
||||
|
||||
// Send read-response to bus
|
||||
Fabric_Data x = truncate (rdata);
|
||||
let rdr = AXI4_Rd_Data {rid: rda.arid,
|
||||
rdata: x,
|
||||
rresp: rresp,
|
||||
rlast: True,
|
||||
ruser: rda.aruser};
|
||||
slave_xactor.i_rd_data.enq (rdr);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: PLIC.rl_process_rd_req", cur_cycle);
|
||||
$display (" ", fshow (rda));
|
||||
$display (" ", fshow (rdr));
|
||||
end
|
||||
endrule: rl_process_rd_req
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Handle memory-mapped write requests
|
||||
|
||||
(* descending_urgency = "rl_process_rd_req, rl_process_wr_req" *) // Ad hoc order
|
||||
|
||||
rule rl_process_wr_req (! f_reset_reqs.notEmpty);
|
||||
|
||||
let wra <- pop_o (slave_xactor.o_wr_addr);
|
||||
let wrd <- pop_o (slave_xactor.o_wr_data);
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: PLIC.rl_process_wr_req", cur_cycle);
|
||||
$display (" ", fshow (wra));
|
||||
$display (" ", fshow (wrd));
|
||||
end
|
||||
|
||||
let addr_offset = wra.awaddr - rg_addr_base;
|
||||
let wdata32 = (((valueOf (Wd_Data) == 64) && ((addr_offset & 'h7) == 'h4))
|
||||
? wrd.wdata [63:32]
|
||||
: wrd.wdata [31:0]);
|
||||
let bresp = axi4_resp_okay;
|
||||
|
||||
if (wra.awaddr < rg_addr_base) begin
|
||||
// Technically this should not happen: the fabric should
|
||||
// never have delivered such an addr to this IP.
|
||||
$display ("%0d: ERROR: PLIC.rl_process_wr_req: unrecognized addr", cur_cycle);
|
||||
$display (" ", fshow (wra));
|
||||
$display (" ", fshow (wrd));
|
||||
bresp = axi4_resp_decerr;
|
||||
end
|
||||
|
||||
// Source priority
|
||||
else if (addr_offset < 'h1000) begin
|
||||
Bit #(T_wd_source_id) source_id = truncate (addr_offset [11:2]);
|
||||
if ((0 < source_id) && (source_id <= fromInteger (n_sources - 1))) begin
|
||||
vrg_source_prio [source_id] <= changeWidth (wdata32);
|
||||
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.rl_process_wr_req: writing Source Priority: source %0d = 0x%0h",
|
||||
cur_cycle, source_id, wdata32);
|
||||
end
|
||||
else begin
|
||||
// Note: write to source_id 0 is error; should it just be ignored?
|
||||
bresp = axi4_resp_slverr;
|
||||
end
|
||||
end
|
||||
|
||||
// Source IPs (interrupt pending).
|
||||
// Read-only, so ignore write; just check that addr ok.
|
||||
else if (('h1000 <= addr_offset) && (addr_offset < 'h2000)) begin
|
||||
Bit #(T_wd_source_id) source_id_base = truncate ({ addr_offset [11:0], 5'h0 });
|
||||
|
||||
if (source_id_base <= fromInteger (n_sources - 1)) begin
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.rl_process_wr_req: Ignoring write to Read-only Intr Pending 32 bits from source %0d",
|
||||
cur_cycle, source_id_base);
|
||||
end
|
||||
else
|
||||
bresp = axi4_resp_slverr;
|
||||
end
|
||||
|
||||
// Source IEs (interrupt enables) for a target
|
||||
// Write 32 consecutive IE bits starting with addr.
|
||||
// Target 0 addrs: 2000-207F, Target 1 addrs: 2080-20FF, ...
|
||||
else if (('h2000 <= addr_offset) && (addr_offset < 'h3000)) begin
|
||||
Bit #(T_wd_target_id) target_id = truncate (addr_offset [11:7]);
|
||||
Bit #(T_wd_source_id) source_id_base = truncate ({ addr_offset [6:0], 5'h0 });
|
||||
|
||||
if ( (source_id_base <= fromInteger (n_sources - 1))
|
||||
&& (target_id <= fromInteger (n_targets - 1))) begin
|
||||
for (Bit #(T_wd_source_id) k = 0; k < 32; k = k + 1) begin
|
||||
Bit #(T_wd_source_id) source_id = source_id_base + k;
|
||||
if (source_id <= fromInteger (n_sources - 1))
|
||||
vvrg_ie [target_id][source_id] <= unpack (wdata32 [k]);
|
||||
end
|
||||
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.rl_process_wr_req: writing Intr Enable 32 bits for target %0d from source %0d = 0x%0h",
|
||||
cur_cycle, target_id, source_id_base, wdata32);
|
||||
end
|
||||
else
|
||||
bresp = axi4_resp_slverr;
|
||||
end
|
||||
|
||||
// Target threshold
|
||||
else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0000) begin
|
||||
Bit #(T_wd_target_id) target_id = truncate (addr_offset [20:12]);
|
||||
if (target_id <= fromInteger (n_targets - 1)) begin
|
||||
vrg_target_threshold [target_id] <= changeWidth (wdata32);
|
||||
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.rl_process_wr_req: writing threshold for target %0d = 0x%0h",
|
||||
cur_cycle, target_id, wdata32);
|
||||
end
|
||||
else
|
||||
bresp = axi4_resp_slverr;
|
||||
end
|
||||
|
||||
// Interrupt service completion by target
|
||||
// Actual memory-write-data is irrelevant.
|
||||
else if ((addr_offset [31:0] & 32'hFFFF_0FFF) == 32'h0020_0004) begin
|
||||
Bit #(T_wd_target_id) target_id = truncate (addr_offset [20:12]);
|
||||
Bit #(T_wd_source_id) source_id = zeroExtend (vrg_servicing_source [target_id]);
|
||||
|
||||
if (target_id <= fromInteger (n_targets - 1)) begin
|
||||
if (vrg_source_busy [source_id]) begin
|
||||
vrg_source_busy [source_id] <= False;
|
||||
vrg_servicing_source [target_id] <= 0;
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.rl_process_wr_req: writing completion for target %0d for source 0x%0h",
|
||||
cur_cycle, target_id, source_id);
|
||||
end
|
||||
else begin
|
||||
$display ("%0d: ERROR: PLIC: interrupt completion to source that is not being serviced",
|
||||
cur_cycle);
|
||||
$display (" Completion message from target %0d to source %0d", target_id, source_id);
|
||||
$display (" Ignoring");
|
||||
bresp = axi4_resp_slverr;
|
||||
end
|
||||
end
|
||||
else
|
||||
bresp = axi4_resp_slverr;
|
||||
end
|
||||
|
||||
else
|
||||
bresp = axi4_resp_slverr;
|
||||
|
||||
if (bresp != axi4_resp_okay) begin
|
||||
$display ("%0d: ERROR: PLIC.rl_process_wr_req: unrecognized addr", cur_cycle);
|
||||
$display (" ", fshow (wra));
|
||||
$display (" ", fshow (wrd));
|
||||
end
|
||||
|
||||
// Send write-response to bus
|
||||
let wrr = AXI4_Wr_Resp {bid: wra.awid,
|
||||
bresp: bresp,
|
||||
buser: wra.awuser};
|
||||
slave_xactor.i_wr_resp.enq (wrr);
|
||||
|
||||
if (cfg_verbosity > 1) begin
|
||||
$display ("%0d: PLIC.AXI4.rl_process_wr_req", cur_cycle);
|
||||
$display (" ", fshow (wra));
|
||||
$display (" ", fshow (wrd));
|
||||
$display (" ", fshow (wrr));
|
||||
end
|
||||
endrule: rl_process_wr_req
|
||||
|
||||
// ================================================================
|
||||
// Creator of each source interface
|
||||
|
||||
function PLIC_Source_IFC fn_mk_PLIC_Source_IFC (Integer source_id);
|
||||
return interface PLIC_Source_IFC;
|
||||
method Action m_interrupt_req (Bool set_not_clear);
|
||||
action
|
||||
if (! vrg_source_busy [source_id]) begin
|
||||
vrg_source_ip [source_id] <= set_not_clear;
|
||||
|
||||
if ((cfg_verbosity > 0) && (vrg_source_ip [source_id] != set_not_clear))
|
||||
$display ("%0d: Changing vrg_source_ip [%0d] to %0d",
|
||||
cur_cycle, source_id, pack (set_not_clear));
|
||||
end
|
||||
endaction
|
||||
endmethod
|
||||
endinterface;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Creator of each target interface
|
||||
|
||||
function PLIC_Target_IFC fn_mk_PLIC_Target_IFC (Integer target_id);
|
||||
return interface PLIC_Target_IFC;
|
||||
method Bool m_eip; // external interrupt pending
|
||||
match { .max_prio, .max_id } = fn_target_max_prio_and_max_id (fromInteger (target_id));
|
||||
Bool eip = (max_prio > vrg_target_threshold [target_id]);
|
||||
return eip;
|
||||
endmethod
|
||||
endinterface;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// INTERFACE
|
||||
|
||||
// Debugging
|
||||
method Action set_verbosity (Bit #(4) verbosity);
|
||||
cfg_verbosity <= verbosity;
|
||||
endmethod
|
||||
|
||||
method Action show_PLIC_state;
|
||||
fa_show_PLIC_state;
|
||||
endmethod
|
||||
|
||||
// Reset
|
||||
interface server_reset = toGPServer (f_reset_reqs, f_reset_rsps);
|
||||
|
||||
// set_addr_map should be called after this module's reset
|
||||
method Action set_addr_map (Bit #(64) addr_base, Bit #(64) addr_lim);
|
||||
if (addr_base [1:0] != 0)
|
||||
$display ("%0d: WARNING: PLIC.set_addr_map: addr_base 0x%0h is not 4-Byte-aligned",
|
||||
cur_cycle, addr_base);
|
||||
|
||||
if (addr_lim [1:0] != 0)
|
||||
$display ("%0d: WARNING: PLIC.set_addr_map: addr_lim 0x%0h is not 4-Byte-aligned",
|
||||
cur_cycle, addr_lim);
|
||||
|
||||
rg_addr_base <= addr_base;
|
||||
rg_addr_lim <= addr_lim;
|
||||
|
||||
if (cfg_verbosity > 0)
|
||||
$display ("%0d: PLIC.set_addr_map: base 0x%0h limit 0x%0h", cur_cycle, addr_base, addr_lim);
|
||||
endmethod
|
||||
|
||||
// Memory-mapped access
|
||||
interface axi4_slave = slave_xactor.axi_side;
|
||||
|
||||
// sources
|
||||
interface v_sources = genWith (fn_mk_PLIC_Source_IFC);
|
||||
|
||||
// targets
|
||||
interface v_targets = genWith (fn_mk_PLIC_Target_IFC);
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
42
src_Core/PLIC/PLIC_16_2_7.bsv
Normal file
42
src_Core/PLIC/PLIC_16_2_7.bsv
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package PLIC_16_2_7;
|
||||
|
||||
// ================================================================
|
||||
// Instantiation of parameterized PLIC to specific parameter values.
|
||||
//
|
||||
// ================================================================
|
||||
// Bluespec lib imports
|
||||
|
||||
// None
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
// None
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import SoC_Map :: *; // For N_External_Interrupt_Sources
|
||||
import PLIC :: *; // For PLIC_IFC, mkPLIC
|
||||
|
||||
// ================================================================
|
||||
// PLIC for this core
|
||||
|
||||
typedef 2 PLIC_N_Targets;
|
||||
typedef 7 PLIC_Max_Priority;
|
||||
|
||||
typedef PLIC_IFC #(N_External_Interrupt_Sources,
|
||||
PLIC_N_Targets,
|
||||
PLIC_Max_Priority) PLIC_IFC_16_2_7;
|
||||
|
||||
(* synthesize *)
|
||||
module mkPLIC_16_2_7 (PLIC_IFC_16_2_7);
|
||||
let m <- mkPLIC;
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
134
src_Core/PLIC/README_PLIC.txt
Normal file
134
src_Core/PLIC/README_PLIC.txt
Normal file
@@ -0,0 +1,134 @@
|
||||
The (relative) Memory map for this PLIC follows the one given in:
|
||||
|
||||
SiFive U54-MC Core Complex Manual, v1p0, Oct 4 2017
|
||||
Chapter 8 Platform Level Interrupt Controller, pp.32-38.
|
||||
|
||||
>----------------
|
||||
Priority registers
|
||||
|
||||
0x0000 Reserved
|
||||
0x0004 Source 1 priority
|
||||
0x0008 Source 2 priority
|
||||
...
|
||||
0x0800 Source 511 priority
|
||||
|
||||
>----------------
|
||||
Reserved
|
||||
|
||||
0x0804 ... 0x0FFF
|
||||
|
||||
>----------------
|
||||
IP (Interrupt Pending) array: 32 sources per 32b word
|
||||
|
||||
0x1000
|
||||
...
|
||||
0x103C
|
||||
|
||||
>----------------
|
||||
Reserved
|
||||
|
||||
0x1018 ... 0x1FFF
|
||||
|
||||
>----------------
|
||||
IE (interrupt enables) array for Hart0 M mode (1 bit per source)
|
||||
|
||||
0x2000
|
||||
...
|
||||
0x2014
|
||||
|
||||
>----------------
|
||||
Reserved
|
||||
|
||||
0x2018 ... 0x207F
|
||||
|
||||
>----------------
|
||||
IE (interrupt enables) array for Hart1 M mode (1 bit per source)
|
||||
|
||||
0x2080
|
||||
...
|
||||
0x2094
|
||||
|
||||
>----------------
|
||||
IE (interrupt enables) array for Hart1 S mode (1 bit per source)
|
||||
|
||||
0x2100
|
||||
...
|
||||
0x2114
|
||||
|
||||
>----------------
|
||||
IE (interrupt enables) array for Hart2 M mode (1 bit per source)
|
||||
|
||||
0x2180
|
||||
...
|
||||
0x2194
|
||||
|
||||
>----------------
|
||||
IE (interrupt enables) array for Hart2 S mode (1 bit per source)
|
||||
|
||||
0x2200
|
||||
...
|
||||
0x2214
|
||||
|
||||
>----------------
|
||||
IE (interrupt enables) array for Hart3 M mode (1 bit per source)
|
||||
|
||||
0x2280
|
||||
...
|
||||
0x2294
|
||||
|
||||
>----------------
|
||||
IE (interrupt enables) array for Hart3 S mode (1 bit per source)
|
||||
|
||||
0x2300
|
||||
...
|
||||
0x2314
|
||||
|
||||
>----------------
|
||||
IE (interrupt enables) array for Hart4 M mode (1 bit per source)
|
||||
|
||||
0x2380
|
||||
...
|
||||
0x2394
|
||||
|
||||
>----------------
|
||||
IE (interrupt enables) array for Hart4 S mode (1 bit per source)
|
||||
|
||||
0x2400
|
||||
...
|
||||
0x2414
|
||||
|
||||
>----------------
|
||||
Reserved
|
||||
|
||||
0x0C00 2480 ... 0x0C1F FFFF
|
||||
|
||||
>----------------
|
||||
Threshold (of priority) and Claim/Complete registers
|
||||
|
||||
0x0C20 0000 Hart 0 M-mode
|
||||
0x0C20 0004 Hart 0 M-mode claim/complete
|
||||
>----------------
|
||||
0x0C20 1000 Hart 1 M-mode
|
||||
0x0C20 1004 Hart 1 M-mode claim/complete
|
||||
>----------------
|
||||
0x0C20 2000 Hart 1 S-mode
|
||||
0x0C20 2004 Hart 1 S-mode claim/complete
|
||||
>----------------
|
||||
0x0C20 3000 Hart 2 M-mode
|
||||
0x0C20 3004 Hart 2 M-mode claim/complete
|
||||
>----------------
|
||||
0x0C20 4000 Hart 2 S-mode
|
||||
0x0C20 4004 Hart 2 S-mode claim/complete
|
||||
>----------------
|
||||
0x0C20 5000 Hart 3 M-mode
|
||||
0x0C20 5004 Hart 3 M-mode claim/complete
|
||||
>----------------
|
||||
0x0C20 6000 Hart 3 S-mode
|
||||
0x0C20 6004 Hart 3 S-mode claim/complete
|
||||
>----------------
|
||||
0x0C20 7000 Hart 4 M-mode
|
||||
0x0C20 7004 Hart 4 M-mode claim/complete
|
||||
>----------------
|
||||
0x0C20 8000 Hart 4 S-mode
|
||||
0x0C20 8004 Hart 4 S-mode claim/complete
|
||||
>----------------
|
||||
296
src_Core/PLIC/Test_PLIC.bsv
Normal file
296
src_Core/PLIC/Test_PLIC.bsv
Normal file
@@ -0,0 +1,296 @@
|
||||
// Copyright (c) 2019 Bluespec, Inc. All Rights Reserved
|
||||
|
||||
package Test_PLIC;
|
||||
|
||||
// ================================================================
|
||||
// Standalone unit-test testbench for PLIC.
|
||||
//
|
||||
// ================================================================
|
||||
// Bluespec lib imports
|
||||
|
||||
import ConfigReg :: *;
|
||||
import Vector :: *;
|
||||
import FIFOF :: *;
|
||||
import GetPut :: *;
|
||||
import ClientServer :: *;
|
||||
import Connectable :: *;
|
||||
import Assert :: *;
|
||||
import StmtFSM :: *;
|
||||
|
||||
// ----------------
|
||||
// BSV additional libs
|
||||
|
||||
import Cur_Cycle :: *;
|
||||
import GetPut_Aux :: *;
|
||||
import Semi_FIFOF :: *;
|
||||
|
||||
// ================================================================
|
||||
// Project imports
|
||||
|
||||
import AXI4_Types :: *;
|
||||
import AXI4_Fabric :: *;
|
||||
import Fabric_Defs :: *; // for Wd_Id, Wd_Addr, Wd_Data, Wd_User
|
||||
import SoC_Map :: *;
|
||||
import PLIC :: *;
|
||||
import PLIC_16_2_7 :: *;
|
||||
|
||||
// ================================================================
|
||||
|
||||
Integer n_external_interrupt_sources = valueOf (N_External_Interrupt_Sources);
|
||||
typedef TAdd #(1, N_External_Interrupt_Sources) N_Interrupt_Sources;
|
||||
typedef Bit #(TLog #(N_Interrupt_Sources)) Source_Id;
|
||||
|
||||
Integer plic_n_targets = valueOf (PLIC_N_Targets);
|
||||
Integer target_0 = 0;
|
||||
Integer target_1 = 1;
|
||||
|
||||
Integer plic_max_priority = valueOf (PLIC_Max_Priority);
|
||||
typedef Bit #(TLog #(TAdd #(1, PLIC_Max_Priority))) Priority;
|
||||
|
||||
// ================================================================
|
||||
// The Core module
|
||||
|
||||
(* synthesize *)
|
||||
module mkTest_PLIC (Empty);
|
||||
|
||||
// System address map
|
||||
SoC_Map_IFC soc_map <- mkSoC_Map;
|
||||
|
||||
// PLIC (Platform-Level Interrupt Controller)
|
||||
PLIC_IFC_16_2_7 plic <- mkPLIC_16_2_7;
|
||||
|
||||
// Master transactor through which to read/write PLIC regs
|
||||
AXI4_Master_Xactor_IFC #(Wd_Id, Wd_Addr, Wd_Data, Wd_User) master_xactor <- mkAXI4_Master_Xactor;
|
||||
|
||||
Vector #(N_Interrupt_Sources, Reg #(Bool)) vrg_irqs <- replicateM (mkReg (False));
|
||||
|
||||
// ================================================================
|
||||
// AXI4 interactions
|
||||
|
||||
FIFOF #(Fabric_Addr) f_read_addr <- mkFIFOF;
|
||||
FIFOF #(Fabric_Data) f_read_data <- mkFIFOF;
|
||||
|
||||
function Action fa_read_req (Integer addr);
|
||||
action
|
||||
let fabric_addr = soc_map.m_plic_addr_base + fromInteger (addr);
|
||||
|
||||
let mem_req_rd_addr = AXI4_Rd_Addr {arid: fabric_default_id,
|
||||
araddr: fabric_addr,
|
||||
arlen: 0, // burst len = arlen+1
|
||||
arsize: zeroExtend (axsize_4),
|
||||
arburst: fabric_default_burst,
|
||||
arlock: fabric_default_lock,
|
||||
arcache: fabric_default_arcache,
|
||||
arprot: fabric_default_prot,
|
||||
arqos: fabric_default_qos,
|
||||
arregion: fabric_default_region,
|
||||
aruser: fabric_default_user};
|
||||
master_xactor.i_rd_addr.enq (mem_req_rd_addr);
|
||||
f_read_addr.enq (fabric_addr);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
function Action fa_read_rsp;
|
||||
action
|
||||
let fabric_addr <- pop (f_read_addr);
|
||||
let rd_data <- pop_o (master_xactor.o_rd_data);
|
||||
if (rd_data.rresp != axi4_resp_okay) begin
|
||||
$display ("ERROR: fa_read_rsp: fabric response error");
|
||||
$display (" ", fshow (rd_data));
|
||||
end
|
||||
let x = (((valueOf (Wd_Data) == 64) && ((fabric_addr & 'h7) == 'h4))
|
||||
? (rd_data.rdata >> 32)
|
||||
: rd_data.rdata);
|
||||
f_read_data.enq (x);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
function Action fa_write_req (Integer addr, Fabric_Data data);
|
||||
action
|
||||
let fabric_addr = soc_map.m_plic_addr_base + fromInteger (addr);
|
||||
|
||||
let mem_req_wr_addr = AXI4_Wr_Addr {awid: fabric_default_id,
|
||||
awaddr: fabric_addr,
|
||||
awlen: 0, // burst len = awlen+1
|
||||
awsize: zeroExtend (axsize_4),
|
||||
awburst: fabric_default_burst,
|
||||
awlock: fabric_default_lock,
|
||||
awcache: fabric_default_awcache,
|
||||
awprot: fabric_default_prot,
|
||||
awqos: fabric_default_qos,
|
||||
awregion: fabric_default_region,
|
||||
awuser: fabric_default_user};
|
||||
|
||||
let x = (((valueOf (Wd_Data) == 64) && ((fabric_addr & 'h7) == 'h4))
|
||||
? (data << 32)
|
||||
: data);
|
||||
let mem_req_wr_data = AXI4_Wr_Data {wid: fabric_default_id,
|
||||
wdata: x,
|
||||
wstrb: '1,
|
||||
wlast: True,
|
||||
wuser: fabric_default_user};
|
||||
master_xactor.i_wr_addr.enq (mem_req_wr_addr);
|
||||
master_xactor.i_wr_data.enq (mem_req_wr_data);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
function Action fa_write_rsp;
|
||||
action
|
||||
let wr_resp <- pop_o (master_xactor.o_wr_resp);
|
||||
|
||||
if (wr_resp.bresp != axi4_resp_okay) begin
|
||||
$display ("ERROR: rl_discard_write_rsp: fabric response error");
|
||||
$display (" ", fshow (wr_resp));
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// Help functions to interact with PLIC at an "API" level
|
||||
|
||||
function Action fa_print_plic_eips ();
|
||||
action
|
||||
$write ("PLIC.v_target eip =");
|
||||
$write (" ", fshow (plic.v_targets [0].m_eip));
|
||||
$write (" ", fshow (plic.v_targets [1].m_eip));
|
||||
$display ("");
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
function Stmt fstmt_set_source_priority (Integer src, Priority prio);
|
||||
return seq
|
||||
fa_write_req (src * 4, zeroExtend (prio));
|
||||
fa_write_rsp;
|
||||
endseq;
|
||||
endfunction
|
||||
|
||||
function Stmt fstmt_set_target_ies (Integer target, Bit #(32) ies);
|
||||
return seq
|
||||
fa_write_req ('h2000 + (target * 'h80), zeroExtend (ies));
|
||||
fa_write_rsp;
|
||||
endseq;
|
||||
endfunction
|
||||
|
||||
function Stmt fstmt_set_target_threshold (Integer target, Priority threshold);
|
||||
return seq
|
||||
fa_write_req ('h20_0000 + (target * 'h1000), zeroExtend (threshold));
|
||||
fa_write_rsp;
|
||||
endseq;
|
||||
endfunction
|
||||
|
||||
function Stmt fstmt_claim (Integer target);
|
||||
return seq
|
||||
fa_read_req ('h20_0004 + (target * 'h1000));
|
||||
fa_read_rsp;
|
||||
action
|
||||
let x <- pop (f_read_data);
|
||||
$display ("fstmt_claim: PLIC returned %0d", x);
|
||||
endaction
|
||||
endseq;
|
||||
endfunction
|
||||
|
||||
function Stmt fstmt_complete (Integer target, Source_Id source_id);
|
||||
return seq
|
||||
fa_write_req ('h20_0004 + (target * 'h1000), zeroExtend (source_id));
|
||||
fa_write_rsp;
|
||||
endseq;
|
||||
endfunction
|
||||
|
||||
// ================================================================
|
||||
// BEHAVIOR
|
||||
|
||||
mkConnection (master_xactor.axi_side, plic.axi4_slave);
|
||||
|
||||
// Drive all interrupt requests from local regs
|
||||
for (Integer j = 0; j < n_external_interrupt_sources; j = j + 1)
|
||||
rule rl_drive_irq;
|
||||
plic.v_sources [j].m_interrupt_req (vrg_irqs [j]);
|
||||
endrule
|
||||
|
||||
// ================================================================
|
||||
|
||||
Stmt init = seq
|
||||
action
|
||||
$display ("Initializing PLIC");
|
||||
plic.server_reset.request.put (?);
|
||||
endaction
|
||||
action
|
||||
let rsp <- plic.server_reset.response.get;
|
||||
plic.set_addr_map (zeroExtend (soc_map.m_plic_addr_base),
|
||||
zeroExtend (soc_map.m_plic_addr_lim));
|
||||
endaction
|
||||
fstmt_set_source_priority (1, 0);
|
||||
fstmt_set_source_priority (2, 0);
|
||||
fstmt_set_source_priority (3, 0);
|
||||
fstmt_set_source_priority (4, 0);
|
||||
|
||||
fstmt_set_source_priority (5, 0);
|
||||
fstmt_set_source_priority (6, 0);
|
||||
fstmt_set_source_priority (7, 0);
|
||||
fstmt_set_source_priority (8, 0);
|
||||
|
||||
fstmt_set_source_priority (9, 0);
|
||||
fstmt_set_source_priority (10, 0);
|
||||
fstmt_set_source_priority (11, 0);
|
||||
fstmt_set_source_priority (12, 0);
|
||||
|
||||
fstmt_set_source_priority (13, 0);
|
||||
fstmt_set_source_priority (14, 0);
|
||||
fstmt_set_source_priority (15, 0);
|
||||
fstmt_set_source_priority (16, 0);
|
||||
|
||||
fstmt_set_target_ies (target_0, 0);
|
||||
fstmt_set_target_ies (target_1, 0);
|
||||
|
||||
fstmt_set_target_threshold (target_0, 7);
|
||||
fstmt_set_target_threshold (target_1, 7);
|
||||
delay (5);
|
||||
$display ("Finished Initializing PLIC");
|
||||
endseq;
|
||||
|
||||
Stmt test1 = seq
|
||||
$display (">---------------- TEST 1");
|
||||
plic.set_verbosity (1);
|
||||
fstmt_set_source_priority (5, 4);
|
||||
|
||||
fstmt_set_target_ies (target_0, 'b10_0000); // bit 5
|
||||
fstmt_set_target_threshold (target_0, 4);
|
||||
|
||||
fstmt_set_target_ies (target_1, 'b10_0000); // bit 5
|
||||
fstmt_set_target_threshold (target_1, 2);
|
||||
|
||||
plic.show_PLIC_state;
|
||||
fa_print_plic_eips;
|
||||
|
||||
vrg_irqs [5] <= True;
|
||||
delay (2);
|
||||
plic.show_PLIC_state;
|
||||
fa_print_plic_eips;
|
||||
|
||||
fstmt_set_target_threshold (target_0, 3);
|
||||
plic.show_PLIC_state;
|
||||
fa_print_plic_eips;
|
||||
|
||||
fstmt_claim (target_1);
|
||||
plic.show_PLIC_state;
|
||||
fa_print_plic_eips;
|
||||
|
||||
fstmt_complete (target_1, 5);
|
||||
plic.show_PLIC_state;
|
||||
fa_print_plic_eips;
|
||||
endseq;
|
||||
|
||||
// ================================================================
|
||||
|
||||
mkAutoFSM (seq
|
||||
init;
|
||||
test1;
|
||||
$finish (0);
|
||||
endseq);
|
||||
|
||||
|
||||
endmodule
|
||||
|
||||
// ================================================================
|
||||
|
||||
endpackage
|
||||
21
src_Core/RISCY_OOO/LICENSE_RISCY-OOO
Normal file
21
src_Core/RISCY_OOO/LICENSE_RISCY-OOO
Normal file
@@ -0,0 +1,21 @@
|
||||
Copyright (c) 2017, 2018 Massachusetts Institute of Technology
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
153
src_Core/RISCY_OOO/Makefile
Normal file
153
src_Core/RISCY_OOO/Makefile
Normal file
@@ -0,0 +1,153 @@
|
||||
# This Makefile copies source BSV files from the MIT riscy-OOO repo
|
||||
# for use in the Bluespec environment
|
||||
|
||||
RISCY_HOME = ~/Projects/RISCV/MIT-riscy/riscy-OOO
|
||||
|
||||
copyfiles: copy_RV64_OOO_files copy_procs_lib_files copy_coherence_src_files copy_fpgautils_files copy_connectal_files
|
||||
|
||||
PROCS_RV64G_OOO = procs/RV64G_OOO
|
||||
.PHONY: copy_RV64_OOO_files
|
||||
copy_RV64_OOO_files:
|
||||
mkdir -p $(PROCS_RV64G_OOO)
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/AluExePipeline.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/CommitStage.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/FetchStage.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/FpuMulDivExePipeline.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/MemExePipeline.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ProcConfig.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/RenameStage.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReorderBufferSynth.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReservationStationAlu.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReservationStationFpuMulDiv.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ReservationStationMem.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/RFileSynth.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/ScoreboardSynth.bsv ./$(PROCS_RV64G_OOO)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_RV64G_OOO)/SynthParam.bsv ./$(PROCS_RV64G_OOO)/
|
||||
|
||||
PROCS_LIB = procs/lib
|
||||
.PHONY: copy_procs_lib_files
|
||||
copy_procs_lib_files:
|
||||
mkdir -p $(PROCS_LIB)
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Amo.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Bht.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/BrPred.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Btb.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Bypass.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/CacheUtils.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ConcatReg.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Decode.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/DirPredictor.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/DTlb.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Ehr.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/EpochManager.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Exec.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Fifo.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Fpu.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/FullAssocTlb.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GlobalBrHistReg.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GlobalSpecUpdate.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GSelectPred.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/GSharePred.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/HasSpecBits.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ITlb.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L1CoCache.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L1LLConnect.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L2SetAssocTlb.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/L2Tlb.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LatencyTimer.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LLCache.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LLCDmaConnect.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/LLCRqMshrSecureModel.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MemLoader.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MemLoaderIF.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MemoryTypes.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MMIOAddrs.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MMIOCore.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MMIOInst.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MsgFifo.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/MulDiv.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Performance.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/PhysRFile.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ProcTypes.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Ras.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/RenameDebugIF.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/RenamingTable.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ReorderBuffer.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/ReservationStationEhr.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SafeCounter.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Scoreboard.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SetAssocTlb.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SpecFifo.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SpecPoisonFifo.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SpecTagManager.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/SplitLSQ.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/StoreBuffer.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TlbConnect.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TlbTypes.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TourPred.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TourPredSecure.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/TranslationCache.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/Types.bsv ./$(PROCS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(PROCS_LIB)/VerificationPacket.bsv ./$(PROCS_LIB)/
|
||||
|
||||
COHERENCE_SRC = coherence/src
|
||||
.PHONY: copy_coherence_src_files
|
||||
copy_coherence_src_files:
|
||||
mkdir -p $(COHERENCE_SRC)
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/CCPipe.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/CCTypes.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/CrossBar.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/IBank.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/ICRqMshr.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/IPRqMshr.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1Bank.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1CRqMshr.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1Pipe.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/L1PRqMshr.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/LLBank.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/LLCRqMshr.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/LLPipe.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/MshrDeadlockChecker.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/RandomReplace.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/RWBramCore.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvIBank.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvIPipe.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvL1Bank.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvL1Pipe.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvLLBank.bsv ./$(COHERENCE_SRC)/
|
||||
cp -p $(RISCY_HOME)/$(COHERENCE_SRC)/SelfInvLLPipe.bsv ./$(COHERENCE_SRC)/
|
||||
|
||||
FPGAUTILS_LIB = fpgautils/lib
|
||||
FPGAUTILS_XILINX_FPU = fpgautils/xilinx/fpu
|
||||
FPGAUTILS_XILINX_RESET_REGS = fpgautils/xilinx/reset_regs
|
||||
.PHONY: copy_fpgautils_files
|
||||
copy_fpgautils_files:
|
||||
mkdir -p $(FPGAUTILS_LIB)
|
||||
mkdir -p $(FPGAUTILS_XILINX_FPU)
|
||||
mkdir -p $(FPGAUTILS_XILINX_RESET_REGS)
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/DramCommon.bsv ./$(FPGAUTILS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/ResetGuard.bsv ./$(FPGAUTILS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/SyncFifo.bsv ./$(FPGAUTILS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxFpu.bsv ./$(FPGAUTILS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxIntDiv.bsv ./$(FPGAUTILS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxIntMul.bsv ./$(FPGAUTILS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/XilinxSyncFifo.bsv ./$(FPGAUTILS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_LIB)/WaitAutoReset.bsv ./$(FPGAUTILS_LIB)/
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_FPU)/fp_sqrt_sim.v ./$(FPGAUTILS_XILINX_FPU)/
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_FPU)/fp_fma_sim.v ./$(FPGAUTILS_XILINX_FPU)/
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_FPU)/fp_div_sim.v ./$(FPGAUTILS_XILINX_FPU)/
|
||||
cp -p $(RISCY_HOME)/$(FPGAUTILS_XILINX_RESET_REGS)/reset_guard.v ./$(FPGAUTILS_XILINX_RESET_REGS)/
|
||||
|
||||
CONNECTAL_BSV = connectal/bsv
|
||||
CONNECTAL_LIB_BSV = connectal/lib/bsv
|
||||
CONNECTAL_TESTS_SPI = connectal/tests/spi
|
||||
.PHONY: copy_connectal_files
|
||||
copy_connectal_files:
|
||||
mkdir -p $(CONNECTAL_BSV)
|
||||
mkdir -p $(CONNECTAL_LIB_BSV)
|
||||
mkdir -p $(CONNECTAL_TESTS_SPI)
|
||||
cp -p $(RISCY_HOME)/$(CONNECTAL_BSV)/ConnectalBramFifo.bsv ./$(CONNECTAL_BSV)/
|
||||
cp -p $(RISCY_HOME)/$(CONNECTAL_BSV)/ConnectalClocks.bsv ./$(CONNECTAL_BSV)/
|
||||
cp -p $(RISCY_HOME)/$(CONNECTAL_LIB_BSV)/Arith.bsv ./$(CONNECTAL_LIB_BSV)/
|
||||
cp -p $(RISCY_HOME)/$(CONNECTAL_TESTS_SPI)/ConnectalProjectConfig.bsv ./$(CONNECTAL_TESTS_SPI)/
|
||||
tree .
|
||||
408
src_Core/RISCY_OOO/coherence/src/CCPipe.bsv
Normal file
408
src_Core/RISCY_OOO/coherence/src/CCPipe.bsv
Normal file
@@ -0,0 +1,408 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Ehr::*;
|
||||
import Fifo::*;
|
||||
import Vector::*;
|
||||
import RWBramCore::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
|
||||
// general type param ordering: way < index < tag < msi < dir < owner < other < rep < line < pipeCmd
|
||||
|
||||
typedef union tagged {
|
||||
void Invalid;
|
||||
msiT DownDir; // cRs downgraded toState
|
||||
msiT UpCs; // pRs upgraded toState
|
||||
} RespState#(type msiT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
pipeCmdT cmd;
|
||||
// tag match & ram output
|
||||
wayT way;
|
||||
Bool pRqMiss; // pRq miss, valid only if go through tag match
|
||||
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) ram;
|
||||
// replace info, actually not needed, just output for debug purposes
|
||||
repT repInfo;
|
||||
} PipeOut#(
|
||||
type wayT,
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type repT,
|
||||
type lineT,
|
||||
type pipeCmdT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface CCPipe#(
|
||||
numeric type wayNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type repT,
|
||||
type lineT,
|
||||
type pipeCmdT
|
||||
);
|
||||
method Action enq(pipeCmdT cmd, Maybe#(lineT) respLine, RespState#(msiT) toState);
|
||||
method Bool notFull;
|
||||
method PipeOut#(Bit#(TLog#(wayNum)), tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT) first;
|
||||
method PipeOut#(Bit#(TLog#(wayNum)), tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT) unguard_first;
|
||||
method Bool notEmpty;
|
||||
method Action deqWrite(
|
||||
Maybe#(pipeCmdT) newCmd,
|
||||
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) wrRam,
|
||||
Bool updateRep // update replacement info
|
||||
);
|
||||
// empty signal when we need to flush self-invalidate cache
|
||||
method Bool emptyForFlush;
|
||||
endinterface
|
||||
|
||||
// internal pipeline reg types
|
||||
// three stages
|
||||
// 1: enq
|
||||
// 2: tag match, read data and dir
|
||||
// 3: output
|
||||
|
||||
typedef struct {
|
||||
pipeCmdT cmd;
|
||||
// bypasses
|
||||
Vector#(wayNum, Maybe#(CacheInfo#(tagT, msiT, dirT, ownerT, otherT))) infoVec;
|
||||
Maybe#(repT) repInfo; // replacement info for the whole set
|
||||
// CRs/PRs info
|
||||
Maybe#(lineT) respLine;
|
||||
RespState#(msiT) toState;
|
||||
} Enq2Match#(
|
||||
numeric type wayNum,
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type repT,
|
||||
type lineT,
|
||||
type pipeCmdT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
pipeCmdT cmd;
|
||||
// tag match results
|
||||
wayT way;
|
||||
Bool pRqMiss;
|
||||
// RAM outputs
|
||||
// cs is merged with PRs toState
|
||||
// dir is merged with CRs toState
|
||||
CacheInfo#(tagT, msiT, dirT, ownerT, otherT) info;
|
||||
repT repInfo;
|
||||
// bypassed or resp line
|
||||
Maybe#(lineT) line;
|
||||
} Match2Out#(
|
||||
type wayT,
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type repT,
|
||||
type lineT,
|
||||
type pipeCmdT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
indexT index;
|
||||
wayT way;
|
||||
RamData#(tagT, msiT, dirT, ownerT, otherT, lineT) ram; // data to write into RAM
|
||||
repT repInfo; // replacement info write into RAM
|
||||
} BypassInfo#(
|
||||
type wayT,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type repT,
|
||||
type lineT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
wayT way;
|
||||
Bool pRqMiss;
|
||||
} TagMatchResult#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
msiT cs;
|
||||
} UpdateByUpCs#(type msiT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
msiT cs;
|
||||
dirT dir;
|
||||
} UpdateByDownDir#(type msiT, type dirT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// index to data ram: {way, normal index}
|
||||
function dataIndexT getDataRamIndex(wayT w, indexT i) provisos(
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(indexT, Bit#(_indexSz)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(_waySz, _indexSz)))
|
||||
);
|
||||
return {w, i};
|
||||
endfunction
|
||||
|
||||
module mkCCPipe#(
|
||||
ReadOnly#(Bool) initDone,
|
||||
function indexT getIndex(pipeCmdT cmd),
|
||||
function ActionValue#(TagMatchResult#(wayT)) tagMatch(
|
||||
// actionvalue enable us to do checking inside the function
|
||||
pipeCmdT cmd,
|
||||
// below are current RAM outputs, is merged with ram write from final stage
|
||||
// but is NOT merged with state changes carried in PRs/CRs
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, msiT) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
),
|
||||
function ActionValue#(UpdateByUpCs#(msiT)) updateByUpCs(
|
||||
pipeCmdT cmd, msiT toState, Bool dataValid, msiT oldCs
|
||||
),
|
||||
function ActionValue#(UpdateByDownDir#(msiT, dirT)) updateByDownDir(
|
||||
pipeCmdT cmd, msiT toState, Bool dataValid, msiT oldCs, dirT oldDir
|
||||
),
|
||||
function ActionValue#(repT) updateRepInfo(repT oldRep, wayT hitWay),
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam,
|
||||
RWBramCore#(indexT, repT) repRam,
|
||||
RWBramCore#(dataIndexT, lineT) dataRam
|
||||
)(
|
||||
CCPipe#(wayNum, indexT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)
|
||||
) provisos (
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(indexT, Bit#(_indexSz)),
|
||||
Alias#(infoT, CacheInfo#(tagT, msiT, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, msiT, dirT, ownerT, otherT, lineT)),
|
||||
Alias#(respStateT, RespState#(msiT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
|
||||
Alias#(enq2MatchT, Enq2Match#(wayNum, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
|
||||
Alias#(match2OutT, Match2Out#(wayT, tagT, msiT, dirT, ownerT, otherT, repT, lineT, pipeCmdT)),
|
||||
Alias#(bypassInfoT, BypassInfo#(wayT, indexT, tagT, msiT, dirT, ownerT, otherT, repT, lineT)),
|
||||
Bits#(tagT, _tagSz),
|
||||
Bits#(msiT, _msiSz),
|
||||
Bits#(dirT, _dirSz),
|
||||
Bits#(ownerT, _ownerSz),
|
||||
Bits#(otherT, _otherSz),
|
||||
Bits#(repT, _repSz),
|
||||
Bits#(lineT, _lineSz),
|
||||
Bits#(pipeCmdT, _pipeCmdSz),
|
||||
// index to data ram: {way, normal index}
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), _indexSz)))
|
||||
);
|
||||
|
||||
// pipeline regs
|
||||
|
||||
Ehr#(3, Maybe#(enq2MatchT)) enq2Mat <- mkEhr(Invalid);
|
||||
// port 0: bypass
|
||||
Reg#(Maybe#(enq2MatchT)) enq2Mat_bypass = enq2Mat[0];
|
||||
// port 1: tag match
|
||||
Reg#(Maybe#(enq2MatchT)) enq2Mat_match = enq2Mat[1];
|
||||
// port 2: enq
|
||||
Reg#(Maybe#(enq2MatchT)) enq2Mat_enq = enq2Mat[2];
|
||||
|
||||
Ehr#(2, Maybe#(match2OutT)) mat2Out <- mkEhr(Invalid);
|
||||
// port 0: out
|
||||
Reg#(Maybe#(match2OutT)) mat2Out_out = mat2Out[0];
|
||||
// port 1: tag match
|
||||
Reg#(Maybe#(match2OutT)) mat2Out_match = mat2Out[1];
|
||||
|
||||
// bypass write to ram
|
||||
RWire#(bypassInfoT) bypass <- mkRWire;
|
||||
|
||||
// stage 2: first get bypass
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule doMatch_bypass(isValid(bypass.wget) && isValid(enq2Mat_bypass) && initDone);
|
||||
bypassInfoT b = fromMaybe(?, bypass.wget);
|
||||
enq2MatchT e2m = fromMaybe(?, enq2Mat_bypass);
|
||||
if(b.index == getIndex(e2m.cmd)) begin
|
||||
e2m.infoVec[b.way] = Valid (b.ram.info);
|
||||
e2m.repInfo = Valid (b.repInfo);
|
||||
end
|
||||
enq2Mat_bypass <= Valid (e2m);
|
||||
endrule
|
||||
|
||||
rule doTagMatch(isValid(enq2Mat_match) && !isValid(mat2Out_match) && initDone);
|
||||
enq2MatchT e2m = fromMaybe(?, enq2Mat_match);
|
||||
// get cache output & merge with bypass
|
||||
Vector#(wayNum, infoT) infoVec;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].deqRdResp;
|
||||
infoVec[i] = fromMaybe(infoRam[i].rdResp, e2m.infoVec[i]);
|
||||
end
|
||||
repRam.deqRdResp;
|
||||
repT repInfo = fromMaybe(repRam.rdResp, e2m.repInfo);
|
||||
// do tag match to get way to occupy
|
||||
Vector#(wayNum, tagT) tagVec;
|
||||
Vector#(wayNum, msiT) csVec;
|
||||
Vector#(wayNum, ownerT) ownerVec;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
tagVec[i] = infoVec[i].tag;
|
||||
csVec[i] = infoVec[i].cs;
|
||||
ownerVec[i] = infoVec[i].owner;
|
||||
end
|
||||
let tmRes <- tagMatch(e2m.cmd, tagVec, csVec, ownerVec, repInfo);
|
||||
wayT way = tmRes.way;
|
||||
Bool pRqMiss = tmRes.pRqMiss;
|
||||
// read data
|
||||
indexT index = getIndex(e2m.cmd);
|
||||
dataRam.rdReq(getDataRamIndex(way, index));
|
||||
// set mat2out & merge with CRs/PRs & merge with data bypass
|
||||
// resp data has higher priority than data bypass
|
||||
match2OutT m2o = Match2Out {
|
||||
cmd: e2m.cmd,
|
||||
way: way,
|
||||
pRqMiss: pRqMiss,
|
||||
info: infoVec[way],
|
||||
repInfo: repInfo,
|
||||
line: e2m.respLine
|
||||
};
|
||||
if(e2m.toState matches tagged UpCs .s) begin
|
||||
UpdateByUpCs#(msiT) upd <- updateByUpCs(
|
||||
e2m.cmd, s, isValid(e2m.respLine), m2o.info.cs
|
||||
);
|
||||
m2o.info.cs = upd.cs;
|
||||
end
|
||||
else if(e2m.toState matches tagged DownDir .s) begin
|
||||
UpdateByDownDir#(msiT, dirT) upd <- updateByDownDir(
|
||||
e2m.cmd, s, isValid(e2m.respLine), m2o.info.cs, m2o.info.dir
|
||||
);
|
||||
m2o.info.cs = upd.cs;
|
||||
m2o.info.dir = upd.dir;
|
||||
end
|
||||
if(bypass.wget matches tagged Valid .b &&& b.index == index &&& b.way == way &&& !isValid(m2o.line)) begin
|
||||
// bypass has lower priority than resp data
|
||||
m2o.line = Valid (b.ram.line);
|
||||
end
|
||||
mat2Out_match <= Valid (m2o);
|
||||
// reset enq2mat
|
||||
enq2Mat_match <= Invalid;
|
||||
endrule
|
||||
|
||||
// construct output with bypass/resp data
|
||||
function pipeOutT firstOut;
|
||||
match2OutT m2o = fromMaybe(?, mat2Out_out);
|
||||
return PipeOut {
|
||||
cmd: m2o.cmd,
|
||||
way: m2o.way,
|
||||
pRqMiss: m2o.pRqMiss,
|
||||
ram: RamData {
|
||||
info: m2o.info,
|
||||
line: fromMaybe(dataRam.rdResp, m2o.line)
|
||||
},
|
||||
repInfo: m2o.repInfo
|
||||
};
|
||||
endfunction
|
||||
|
||||
Bool enq_guard = !isValid(enq2Mat_enq) && initDone;
|
||||
|
||||
Bool deq_guard = isValid(mat2Out_out) && initDone;
|
||||
|
||||
// stage 1: enq req to pipeline: access info+rep RAM & bypass
|
||||
method Action enq(pipeCmdT cmd, Maybe#(lineT) respLine, respStateT toState) if(enq_guard);
|
||||
// read ram
|
||||
indexT index = getIndex(cmd);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].rdReq(index);
|
||||
end
|
||||
repRam.rdReq(index);
|
||||
// write reg & get bypass
|
||||
enq2MatchT e2m = Enq2Match {
|
||||
cmd: cmd,
|
||||
infoVec: replicate(Invalid),
|
||||
repInfo: Invalid,
|
||||
respLine: respLine,
|
||||
toState: toState
|
||||
};
|
||||
if(bypass.wget matches tagged Valid .b &&& b.index == index) begin
|
||||
e2m.infoVec[b.way] = Valid (b.ram.info);
|
||||
e2m.repInfo = Valid (b.repInfo);
|
||||
end
|
||||
enq2Mat_enq <= Valid (e2m);
|
||||
endmethod
|
||||
|
||||
method Bool notFull = enq_guard;
|
||||
|
||||
method pipeOutT first if(deq_guard);
|
||||
return firstOut;
|
||||
endmethod
|
||||
|
||||
method pipeOutT unguard_first;
|
||||
return firstOut;
|
||||
endmethod
|
||||
|
||||
method Bool notEmpty = deq_guard;
|
||||
|
||||
method Action deqWrite(Maybe#(pipeCmdT) newCmd, ramDataT wrRam, Bool updateRep) if(deq_guard);
|
||||
match2OutT m2o = fromMaybe(?, mat2Out_out);
|
||||
wayT way = m2o.way;
|
||||
indexT index = getIndex(m2o.cmd);
|
||||
// update replacement info
|
||||
repT repInfo = m2o.repInfo;
|
||||
if(updateRep) begin
|
||||
repInfo <- updateRepInfo(m2o.repInfo, way);
|
||||
end
|
||||
// write ram
|
||||
infoRam[way].wrReq(index, wrRam.info);
|
||||
repRam.wrReq(index, repInfo);
|
||||
dataRam.wrReq(getDataRamIndex(way, index), wrRam.line);
|
||||
// set bypass to Enq and Match stages
|
||||
bypass.wset(BypassInfo {
|
||||
index: index,
|
||||
way: way,
|
||||
ram: wrRam,
|
||||
repInfo: repInfo
|
||||
});
|
||||
// change pipeline reg
|
||||
if(newCmd matches tagged Valid .cmd) begin
|
||||
// update pipeline reg
|
||||
mat2Out_out <= Valid (Match2Out {
|
||||
cmd: cmd, // swapped in new cmd
|
||||
way: way, // keep way same
|
||||
pRqMiss: False, // reset (not valid for swapped in pRq)
|
||||
info: wrRam.info, // get bypass
|
||||
repInfo: repInfo, // get bypass
|
||||
line: Valid (wrRam.line) // get bypass
|
||||
});
|
||||
end
|
||||
else begin
|
||||
// XXX deq ram resp, I think this should not block
|
||||
dataRam.deqRdResp;
|
||||
// reset pipeline reg
|
||||
mat2Out_out <= Invalid;
|
||||
end
|
||||
endmethod
|
||||
|
||||
method Bool emptyForFlush;
|
||||
return !isValid(mat2Out[0]) && !isValid(enq2Mat[0]);
|
||||
endmethod
|
||||
endmodule
|
||||
464
src_Core/RISCY_OOO/coherence/src/CCTypes.bsv
Normal file
464
src_Core/RISCY_OOO/coherence/src/CCTypes.bsv
Normal file
@@ -0,0 +1,464 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Types::*; // import from RISCY repo
|
||||
import MemoryTypes::*; // import from RISCY repo
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import CacheUtils::*;
|
||||
import Assert::*;
|
||||
import Connectable::*;
|
||||
import GetPut::*;
|
||||
import ClientServer::*;
|
||||
|
||||
typedef enum {
|
||||
I = 2'd0,
|
||||
S = 2'd1,
|
||||
E = 2'd2,
|
||||
M = 2'd3
|
||||
} MESI deriving(Bits, Eq, FShow);
|
||||
typedef MESI Msi;
|
||||
|
||||
instance Ord#(MESI);
|
||||
function Bool \< ( MESI x, MESI y );
|
||||
return pack(x) < pack(y);
|
||||
endfunction
|
||||
function Bool \<= ( MESI x, MESI y );
|
||||
return pack(x) <= pack(y);
|
||||
endfunction
|
||||
function Bool \> ( MESI x, MESI y );
|
||||
return pack(x) > pack(y);
|
||||
endfunction
|
||||
function Bool \>= ( MESI x, MESI y );
|
||||
return pack(x) >= pack(y);
|
||||
endfunction
|
||||
function Ordering compare( MESI x, MESI y );
|
||||
return compare(pack(x), pack(y));
|
||||
endfunction
|
||||
function MESI min( MESI x, MESI y );
|
||||
return x < y ? x : y;
|
||||
endfunction
|
||||
function MESI max( MESI x, MESI y );
|
||||
return x > y ? x : y;
|
||||
endfunction
|
||||
endinstance
|
||||
|
||||
// whether cache state is enough to serice upgrade req, i.e., no
|
||||
// need to req parent
|
||||
function Bool enoughCacheState(Msi cs, Msi to);
|
||||
return cs >= to || cs >= E;
|
||||
endfunction
|
||||
|
||||
// the maximum dir state that a peer can have so that it will not
|
||||
// be downgraded to service upgrade req to state x
|
||||
function Msi toCompat(Msi x);
|
||||
return x == S ? S : I;
|
||||
endfunction
|
||||
|
||||
typedef TDiv#(DataSz, 8) DataSzBytes;
|
||||
typedef TLog#(DataSzBytes) LgDataSzBytes;
|
||||
typedef Bit#(LgDataSzBytes) DataBytesOffset;
|
||||
|
||||
typedef TDiv#(InstSz, 8) InstSzBytes;
|
||||
typedef TLog#(InstSzBytes) LgInstSzBytes;
|
||||
|
||||
// 64B cache line -- XXX same with parameters in CacheUtils.bsv
|
||||
typedef CacheUtils::LogCLineNumData LgLineSzData;
|
||||
typedef CacheUtils::LogCLineNumBytes LgLineSzBytes;
|
||||
typedef CacheUtils::CLineAddrSz LineAddrSz;
|
||||
typedef CacheUtils::CLineAddr LineAddr;
|
||||
|
||||
typedef CacheUtils::CLineNumData LineSzData;
|
||||
typedef CacheUtils::CLineDataSel LineDataOffset;
|
||||
|
||||
typedef CacheUtils::CLineNumBytes LineSzBytes;
|
||||
typedef CacheUtils::CLineByteEn LineByteEn;
|
||||
|
||||
typedef TDiv#(LineSzBytes, InstSzBytes) LineSzInst;
|
||||
typedef Bit#(TLog#(LineSzInst)) LineInstOffset;
|
||||
|
||||
typedef Vector#(LineSzData, Data) Line;
|
||||
|
||||
function LineAddr getLineAddr(Addr addr) = truncateLSB(addr);
|
||||
|
||||
function LineDataOffset getLineDataOffset(Addr a);
|
||||
return truncate(a >> valueOf(LgDataSzBytes));
|
||||
endfunction
|
||||
|
||||
function LineInstOffset getLineInstOffset(Addr a);
|
||||
return truncate(a >> valueof(LgInstSzBytes));
|
||||
endfunction
|
||||
|
||||
function Line getUpdatedLine(Line curLine, LineByteEn wrBE, Line wrLine);
|
||||
Vector#(LineSzBytes, Bit#(8)) curVec = unpack(pack(curLine));
|
||||
Vector#(LineSzBytes, Bit#(8)) wrVec = unpack(pack(wrLine));
|
||||
function Bit#(8) getNewByte(Integer i);
|
||||
return wrBE[i] ? wrVec[i] : curVec[i];
|
||||
endfunction
|
||||
Vector#(LineSzBytes, Bit#(8)) newVec = map(getNewByte, genVector);
|
||||
return unpack(pack(newVec));
|
||||
endfunction
|
||||
|
||||
function Data getUpdatedData(Data curData, ByteEn wrBE, Data wrData);
|
||||
Vector#(DataSzBytes, Bit#(8)) curVec = unpack(pack(curData));
|
||||
Vector#(DataSzBytes, Bit#(8)) wrVec = unpack(pack(wrData));
|
||||
function Bit#(8) getNewByte(Integer i);
|
||||
return wrBE[i] ? wrVec[i] : curVec[i];
|
||||
endfunction
|
||||
Vector#(DataSzBytes, Bit#(8)) newVec = map(getNewByte, genVector);
|
||||
return pack(newVec);
|
||||
endfunction
|
||||
|
||||
// calculate tag
|
||||
typedef TSub#(AddrSz, TAdd#(LgLineSzBytes, TAdd#(lgBankNum, lgSetNum)))
|
||||
GetTagSz#(numeric type lgBankNum, numeric type lgSetNum);
|
||||
|
||||
// dependency tracking in MSHR
|
||||
typedef union tagged {
|
||||
cRqIdxT CRq;
|
||||
pRqIdxT PRq;
|
||||
} MshrIndex#(type cRqIdxT, type pRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// cache owner
|
||||
typedef struct {
|
||||
cRqIdxT mshrIdx;
|
||||
Bool replacing;
|
||||
} CRqOwner#(type cRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
pRqIdxT mshrIdx;
|
||||
Bool hasSucc; // has successor in dependency chain
|
||||
} PRqOwner#(type pRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
void Invalid;
|
||||
CRqOwner#(cRqIdxT) CRq;
|
||||
PRqOwner#(pRqIdxT) PRq;
|
||||
} CacheOwner#(type cRqIdxT, type pRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// cache info: tag, cs, dir, owner, and other
|
||||
typedef struct {
|
||||
tagT tag;
|
||||
msiT cs;
|
||||
dirT dir;
|
||||
ownerT owner;
|
||||
otherT other;
|
||||
} CacheInfo#(
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
// ram output
|
||||
typedef struct {
|
||||
CacheInfo#(tagT, msiT, dirT, ownerT, otherT) info;
|
||||
lineT line;
|
||||
} RamData#(
|
||||
type tagT,
|
||||
type msiT,
|
||||
type dirT,
|
||||
type ownerT,
|
||||
type otherT,
|
||||
type lineT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
// processor req/resp
|
||||
typedef struct {
|
||||
idT id;
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
// below are detailed mem op
|
||||
MemOp op; // Ld, St, Lr, Sc, Amo
|
||||
ByteEn byteEn; // valid when op == Sc
|
||||
Data data; // valid when op == Sc/Amo
|
||||
AmoInst amoInst; // valid when op == Amo
|
||||
} ProcRq#(type idT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface L1ProcReq#(type idT);
|
||||
method Action req(ProcRq#(idT) r);
|
||||
endinterface
|
||||
|
||||
interface L1ProcResp#(type idT);
|
||||
method Action respLd(idT id, Data resp);
|
||||
method Action respLrScAmo(idT id, Data resp);
|
||||
method ActionValue#(Tuple2#(LineByteEn, Line)) respSt(idT id);
|
||||
method Action evict(LineAddr a); // called when cache line is evicted
|
||||
endinterface
|
||||
|
||||
// RISCV-specific store-cond return values
|
||||
typedef 0 ScSuccVal;
|
||||
typedef 1 ScFailVal;
|
||||
|
||||
`ifdef DEBUG_ICACHE
|
||||
typedef struct {
|
||||
Bit#(64) id;
|
||||
Line line;
|
||||
} DebugICacheResp deriving(Bits, Eq, FShow);
|
||||
`endif
|
||||
|
||||
// I$ req/resp
|
||||
interface InstServer#(numeric type supSz);
|
||||
interface Put#(Addr) req;
|
||||
interface Get#(Vector#(supSz, Maybe#(Instruction))) resp;
|
||||
`ifdef DEBUG_ICACHE
|
||||
interface Get#(DebugICacheResp) done; // the id and cache line of the I$ req that truly performs
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
`ifdef DEBUG_ICACHE
|
||||
Bit#(64) id; // incremening id for each incoming I$ req (0,1,...)
|
||||
`endif
|
||||
} ProcRqToI deriving(Bits, Eq, FShow);
|
||||
|
||||
// child/parent req/resp
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi fromState;
|
||||
Msi toState;
|
||||
Bool canUpToE; // meaningful to upgrade to E if toState is S
|
||||
idT id; // slot id in child cache
|
||||
childT child; // from which child
|
||||
} CRqMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
Maybe#(Line) data;
|
||||
childT child; // from which child
|
||||
} CRsMsg#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
childT child; // to which child
|
||||
} PRqMsg#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
childT child; // to which child
|
||||
Maybe#(Line) data;
|
||||
idT id; // slot id in cache
|
||||
} PRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
PRqMsg#(childT) PRq;
|
||||
PRsMsg#(idT, childT) PRs;
|
||||
} PRqRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface ChildCacheToParent#(type cRqIdT, type childT);
|
||||
interface FifoDeq#(CRsMsg#(childT)) rsToP;
|
||||
interface FifoDeq#(CRqMsg#(cRqIdT, childT)) rqToP;
|
||||
interface FifoEnq#(PRqRsMsg#(cRqIdT, childT)) fromP;
|
||||
endinterface
|
||||
|
||||
interface ParentCacheToChild#(type cRqIdT, type childT);
|
||||
interface FifoEnq#(CRsMsg#(childT)) rsFromC;
|
||||
interface FifoEnq#(CRqMsg#(cRqIdT, childT)) rqFromC;
|
||||
interface FifoDeq#(PRqRsMsg#(cRqIdT, childT)) toC;
|
||||
endinterface
|
||||
|
||||
// unified child req & dma req
|
||||
typedef union tagged {
|
||||
cRqIdT Child;
|
||||
dmaRqIdT Dma;
|
||||
} LLRqId#(type cRqIdT, type dmaRqIdT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
// common addr
|
||||
Addr addr;
|
||||
// child req stuff
|
||||
Msi fromState;
|
||||
Msi toState;
|
||||
Bool canUpToE;
|
||||
childT child;
|
||||
// dma req stuff
|
||||
LineByteEn byteEn;
|
||||
// req id: distinguish between child and dma
|
||||
LLRqId#(cRqIdT, dmaRqIdT) id;
|
||||
} LLRq#(type cRqIdT, type dmaRqIdT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// memory msg
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
childT child; // from which LLC/Dir
|
||||
idT id; // ld req id and other info need encoding
|
||||
} LdMemRq#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct { // LdMemRq id with more info encoded to handle DMA req in LLC
|
||||
Bool refill; // the future mem resp will refill LLC cache line
|
||||
// this is False for DMA read req that miss in LLC (i.e. resp won't refill LLC)
|
||||
mshrIdxT mshrIdx; // mshr id
|
||||
} LdMemRqId#(type mshrIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
LineByteEn byteEn;
|
||||
Line data;
|
||||
} WbMemRs deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
LdMemRq#(idT, childT) Ld;
|
||||
WbMemRs Wb;
|
||||
} ToMemMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Line data;
|
||||
childT child; // send to which LLC/Dir
|
||||
idT id; // original Ld req id
|
||||
} MemRsMsg#(type idT, type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// Dma req/resp
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
LineByteEn byteEn; // all False means read
|
||||
Line data;
|
||||
idT id; // req id (resp may come out of order, may contain routing info)
|
||||
} DmaRq#(type idT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Line data; // meaningless for write
|
||||
idT id;
|
||||
} DmaRs#(type idT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface DmaServer#(type dmaRqIdT);
|
||||
interface FifoEnq#(DmaRq#(dmaRqIdT)) memReq;
|
||||
interface FifoDeq#(DmaRs#(dmaRqIdT)) respLd;
|
||||
interface FifoDeq#(dmaRqIdT) respSt;
|
||||
`ifdef DEBUG_DMA
|
||||
// signal when DMA req really takes effect
|
||||
interface Get#(dmaRqIdT) wrMissResp;
|
||||
interface Get#(dmaRqIdT) wrHitResp;
|
||||
interface Get#(dmaRqIdT) rdMissResp;
|
||||
interface Get#(dmaRqIdT) rdHitResp;
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
// memory interface
|
||||
interface MemFifoServer#(type idT, type childT);
|
||||
interface FifoEnq#(ToMemMsg#(idT, childT)) fromC;
|
||||
interface FifoDeq#(MemRsMsg#(idT, childT)) rsToC;
|
||||
endinterface
|
||||
|
||||
interface MemFifoClient#(type idT, type childT);
|
||||
interface FifoDeq#(ToMemMsg#(idT, childT)) toM;
|
||||
interface FifoEnq#(MemRsMsg#(idT, childT)) rsFromM;
|
||||
endinterface
|
||||
|
||||
instance Connectable#(MemFifoServer#(idT, childT), MemFifoClient#(idT, childT));
|
||||
module mkConnection#(
|
||||
MemFifoServer#(idT, childT) server,
|
||||
MemFifoClient#(idT, childT) client
|
||||
)(Empty);
|
||||
rule doCToM;
|
||||
client.toM.deq;
|
||||
server.fromC.enq(client.toM.first);
|
||||
endrule
|
||||
rule doMToC;
|
||||
server.rsToC.deq;
|
||||
client.rsFromM.enq(server.rsToC.first);
|
||||
endrule
|
||||
endmodule
|
||||
endinstance
|
||||
|
||||
instance Connectable#(MemFifoClient#(idT, childT), MemFifoServer#(idT, childT));
|
||||
module mkConnection#(
|
||||
MemFifoClient#(idT, childT) client,
|
||||
MemFifoServer#(idT, childT) server
|
||||
)(Empty);
|
||||
mkConnection(server, client);
|
||||
endmodule
|
||||
endinstance
|
||||
|
||||
// MSHR dir pending bits
|
||||
typedef union tagged {
|
||||
void Invalid;
|
||||
Msi ToSend; // need to send down req to downgrade to some state
|
||||
Msi Waiting; // waiting for down resp for some state
|
||||
} DirPend deriving(Bits, Eq, FShow);
|
||||
|
||||
function Vector#(childNum, DirPend) getDirPendInitVal;
|
||||
return replicate(Invalid);
|
||||
endfunction
|
||||
|
||||
function Bool getNeedReqChild(Vector#(childNum, DirPend) dirPend);
|
||||
// function to determine whether we need to send req to some children
|
||||
function Bool isToSend(DirPend dp);
|
||||
if(dp matches tagged ToSend .s) begin
|
||||
return True;
|
||||
end
|
||||
else begin
|
||||
return False;
|
||||
end
|
||||
endfunction
|
||||
return any(isToSend, dirPend);
|
||||
endfunction
|
||||
|
||||
// Self-inv cache dir pending bits
|
||||
typedef union tagged {
|
||||
void Invalid;
|
||||
childT ToSend;
|
||||
childT Waiting;
|
||||
} SelfInvDirPend#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
function SelfInvDirPend#(childT) getSelfInvDirPendInitVal;
|
||||
return Invalid;
|
||||
endfunction
|
||||
|
||||
function Bool getSelfInvNeedReqChild(SelfInvDirPend#(childT) dirPend);
|
||||
return dirPend matches tagged ToSend .c ? True : False;
|
||||
endfunction
|
||||
|
||||
// useful functions
|
||||
function Action check(Bool v);
|
||||
action
|
||||
when(v, noAction);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
function Maybe#(Bit#(logv)) searchIndex
|
||||
(function Bool pred(element_type x1), Vector#(vsize, element_type) vect)
|
||||
provisos (Log#(vsize, logv));
|
||||
return case (findIndex(pred, vect)) matches
|
||||
tagged Valid .idx: return tagged Valid pack(idx);
|
||||
Invalid: return tagged Invalid;
|
||||
endcase;
|
||||
endfunction
|
||||
|
||||
function a readReg(Reg#(a) r) provisos(Bits#(a, aSz)) = r;
|
||||
function Vector#(n, a) readVector(Vector#(n, Reg#(a)) vr) provisos(Bits#(a, aSz)) = map(readReg, vr);
|
||||
|
||||
// doAssert now defined in Types.bsv
|
||||
//function Action doAssert(Bool b, String str) = dynamicAssert(b, "%m: " + str);
|
||||
|
||||
function Get#(t) nullGet;
|
||||
return (interface Get;
|
||||
method ActionValue#(t) get if(False);
|
||||
return ?;
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
215
src_Core/RISCY_OOO/coherence/src/CrossBar.bsv
Normal file
215
src_Core/RISCY_OOO/coherence/src/CrossBar.bsv
Normal file
@@ -0,0 +1,215 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import Connectable::*;
|
||||
import CacheUtils::*;
|
||||
import CCTypes::*;
|
||||
import Types::*;
|
||||
import FShow::*;
|
||||
import Fifo::*;
|
||||
import Ehr::*;
|
||||
|
||||
typedef struct {
|
||||
dstIdxT idx;
|
||||
dstDataT data;
|
||||
} XBarDstInfo#(type dstIdxT, type dstDataT) deriving(Bits, Eq);
|
||||
|
||||
module mkXBar#(
|
||||
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data),
|
||||
Vector#(srcNum, Get#(srcDataT)) srcIfc,
|
||||
Vector#(dstNum, Put#(dstDataT)) dstIfc
|
||||
)(Empty) provisos(
|
||||
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
|
||||
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
|
||||
Bits#(srcDataT, _srcDataSz),
|
||||
Bits#(dstDataT, _dstDataSz),
|
||||
FShow#(dstDataT)
|
||||
);
|
||||
// proposed data transfer by each src
|
||||
Vector#(srcNum, Ehr#(2, Maybe#(dstIdxT))) propDstIdx <- replicateM(mkEhr(Invalid));
|
||||
Vector#(srcNum, Ehr#(2, dstDataT)) propDstData <- replicateM(mkEhr(?));
|
||||
// enq command that should be carried out
|
||||
Vector#(dstNum, Ehr#(2, Maybe#(dstDataT))) enqDst <- replicateM(mkEhr(Invalid));
|
||||
|
||||
// src propose data transfer when src is not empty
|
||||
// and there is no unfinished src proposal
|
||||
for(Integer i = 0; i < valueOf(srcNum); i = i+1) begin
|
||||
rule srcPropose(!isValid(propDstIdx[i][0]));
|
||||
let d <- srcIfc[i].get;
|
||||
let info = getDstInfo(fromInteger(i), d);
|
||||
propDstIdx[i][0] <= Valid (info.idx);
|
||||
propDstData[i][0] <= info.data;
|
||||
endrule
|
||||
end
|
||||
|
||||
// round-robin select src for each dst
|
||||
Vector#(dstNum, Reg#(srcIdxT)) srcRR <- replicateM(mkReg(0));
|
||||
|
||||
// do selection for each dst & generate enq commands & deq src proposals
|
||||
// dst which has unfinished enq command cannot select src
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule dstSelectSrc;
|
||||
// which src to deq (i.e. select) by each dst
|
||||
Vector#(dstNum, Maybe#(srcIdxT)) deqSrcByDst = replicate(Invalid);
|
||||
// each dst selects
|
||||
for(Integer dst = 0; dst < valueOf(dstNum); dst = dst + 1) begin
|
||||
// only select src to deq when dst has no unfinished enq command
|
||||
if(!isValid(enqDst[dst][0])) begin
|
||||
function Bool isFromSrc(srcIdxT src);
|
||||
// src has proposed data to this dst or not
|
||||
if(propDstIdx[src][1] matches tagged Valid .dstIdx &&& dstIdx == fromInteger(dst)) begin
|
||||
return True;
|
||||
end
|
||||
else begin
|
||||
return False;
|
||||
end
|
||||
endfunction
|
||||
Maybe#(srcIdxT) whichSrc = Invalid; // the src to select
|
||||
if(isFromSrc(srcRR[dst])) begin
|
||||
// first check the src with priority
|
||||
whichSrc = Valid (srcRR[dst]);
|
||||
end
|
||||
else begin
|
||||
// otherwise just get one valid src
|
||||
Vector#(srcNum, srcIdxT) srcIdxVec = genWith(fromInteger);
|
||||
whichSrc = searchIndex(isFromSrc, srcIdxVec);
|
||||
end
|
||||
if(whichSrc matches tagged Valid .src) begin
|
||||
// can do enq & deq
|
||||
deqSrcByDst[dst] = whichSrc;
|
||||
enqDst[dst][0] <= Valid (propDstData[src][1]); // set enq command
|
||||
// change round robin
|
||||
srcRR[dst] <= srcRR[dst] == fromInteger(valueOf(srcNum) - 1) ? 0 : srcRR[dst] + 1;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// deq selected src
|
||||
function Bool isDeqSrc(srcIdxT src);
|
||||
function Bool isMatch(Maybe#(srcIdxT) deqIdx);
|
||||
return deqIdx matches tagged Valid .idx &&& idx == src ? True : False;
|
||||
endfunction
|
||||
return any(isMatch, deqSrcByDst);
|
||||
endfunction
|
||||
for(Integer i = 0; i < valueOf(srcNum); i = i+1) begin
|
||||
if(isDeqSrc(fromInteger(i))) begin
|
||||
propDstIdx[i][1] <= Invalid;
|
||||
$display("%t XBar %m: deq src %d", $time, i);
|
||||
doAssert(isValid(propDstIdx[i][1]), "src must be proposing");
|
||||
end
|
||||
end
|
||||
endrule
|
||||
|
||||
// enq dst & change round robin
|
||||
for(Integer i = 0; i < valueOf(dstNum); i = i+1) begin
|
||||
// XXX since dstIfc.put may conflict with other rules
|
||||
// the following rule should only fire when it can make progress
|
||||
// otherwise it may prevent other rules from firing forever
|
||||
rule doEnq(enqDst[i][1] matches tagged Valid .d);
|
||||
dstIfc[i].put(d);
|
||||
enqDst[i][1] <= Invalid; // reset enq command
|
||||
$display("%t XBAR %m: enq dst %d ; ", $time, i, fshow(d));
|
||||
endrule
|
||||
end
|
||||
endmodule
|
||||
|
||||
// XBar with latency added at src or dst (mainly for model timing)
|
||||
interface XBarDelay#(numeric type srcLat, numeric type dstLat);
|
||||
endinterface
|
||||
|
||||
module mkXBarDelay#(
|
||||
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data),
|
||||
Vector#(srcNum, Get#(srcDataT)) srcIfc,
|
||||
Vector#(dstNum, Put#(dstDataT)) dstIfc
|
||||
)(XBarDelay#(srcLat, dstLat)) provisos(
|
||||
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
|
||||
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
|
||||
Bits#(srcDataT, _srcDataSz),
|
||||
Bits#(dstDataT, _dstDataSz),
|
||||
FShow#(dstDataT)
|
||||
);
|
||||
// Add latency at src side
|
||||
Vector#(srcNum, Get#(srcDataT)) src = srcIfc;
|
||||
if(valueof(srcLat) > 0) begin
|
||||
for(Integer i = 0; i < valueof(srcNum); i = i+1) begin
|
||||
Vector#(srcLat, Fifo#(2, srcDataT)) delayQ <- replicateM(mkCFFifo);
|
||||
mkConnection(srcIfc[i], toPut(delayQ[0]));
|
||||
for(Integer j = 0; j < valueof(srcLat) - 1; j = j+1) begin
|
||||
mkConnection(toGet(delayQ[j]), toPut(delayQ[j + 1]));
|
||||
end
|
||||
src[i] = toGet(delayQ[valueof(srcLat) - 1]);
|
||||
end
|
||||
end
|
||||
|
||||
// Add latency at dst side
|
||||
Vector#(dstNum, Put#(dstDataT)) dst = dstIfc;
|
||||
if(valueof(dstLat) > 0) begin
|
||||
for(Integer i = 0; i < valueof(dstNum); i = i+1) begin
|
||||
Vector#(dstLat, Fifo#(2, dstDataT)) delayQ <- replicateM(mkCFFifo);
|
||||
mkConnection(toGet(delayQ[0]), dstIfc[i]);
|
||||
for(Integer j = 0; j < valueof(dstLat) - 1; j = j+1) begin
|
||||
mkConnection(toGet(delayQ[j + 1]), toPut(delayQ[j]));
|
||||
end
|
||||
dst[i] = toPut(delayQ[valueof(dstLat) - 1]);
|
||||
end
|
||||
end
|
||||
|
||||
mkXBar(getDstInfo, src, dst);
|
||||
endmodule
|
||||
|
||||
interface CrossBar#(
|
||||
numeric type srcNum,
|
||||
numeric type srcLat,
|
||||
type srcDataT,
|
||||
numeric type dstNum,
|
||||
numeric type dstLat,
|
||||
type dstDataT
|
||||
);
|
||||
interface Vector#(srcNum, FifoEnq#(srcDataT)) srcIfc;
|
||||
interface Vector#(dstNum, FifoDeq#(dstDataT)) dstIfc;
|
||||
endinterface
|
||||
|
||||
module mkCrossBar#(
|
||||
function XBarDstInfo#(dstIdxT, dstDataT) getDstInfo(srcIdxT idx, srcDataT data)
|
||||
)(
|
||||
CrossBar#(srcNum, srcLat, srcDataT, dstNum, dstLat, dstDataT)
|
||||
) provisos(
|
||||
Alias#(srcIdxT, Bit#(TLog#(srcNum))),
|
||||
Alias#(dstIdxT, Bit#(TLog#(dstNum))),
|
||||
Bits#(srcDataT, _srcDataSz),
|
||||
Bits#(dstDataT, _dstDataSz),
|
||||
FShow#(dstDataT)
|
||||
);
|
||||
|
||||
// in/out FIFOs
|
||||
Vector#(srcNum, Fifo#(2, srcDataT)) srcQ <- replicateM(mkCFFifo);
|
||||
Vector#(dstNum, Fifo#(2, dstDataT)) dstQ <- replicateM(mkCFFifo);
|
||||
|
||||
XBarDelay#(srcLat, dstLat) xbar <- mkXBarDelay(getDstInfo, map(toGet, srcQ), map(toPut, dstQ));
|
||||
|
||||
interface srcIfc = map(toFifoEnq, srcQ);
|
||||
interface dstIfc = map(toFifoDeq, dstQ);
|
||||
endmodule
|
||||
|
||||
849
src_Core/RISCY_OOO/coherence/src/IBank.bsv
Normal file
849
src_Core/RISCY_OOO/coherence/src/IBank.bsv
Normal file
@@ -0,0 +1,849 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
|
||||
import Types::*;
|
||||
import MemoryTypes::*;
|
||||
import Amo::*;
|
||||
|
||||
import Cntrs::*;
|
||||
import Vector::*;
|
||||
import ConfigReg::*;
|
||||
import FIFO::*;
|
||||
import GetPut::*;
|
||||
import ClientServer::*;
|
||||
import CCTypes::*;
|
||||
import ICRqMshr::*;
|
||||
import IPRqMshr::*;
|
||||
import CCPipe::*;
|
||||
import L1Pipe ::*;
|
||||
import FShow::*;
|
||||
import DefaultValue::*;
|
||||
import Fifo::*;
|
||||
import CacheUtils::*;
|
||||
import Performance::*;
|
||||
import LatencyTimer::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export ICRqStuck(..);
|
||||
export IPRqStuck(..);
|
||||
export IBank(..);
|
||||
export mkIBank;
|
||||
|
||||
// L1 I$
|
||||
|
||||
// although pRq never appears in dependency chain
|
||||
// we still need pRq MSHR to limit the number of pRq
|
||||
// and thus limit the size of rsToPIndexQ
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
ICRqState state;
|
||||
Bool waitP;
|
||||
} ICRqStuck deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef IPRqMshrStuck IPRqStuck;
|
||||
|
||||
interface IBank#(
|
||||
numeric type supSz, // superscalar size
|
||||
numeric type lgBankNum,
|
||||
numeric type wayNum,
|
||||
numeric type indexSz,
|
||||
numeric type tagSz,
|
||||
numeric type cRqNum,
|
||||
numeric type pRqNum
|
||||
);
|
||||
interface ChildCacheToParent#(Bit#(TLog#(wayNum)), void) to_parent;
|
||||
interface InstServer#(supSz) to_proc; // to child, i.e. processor
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(ICRqStuck) cRqStuck;
|
||||
interface Get#(IPRqStuck) pRqStuck;
|
||||
// security: flush
|
||||
method Action flush;
|
||||
method Bool flush_done;
|
||||
// performance
|
||||
method Action setPerfStatus(Bool stats);
|
||||
method Data getPerfData(L1IPerfType t);
|
||||
endinterface
|
||||
|
||||
module mkIBank#(
|
||||
Bit#(lgBankNum) bankId,
|
||||
module#(ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT)) mkICRqMshrLocal,
|
||||
module#(IPRqMshr#(pRqNum)) mkIPRqMshrLocal,
|
||||
module#(L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT)) mkL1Pipeline
|
||||
)(
|
||||
IBank#(supSz, lgBankNum, wayNum, indexSz, tagSz, cRqNum, pRqNum)
|
||||
) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(TLog#(cRqNum))),
|
||||
Alias#(pRqIdxT, Bit#(TLog#(pRqNum))),
|
||||
Alias#(cacheOwnerT, Maybe#(cRqIdxT)), // owner cannot be pRq
|
||||
Alias#(cacheInfoT, CacheInfo#(tagT, Msi, void, cacheOwnerT, void)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, void, cacheOwnerT, void, Line)),
|
||||
Alias#(procRqT, ProcRqToI),
|
||||
Alias#(cRqToPT, CRqMsg#(wayT, void)),
|
||||
Alias#(cRsToPT, CRsMsg#(void)),
|
||||
Alias#(pRqFromPT, PRqMsg#(void)),
|
||||
Alias#(pRsFromPT, PRsMsg#(wayT, void)),
|
||||
Alias#(pRqRsFromPT, PRqRsMsg#(wayT, void)),
|
||||
Alias#(cRqSlotT, ICRqSlot#(wayT, tagT)), // cRq MSHR slot
|
||||
Alias#(l1CmdT, L1Cmd#(indexT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, void, cacheOwnerT, void, RandRepInfo, Line, l1CmdT)),
|
||||
Alias#(resultT, Vector#(supSz, Maybe#(Instruction))),
|
||||
// requirements
|
||||
FShow#(pipeOutT),
|
||||
Add#(tagSz, a__, AddrSz),
|
||||
// make sure: cRqNum <= wayNum
|
||||
Add#(cRqNum, b__, wayNum),
|
||||
Add#(TAdd#(tagSz, indexSz), TAdd#(lgBankNum, LgLineSzBytes), AddrSz)
|
||||
);
|
||||
|
||||
ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT) cRqMshr <- mkICRqMshrLocal;
|
||||
|
||||
IPRqMshr#(pRqNum) pRqMshr <- mkIPRqMshrLocal;
|
||||
|
||||
L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT) pipeline <- mkL1Pipeline;
|
||||
|
||||
Fifo#(1, Addr) rqFromCQ <- mkBypassFifo;
|
||||
|
||||
Fifo#(2, cRsToPT) rsToPQ <- mkCFFifo;
|
||||
Fifo#(2, cRqToPT) rqToPQ <- mkCFFifo;
|
||||
Fifo#(2, pRqRsFromPT) fromPQ <- mkCFFifo;
|
||||
|
||||
FIFO#(MshrIndex#(cRqIdxT, pRqIdxT)) rsToPIndexQ <- mkSizedFIFO(valueOf(TAdd#(cRqNum, pRqNum)));
|
||||
|
||||
FIFO#(cRqIdxT) rqToPIndexQ <- mkSizedFIFO(valueOf(cRqNum));
|
||||
// temp fifo for pipelineResp & sendRsToP (reduce conflict)
|
||||
FIFO#(cRqIdxT) rqToPIndexQ_pipelineResp <- mkFIFO;
|
||||
FIFO#(cRqIdxT) rqToPIndexQ_sendRsToP <- mkFIFO;
|
||||
|
||||
// index Q to order all in flight cRq for in-order resp
|
||||
FIFO#(cRqIdxT) cRqIndexQ <- mkSizedFIFO(valueof(cRqNum));
|
||||
|
||||
`ifdef DEBUG_ICACHE
|
||||
// id for each cRq, incremented when each new req comes
|
||||
Reg#(Bit#(64)) cRqId <- mkReg(0);
|
||||
// FIFO to signal the id of cRq that is performed
|
||||
// FIFO has 0 cycle latency to match L1 D$ resp latency
|
||||
Fifo#(1, DebugICacheResp) cRqDoneQ <- mkBypassFifo;
|
||||
`endif
|
||||
|
||||
// security flush
|
||||
`ifdef SECURITY
|
||||
Reg#(Bool) flushDone <- mkReg(True);
|
||||
Reg#(Bool) flushReqStart <- mkReg(False);
|
||||
Reg#(Bool) flushReqDone <- mkReg(False);
|
||||
Reg#(Bool) flushRespDone <- mkReg(False);
|
||||
Reg#(indexT) flushIndex <- mkReg(0);
|
||||
Reg#(wayT) flushWay <- mkReg(0);
|
||||
`else
|
||||
Bool flushDone = True;
|
||||
`endif
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
Reg#(Bool) doStats <- mkConfigReg(False);
|
||||
Count#(Data) ldCnt <- mkCount(0);
|
||||
Count#(Data) ldMissCnt <- mkCount(0);
|
||||
Count#(Data) ldMissLat <- mkCount(0);
|
||||
|
||||
LatencyTimer#(cRqNum, 10) latTimer <- mkLatencyTimer;
|
||||
|
||||
function Action incrReqCnt;
|
||||
action
|
||||
if(doStats) begin
|
||||
ldCnt.incr(1);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
function Action incrMissCnt(cRqIdxT idx);
|
||||
action
|
||||
let lat <- latTimer.done(idx);
|
||||
if(doStats) begin
|
||||
ldMissLat.incr(zeroExtend(lat));
|
||||
ldMissCnt.incr(1);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
// XXX since I$ may be requested by processor constantly
|
||||
// cRq may come at every cycle, so we must make cRq has lower priority than pRq/pRs
|
||||
// otherwise the whole system may deadlock/livelock
|
||||
// we stop accepting cRq when we need to flush for security
|
||||
rule cRqTransfer(flushDone);
|
||||
Addr addr <- toGet(rqFromCQ).get;
|
||||
`ifdef DEBUG_ICACHE
|
||||
procRqT r = ProcRqToI {addr: addr, id: cRqId};
|
||||
cRqId <= cRqId + 1;
|
||||
`else
|
||||
procRqT r = ProcRqToI {addr: addr};
|
||||
`endif
|
||||
cRqIdxT n <- cRqMshr.getEmptyEntryInit(r);
|
||||
// send to pipeline
|
||||
pipeline.send(CRq (L1PipeRqIn {
|
||||
addr: r.addr,
|
||||
mshrIdx: n
|
||||
}));
|
||||
// enq to indexQ for in order resp
|
||||
cRqIndexQ.enq(n);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: cRq type
|
||||
incrReqCnt;
|
||||
`endif
|
||||
$display("%t I %m cRqTransfer: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(r)
|
||||
);
|
||||
endrule
|
||||
|
||||
// this descending urgency is necessary to avoid deadlock/livelock
|
||||
(* descending_urgency = "pRqTransfer, cRqTransfer" *)
|
||||
rule pRqTransfer(fromPQ.first matches tagged PRq .req);
|
||||
fromPQ.deq;
|
||||
pRqIdxT n <- pRqMshr.getEmptyEntryInit(req);
|
||||
// send to pipeline
|
||||
pipeline.send(PRq (L1PipeRqIn {
|
||||
addr: req.addr,
|
||||
mshrIdx: n
|
||||
}));
|
||||
$display("%t I %m pRqTransfer: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(req)
|
||||
);
|
||||
endrule
|
||||
|
||||
// this descending urgency is necessary to avoid deadlock/livelock
|
||||
(* descending_urgency = "pRsTransfer, cRqTransfer" *)
|
||||
rule pRsTransfer(fromPQ.first matches tagged PRs .resp);
|
||||
fromPQ.deq;
|
||||
pipeline.send(PRs (L1PipePRsIn {
|
||||
addr: resp.addr,
|
||||
toState: S,
|
||||
data: resp.data,
|
||||
way: resp.id
|
||||
}));
|
||||
$display("%t I %m pRsTransfer: ", $time, fshow(resp));
|
||||
doAssert(resp.toState == S && isValid(resp.data), "I$ must upgrade to S with data");
|
||||
endrule
|
||||
|
||||
`ifdef SECURITY
|
||||
// start flush when cRq MSHR is empty
|
||||
rule startFlushReq(!flushDone && !flushReqStart && cRqMshr.emptyForFlush);
|
||||
flushReqStart <= True;
|
||||
endrule
|
||||
|
||||
(* descending_urgency = "pRsTransfer, flushTransfer" *)
|
||||
(* descending_urgency = "pRqTransfer, flushTransfer" *)
|
||||
rule flushTransfer(!flushDone && flushReqStart && !flushReqDone);
|
||||
// We allocate a pRq MSHR entry for 2 reasons:
|
||||
// (1) reuse the pRq logic to send resp to parent
|
||||
// (2) control the number of downgrade resp to avoid stalling the cache
|
||||
// pipeline
|
||||
pRqIdxT n <- pRqMshr.getEmptyEntryInit(PRqMsg {
|
||||
addr: ?,
|
||||
toState: I,
|
||||
child: ?
|
||||
});
|
||||
pipeline.send(Flush (L1PipeFlushIn {
|
||||
index: flushIndex,
|
||||
way: flushWay,
|
||||
mshrIdx: n
|
||||
}));
|
||||
// increment flush index/way
|
||||
if (flushWay < fromInteger(valueof(wayNum) - 1)) begin
|
||||
flushWay <= flushWay + 1;
|
||||
end
|
||||
else begin
|
||||
flushWay <= 0;
|
||||
flushIndex <= flushIndex + 1; // index num should be power of 2
|
||||
if (flushIndex == maxBound) begin
|
||||
flushReqDone <= True;
|
||||
end
|
||||
end
|
||||
$display("%t I %m flushTransfer: ", $time, fshow(n), " ; ",
|
||||
fshow(flushIndex), " ; ", fshow(flushWay));
|
||||
endrule
|
||||
`endif
|
||||
|
||||
rule sendRsToP_cRq(rsToPIndexQ.first matches tagged CRq .n);
|
||||
rsToPIndexQ.deq;
|
||||
// get cRq replacement info
|
||||
procRqT req = cRqMshr.sendRsToP_cRq.getRq(n);
|
||||
cRqSlotT slot = cRqMshr.sendRsToP_cRq.getSlot(n);
|
||||
// send resp to parent
|
||||
cRsToPT resp = CRsMsg {
|
||||
addr: {slot.repTag, truncate(req.addr)}, // get bank id & index from req
|
||||
toState: I,
|
||||
data: Invalid, // I$ never downgrade with data to writeback
|
||||
child: ?
|
||||
};
|
||||
rsToPQ.enq(resp);
|
||||
// req parent for upgrade now
|
||||
// (prevent parent resp from coming to release MSHR entry before replace resp is sent)
|
||||
rqToPIndexQ_sendRsToP.enq(n);
|
||||
$display("%t I %m sendRsToP: ", $time,
|
||||
fshow(rsToPIndexQ.first)," ; ",
|
||||
fshow(req), " ; ",
|
||||
fshow(slot), " ; ",
|
||||
fshow(resp)
|
||||
);
|
||||
endrule
|
||||
|
||||
rule sendRsToP_pRq(rsToPIndexQ.first matches tagged PRq .n);
|
||||
rsToPIndexQ.deq;
|
||||
// get pRq info & send resp & release MSHR entry
|
||||
pRqFromPT req = pRqMshr.sendRsToP_pRq.getRq(n);
|
||||
cRsToPT resp = CRsMsg {
|
||||
addr: req.addr,
|
||||
toState: I, // I$ must downgrade to I
|
||||
data: Invalid, // I$ never downgrade with data to writeback
|
||||
child: ?
|
||||
};
|
||||
rsToPQ.enq(resp);
|
||||
pRqMshr.sendRsToP_pRq.releaseEntry(n); // mshr entry released
|
||||
$display("%t I %m sendRsToP: ", $time,
|
||||
fshow(rsToPIndexQ.first), " ; ",
|
||||
fshow(req), " ; ",
|
||||
fshow(resp)
|
||||
);
|
||||
doAssert(req.toState == I, "I$ only has downgrade req to I");
|
||||
endrule
|
||||
|
||||
rule sendRqToP;
|
||||
rqToPIndexQ.deq;
|
||||
cRqIdxT n = rqToPIndexQ.first;
|
||||
procRqT req = cRqMshr.sendRqToP.getRq(n);
|
||||
cRqSlotT slot = cRqMshr.sendRqToP.getSlot(n);
|
||||
cRqToPT cRqToP = CRqMsg {
|
||||
addr: req.addr,
|
||||
fromState: I, // I$ upgrade from I
|
||||
toState: S, // I$ upgrade to S
|
||||
canUpToE: False,
|
||||
id: slot.way,
|
||||
child: ?
|
||||
};
|
||||
rqToPQ.enq(cRqToP);
|
||||
$display("%t I %m sendRqToP: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(req), " ; ",
|
||||
fshow(slot), " ; ",
|
||||
fshow(cRqToP)
|
||||
);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: start miss timer
|
||||
latTimer.start(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
// last stage of pipeline: process req
|
||||
|
||||
// XXX: in L1, pRq cannot exist in dependency chain
|
||||
// because there are only two ways to include pRq into chain
|
||||
// (1) append to a cRq that could finish, but such cRq must have been directly reponded
|
||||
// (2) overtake cRq (S->M), but such downgrade can be done instaneously without the need of chaining
|
||||
// (this cannot happen in I$)
|
||||
// Thus, dependency chain in L1 only contains cRq
|
||||
|
||||
// pipeline outputs
|
||||
pipeOutT pipeOut = pipeline.first;
|
||||
ramDataT ram = pipeOut.ram;
|
||||
// get proc req to select from cRqMshr
|
||||
procRqT pipeOutCRq = cRqMshr.pipelineResp.getRq(
|
||||
case(pipeOut.cmd) matches
|
||||
tagged L1CRq .n: (n);
|
||||
default: (fromMaybe(0, ram.info.owner)); // L1PRs
|
||||
endcase
|
||||
);
|
||||
|
||||
// function to get superscaler inst read result
|
||||
function resultT readInst(Line line, Addr addr);
|
||||
Vector#(LineSzInst, Instruction) instVec = unpack(pack(line));
|
||||
// the start offset for reading inst
|
||||
LineInstOffset startSel = getLineInstOffset(addr);
|
||||
// calculate the maximum inst count that could be read from line
|
||||
LineInstOffset maxCntMinusOne = maxBound - startSel;
|
||||
// read inst superscalaer
|
||||
resultT val = ?;
|
||||
for(Integer i = 0; i < valueof(supSz); i = i+1) begin
|
||||
if(fromInteger(i) <= maxCntMinusOne) begin
|
||||
LineInstOffset sel = startSel + fromInteger(i);
|
||||
val[i] = Valid (instVec[sel]);
|
||||
end
|
||||
else begin
|
||||
val[i] = Invalid;
|
||||
end
|
||||
end
|
||||
return val;
|
||||
endfunction
|
||||
|
||||
// function to process cRq hit (MSHR slot may have garbage)
|
||||
function Action cRqHit(cRqIdxT n, procRqT req);
|
||||
action
|
||||
$display("%t I %m pipelineResp: Hit func: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(req)
|
||||
);
|
||||
// check tag & cs: even this function is called by pRs, tag should match,
|
||||
// because tag is written into cache before sending req to parent
|
||||
doAssert(ram.info.tag == getTag(req.addr) && ram.info.cs == S,
|
||||
"cRqHit but tag or cs incorrect"
|
||||
);
|
||||
// deq pipeline or swap in successor
|
||||
Maybe#(cRqIdxT) succ = cRqMshr.pipelineResp.getSucc(n);
|
||||
pipeline.deqWrite(succ, RamData {
|
||||
info: CacheInfo {
|
||||
tag: getTag(req.addr), // should be the same as original tag
|
||||
cs: ram.info.cs, // use cs in ram
|
||||
dir: ?,
|
||||
owner: succ,
|
||||
other: ?
|
||||
},
|
||||
line: ram.line
|
||||
}, True); // hit, so update rep info
|
||||
// process req to get superscalar inst read results
|
||||
// set MSHR entry as Done & save inst results
|
||||
let instResult = readInst(ram.line, req.addr);
|
||||
cRqMshr.pipelineResp.setResult(n, instResult);
|
||||
cRqMshr.pipelineResp.setStateSlot(n, Done, ?);
|
||||
$display("%t I %m pipelineResp: Hit func: update ram: ", $time,
|
||||
fshow(succ), " ; ", fshow(instResult)
|
||||
);
|
||||
`ifdef DEBUG_ICACHE
|
||||
// signal that this req is performed
|
||||
cRqDoneQ.enq(DebugICacheResp {
|
||||
id: req.id,
|
||||
line: ram.line
|
||||
});
|
||||
`endif
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
rule pipelineResp_cRq(pipeOut.cmd matches tagged L1CRq .n);
|
||||
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
|
||||
|
||||
procRqT procRq = pipeOutCRq;
|
||||
$display("%t I %m pipelineResp: cRq: ", $time, fshow(n), " ; ", fshow(procRq));
|
||||
|
||||
// find end of dependency chain
|
||||
Maybe#(cRqIdxT) cRqEOC = cRqMshr.pipelineResp.searchEndOfChain(procRq.addr);
|
||||
|
||||
// function to process cRq miss without replacement (MSHR slot may have garbage)
|
||||
function Action cRqMissNoReplacement;
|
||||
action
|
||||
cRqSlotT cSlot = cRqMshr.pipelineResp.getSlot(n);
|
||||
// it is impossible in L1 to have slot.waitP == True in this function
|
||||
// because cRq is not set to Depend when pRq invalidates it (pRq just directly resp)
|
||||
// and this func is only called when cs < S (otherwise will hit)
|
||||
// because L1 has no children to wait for
|
||||
doAssert(!cSlot.waitP && ram.info.cs == I, "waitP must be false and cs must be I");
|
||||
// Thus we must send req to parent
|
||||
// XXX first send to a temp indexQ to avoid conflict, then merge to rqToPIndexQ later
|
||||
rqToPIndexQ_pipelineResp.enq(n);
|
||||
// update mshr
|
||||
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
|
||||
way: pipeOut.way, // use way from pipeline
|
||||
repTag: ?, // no replacement
|
||||
waitP: True // must fetch from parent
|
||||
});
|
||||
// deq pipeline & set owner, tag
|
||||
pipeline.deqWrite(Invalid, RamData {
|
||||
info: CacheInfo {
|
||||
tag: getTag(procRq.addr), // tag may be garbage if cs == I
|
||||
cs: ram.info.cs,
|
||||
dir: ?,
|
||||
owner: Valid (n), // owner is req itself
|
||||
other: ?
|
||||
},
|
||||
line: ram.line
|
||||
}, False);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// function to do replacement for cRq
|
||||
function Action cRqReplacement;
|
||||
action
|
||||
// deq pipeline
|
||||
pipeline.deqWrite(Invalid, RamData {
|
||||
info: CacheInfo {
|
||||
tag: getTag(procRq.addr), // set to req tag (old tag is replaced right now)
|
||||
cs: I,
|
||||
dir: ?,
|
||||
owner: Valid (n), // owner is req itself
|
||||
other: ?
|
||||
},
|
||||
line: ? // data is no longer used
|
||||
}, False);
|
||||
doAssert(ram.info.cs == S, "I$ replacement only replace S line");
|
||||
// update MSHR to save replaced tag
|
||||
// although we send req to parent later (when resp to parent is sent)
|
||||
// we set state to WaitSt now, since the req to parent is already on schedule
|
||||
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
|
||||
way: pipeOut.way, // use way from pipeline
|
||||
repTag: ram.info.tag, // tag being replaced for sending rs to parent
|
||||
waitP: True
|
||||
});
|
||||
// send replacement resp to parent
|
||||
rsToPIndexQ.enq(CRq (n));
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// function to set cRq to Depend, and make no further change to cache
|
||||
function Action cRqSetDepNoCacheChange;
|
||||
action
|
||||
cRqMshr.pipelineResp.setStateSlot(n, Depend, defaultValue);
|
||||
pipeline.deqWrite(Invalid, pipeOut.ram, False);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
if(ram.info.owner matches tagged Valid .cOwner) begin
|
||||
if(cOwner != n) begin
|
||||
// owner is another cRq, so must just go through tag match
|
||||
// tag match must be hit (because replacement algo won't give a way with owner)
|
||||
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
|
||||
"cRq should hit in tag match"
|
||||
);
|
||||
// should be added to a cRq in dependency chain & deq from pipeline
|
||||
doAssert(isValid(cRqEOC), "cRq hit on another cRq, cRqEOC must be true");
|
||||
cRqMshr.pipelineResp.setSucc(fromMaybe(?, cRqEOC), Valid (n));
|
||||
cRqSetDepNoCacheChange;
|
||||
$display("%t I %m pipelineResp: cRq: own by other cRq ", $time,
|
||||
fshow(cOwner), ", depend on cRq ", fshow(cRqEOC)
|
||||
);
|
||||
end
|
||||
else begin
|
||||
// owner is myself, so must be swapped in
|
||||
// tag should match, since always swapped in by cRq, cs = S
|
||||
doAssert(ram.info.tag == getTag(procRq.addr) && ram.info.cs == S,
|
||||
"cRq swapped in by previous cRq, tag must match & cs = S"
|
||||
);
|
||||
// Hit
|
||||
$display("%t I %m pipelineResp: cRq: own by itself, hit", $time);
|
||||
cRqHit(n, procRq);
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// cache has no owner, cRq must just go through tag match
|
||||
// check for cRqEOC to append to dependency chain
|
||||
if(cRqEOC matches tagged Valid .k) begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, depend on cRq ", $time, fshow(k));
|
||||
cRqMshr.pipelineResp.setSucc(k, Valid (n));
|
||||
cRqSetDepNoCacheChange;
|
||||
end
|
||||
else if(ram.info.cs == I || ram.info.tag == getTag(procRq.addr)) begin
|
||||
// No Replacement necessary
|
||||
if(ram.info.cs > I) begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, hit", $time);
|
||||
cRqHit(n, procRq);
|
||||
end
|
||||
else begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, miss no replace", $time);
|
||||
cRqMissNoReplacement;
|
||||
end
|
||||
end
|
||||
else begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, replace", $time);
|
||||
cRqReplacement;
|
||||
end
|
||||
end
|
||||
endrule
|
||||
|
||||
rule pipelineResp_pRs(pipeOut.cmd == L1PRs);
|
||||
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
|
||||
$display("%t I %m pipelineResp: pRs: ", $time);
|
||||
|
||||
if(ram.info.owner matches tagged Valid .cOwner) begin
|
||||
procRqT procRq = pipeOutCRq;
|
||||
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
|
||||
"pRs must be a hit"
|
||||
);
|
||||
cRqHit(cOwner, procRq);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: miss cRq
|
||||
incrMissCnt(cOwner);
|
||||
`endif
|
||||
end
|
||||
else begin
|
||||
doAssert(False, ("pRs owner must match some cRq"));
|
||||
end
|
||||
endrule
|
||||
|
||||
rule pipelineResp_pRq(pipeOut.cmd matches tagged L1PRq .n);
|
||||
pRqFromPT pRq = pRqMshr.pipelineResp.getRq(n);
|
||||
$display("%t I %m pipelineResp: pRq: ", $time, fshow(n), " ; ", fshow(pRq));
|
||||
|
||||
doAssert(pRq.toState == I, "I$ pRq only downgrade to I");
|
||||
|
||||
// pRq is never in dependency chain, so it is never swapped in
|
||||
// pRq must go through tag match, which either returns a tag matched way or asserts pRqMiss
|
||||
// In I$ a tag matched way should always be processed since pRq always downgrades to I
|
||||
// pRq is always directly handled: either dropped or Done
|
||||
|
||||
if(pipeOut.pRqMiss) begin
|
||||
$display("%t I %m pipelineResp: pRq: drop", $time);
|
||||
// pRq can be directly dropped, no successor (since just go through pipeline)
|
||||
pRqMshr.pipelineResp.releaseEntry(n);
|
||||
pipeline.deqWrite(Invalid, pipeOut.ram, False);
|
||||
end
|
||||
else begin
|
||||
$display("%t I %m pipelineResp: pRq: valid process", $time);
|
||||
// should process pRq
|
||||
doAssert(ram.info.cs == S && pRq.toState == I && ram.info.tag == getTag(pRq.addr),
|
||||
"pRq should be processed"
|
||||
);
|
||||
// line cannot be owned, because
|
||||
// (1) pRq never own cache line
|
||||
// (2) if owned by cRq, cRq would have hit and released ownership
|
||||
doAssert(ram.info.owner == Invalid, "pRq cannot hit on line owned by anyone");
|
||||
// write ram: set cs to I
|
||||
pipeline.deqWrite(Invalid, RamData {
|
||||
info: CacheInfo {
|
||||
tag: ram.info.tag,
|
||||
cs: I, // I$ is always downgraded by pRq to I
|
||||
dir: ?,
|
||||
owner: Invalid, // no successor
|
||||
other: ?
|
||||
},
|
||||
line: ? // line is not useful
|
||||
}, False);
|
||||
// pRq is done
|
||||
pRqMshr.pipelineResp.setDone(n);
|
||||
// send resp to parent
|
||||
rsToPIndexQ.enq(PRq (n));
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef SECURITY
|
||||
rule pipelineResp_flush(
|
||||
!flushDone &&& !flushRespDone &&&
|
||||
pipeOut.cmd matches tagged L1Flush .flush
|
||||
);
|
||||
pRqIdxT n = flush.mshrIdx;
|
||||
$display("%t I %m pipelineResp: flush: ", $time, fshow(flush));
|
||||
|
||||
// During flush, cRq MSHR is empty, so cache line cannot have owner
|
||||
doAssert(ram.info.owner == Invalid, "flushing line cannot have owner");
|
||||
|
||||
// flush always goes through cache pipeline, and is directly handled
|
||||
// here: either dropped or Done
|
||||
if(ram.info.cs == I) begin
|
||||
$display("%t I %m pipelineResp: flush: drop", $time);
|
||||
// flush can be directly dropped
|
||||
pRqMshr.pipelineResp.releaseEntry(n);
|
||||
end
|
||||
else begin
|
||||
$display("%t I %m pipelineResp: flush: valid process", $time);
|
||||
pRqMshr.pipelineResp.setDone(n);
|
||||
rsToPIndexQ.enq(PRq (n));
|
||||
// record the flushed addr in MSHR so that sendRsToP rule knows
|
||||
// which addr is invalidated
|
||||
Bit#(LgLineSzBytes) offset = 0;
|
||||
Addr addr = {ram.info.tag, flush.index, bankId, offset};
|
||||
pRqMshr.pipelineResp.setFlushAddr(n, addr);
|
||||
end
|
||||
|
||||
// always clear the cache line
|
||||
pipeline.deqWrite(Invalid, RamData {
|
||||
info: CacheInfo {
|
||||
tag: ?,
|
||||
cs: I, // downgraded to I
|
||||
dir: ?,
|
||||
owner: Invalid, // no successor
|
||||
other: ?
|
||||
},
|
||||
line: ?
|
||||
}, False);
|
||||
|
||||
// check if we have finished all flush
|
||||
if (flush.index == maxBound &&
|
||||
pipeOut.way == fromInteger(valueof(wayNum) - 1)) begin
|
||||
flushRespDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
rule completeFlush(!flushDone && flushReqStart && flushReqDone && flushRespDone);
|
||||
flushDone <= True;
|
||||
flushReqStart <= False;
|
||||
flushReqDone <= False;
|
||||
flushRespDone <= False;
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// merge rq to parent index into indexQ
|
||||
rule rqIndexFromPipelineResp;
|
||||
let n <- toGet(rqToPIndexQ_pipelineResp).get;
|
||||
rqToPIndexQ.enq(n);
|
||||
endrule
|
||||
|
||||
(* descending_urgency = "rqIndexFromPipelineResp, rqIndexFromSendRsToP" *)
|
||||
rule rqIndexFromSendRsToP;
|
||||
let n <- toGet(rqToPIndexQ_sendRsToP).get;
|
||||
rqToPIndexQ.enq(n);
|
||||
endrule
|
||||
|
||||
interface ChildCacheToParent to_parent;
|
||||
interface rsToP = toFifoDeq(rsToPQ);
|
||||
interface rqToP = toFifoDeq(rqToPQ);
|
||||
interface fromP = toFifoEnq(fromPQ);
|
||||
endinterface
|
||||
|
||||
interface InstServer to_proc;
|
||||
interface Put req;
|
||||
method Action put(Addr addr);
|
||||
rqFromCQ.enq(addr);
|
||||
endmethod
|
||||
endinterface
|
||||
interface Get resp;
|
||||
method ActionValue#(resultT) get if(
|
||||
cRqMshr.sendRsToC.getResult(cRqIndexQ.first) matches tagged Valid .inst
|
||||
);
|
||||
cRqIndexQ.deq;
|
||||
cRqMshr.sendRsToC.releaseEntry(cRqIndexQ.first); // release MSHR entry
|
||||
$display("%t I %m sendRsToC: ", $time,
|
||||
fshow(cRqIndexQ.first), " ; ",
|
||||
fshow(inst)
|
||||
);
|
||||
return inst;
|
||||
endmethod
|
||||
endinterface
|
||||
`ifdef DEBUG_ICACHE
|
||||
interface done = toGet(cRqDoneQ);
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
interface Get cRqStuck;
|
||||
method ActionValue#(ICRqStuck) get;
|
||||
let s <- cRqMshr.stuck.get;
|
||||
return ICRqStuck {
|
||||
addr: s.req.addr,
|
||||
state: s.state,
|
||||
waitP: s.waitP
|
||||
};
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface pRqStuck = pRqMshr.stuck;
|
||||
|
||||
`ifdef SECURITY
|
||||
method Action flush if(flushDone);
|
||||
flushDone <= False;
|
||||
endmethod
|
||||
method flush_done = flushDone._read;
|
||||
`else
|
||||
method flush = noAction;
|
||||
method flush_done = True;
|
||||
`endif
|
||||
|
||||
method Action setPerfStatus(Bool stats);
|
||||
`ifdef PERF_COUNT
|
||||
doStats <= stats;
|
||||
`else
|
||||
noAction;
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method Data getPerfData(L1IPerfType t);
|
||||
return (case(t)
|
||||
`ifdef PERF_COUNT
|
||||
L1ILdCnt: ldCnt;
|
||||
L1ILdMissCnt: ldMissCnt;
|
||||
L1ILdMissLat: ldMissLat;
|
||||
`endif
|
||||
default: 0;
|
||||
endcase);
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
|
||||
// Scheduling note
|
||||
|
||||
// cRqTransfer (toC.req.put): write new cRq MSHR entry, cRqMshr.getEmptyEntry
|
||||
|
||||
// pRqTransfer: write new pRq MSHR entry, pRqMshr.getEmptyEntry
|
||||
|
||||
// pRsTransfer: -
|
||||
|
||||
// sendRsToC (toC.resp.get): read cRq MSHR result, releaseEntry
|
||||
|
||||
// sendRsToP_cRq: read cRq MSHR req/slot that is replacing
|
||||
|
||||
// sendRsToP_pRq: read pRq MSHR entry that is responding, pRqMshr.releaseEntry
|
||||
|
||||
// sendRqToP: read cRq MSHR req/slot that is requesting parent
|
||||
|
||||
// pipelineResp_cRq:
|
||||
// -- read cRq MSHR req/state/slot currently processed
|
||||
// -- write cRq MSHR state/slot/result currently processed
|
||||
// -- write succ of some existing cRq MSHR entry (in WaitNewTag or WaitSt)
|
||||
// -- read all state/req/succ in cRq MSHR entry (searchEOC)
|
||||
// -- not affected by write in cRqTransfer (state change is Empty->Init)
|
||||
// -- not affected by write in sendRsC (state change is Done->Empty)
|
||||
|
||||
// pipelineResp_pRs:
|
||||
// -- read cRq MSHR req/succ, write cRq MSHR state/slot/result
|
||||
|
||||
// pipelineResp_pRq:
|
||||
// -- r/w pRq MSHR entry, pRqMshr.releaseEntry
|
||||
|
||||
// ---- conflict analysis ----
|
||||
|
||||
// XXXTransfer is conflict with each other
|
||||
// Impl of getEmptyEntry and releaseEntry ensures that they are not on the same entry (e.g. cRqTransfer v.s. sendRsToC)
|
||||
// XXXTransfer should operate on different cRq/pRq from other rules
|
||||
|
||||
// sendRsToC is ordered after pipelineResp to save 1 cycle in I$ latency
|
||||
|
||||
// sendRqToP and sendRsToP_cRq are read only
|
||||
|
||||
// sendRsToP_pRq is operating on different pRq from pipelineResp_pRq (since we use CF index FIFO)
|
||||
|
||||
// ---- conclusion ----
|
||||
|
||||
// rules/methods are operating on different MSHR entries, except pipelineResp v.s. sendRsToC
|
||||
|
||||
// we have 5 ports from cRq MSHR
|
||||
// 1. cRqTransfer
|
||||
// 2. sendRsToC
|
||||
// 3. sendRsToP_cRq
|
||||
// 4. sendRqToP
|
||||
// 5. pipelineResp
|
||||
|
||||
// we have 3 ports from pRq MSHR
|
||||
// 1. pRqTransfer
|
||||
// 2. sendRsToP_pRq
|
||||
// 3. pipelineResp
|
||||
|
||||
// safe version: use EHR ports
|
||||
// sendRsToP_cRq/sendRqToP/pipelineResp < sendRsToC < cRqTransfer
|
||||
// pipelineResp < sendRsToP_pRq < pRqTransfer
|
||||
// (note there is no bypass path from pipelineResp to sendRsToP_pRq since sendRsToP_pRq only reads pRq)
|
||||
|
||||
// unsafe version: all reads read the original reg value, except sendRsToC, which should bypass from pipelineResp
|
||||
// all writes are cononicalized. NOTE: writes of sendRsToC should be after pipelineResp
|
||||
// we maintain the logical ordering in safe version
|
||||
|
||||
353
src_Core/RISCY_OOO/coherence/src/ICRqMshr.bsv
Normal file
353
src_Core/RISCY_OOO/coherence/src/ICRqMshr.bsv
Normal file
@@ -0,0 +1,353 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import RegFile::*;
|
||||
import FIFO::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import DefaultValue::*;
|
||||
import Ehr::*;
|
||||
import MshrDeadlockChecker::*;
|
||||
|
||||
// MSHR dependency chain invariant:
|
||||
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
|
||||
|
||||
// in L1, pRq is always handled immediately, so cRq never depends on pRq and vice versa
|
||||
|
||||
// CRq MSHR entry state
|
||||
typedef enum {
|
||||
Empty,
|
||||
Init,
|
||||
WaitSt, // wait pRs/cRs to come
|
||||
Done, // resp is in index FIFO
|
||||
Depend
|
||||
} ICRqState deriving(Bits, Eq, FShow);
|
||||
|
||||
// CRq info returned to outside
|
||||
typedef struct {
|
||||
wayT way; // the way to occupy
|
||||
tagT repTag; // tag being replaced
|
||||
Bool waitP; // waiting for parent resp
|
||||
} ICRqSlot#(type wayT, type tagT) deriving(Bits, Eq, FShow);
|
||||
|
||||
instance DefaultValue#(ICRqSlot#(wayT, tagT));
|
||||
defaultValue = ICRqSlot {
|
||||
way: ?,
|
||||
repTag: ?,
|
||||
waitP: False
|
||||
};
|
||||
endinstance
|
||||
|
||||
typedef struct {
|
||||
reqT req;
|
||||
ICRqState state;
|
||||
Bool waitP;
|
||||
} ICRqMshrStuck#(type reqT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// MSHR data is purely for replacement resp to parent
|
||||
// (resp to processor is done immediately, no data buffering needed)
|
||||
|
||||
// port for sendRsToP_cRq
|
||||
interface ICRqMshr_sendRsToP_cRq#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
// port for sendRqToP
|
||||
interface ICRqMshr_sendRqToP#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
// port for pipelineResp
|
||||
interface ICRqMshr_pipelineResp#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT,
|
||||
type resultT
|
||||
);
|
||||
method ICRqState getState(Bit#(TLog#(cRqNum)) n);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method ICRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
|
||||
method Action setResult(Bit#(TLog#(cRqNum)) n, resultT r); // set a valid result
|
||||
method Action setStateSlot(
|
||||
Bit#(TLog#(cRqNum)) n,
|
||||
ICRqState state,
|
||||
ICRqSlot#(wayT, tagT) slot
|
||||
);
|
||||
// can only change state to NON-Empty state
|
||||
// cannot be used to release MSHR entry (use releaseSlot instead)
|
||||
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) getSucc(Bit#(TLog#(cRqNum)) n);
|
||||
method Action setSucc(Bit#(TLog#(cRqNum)) n, Maybe#(Bit#(TLog#(cRqNum))) succ);
|
||||
// index in setSucc is usually different from other getXXX methods
|
||||
|
||||
// find existing cRq which has gone through pipeline, but not in Done state, and has not successor
|
||||
// i.e. search the end of dependency chain
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
|
||||
endinterface
|
||||
|
||||
interface ICRqMshr_sendRsToC#(
|
||||
numeric type cRqNum,
|
||||
type resultT
|
||||
);
|
||||
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(resultT) getResult(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
interface ICRqMshr#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT, // child req type
|
||||
type resultT // inst result type
|
||||
);
|
||||
// port for cRqTransfer, initialization is done inside method
|
||||
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r);
|
||||
|
||||
// port for sendRsToC
|
||||
interface ICRqMshr_sendRsToC#(cRqNum, resultT) sendRsToC;
|
||||
|
||||
// port for sendRsToP_cRq
|
||||
interface ICRqMshr_sendRsToP_cRq#(cRqNum, wayT, tagT, reqT) sendRsToP_cRq;
|
||||
|
||||
// port for sendRqToP
|
||||
interface ICRqMshr_sendRqToP#(cRqNum, wayT, tagT, reqT) sendRqToP;
|
||||
|
||||
// port for pipelineResp
|
||||
interface ICRqMshr_pipelineResp#(cRqNum, wayT, tagT, reqT, resultT) pipelineResp;
|
||||
|
||||
// port for security flush
|
||||
method Bool emptyForFlush;
|
||||
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(ICRqMshrStuck#(reqT)) stuck;
|
||||
endinterface
|
||||
|
||||
|
||||
//////////////////
|
||||
// safe version //
|
||||
//////////////////
|
||||
module mkICRqMshrSafe#(
|
||||
function Addr getAddrFromReq(reqT r)
|
||||
)(
|
||||
ICRqMshr#(cRqNum, wayT, tagT, reqT, resultT)
|
||||
) provisos (
|
||||
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
|
||||
Alias#(slotT, ICRqSlot#(wayT, tagT)),
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(tagT, Bit#(_tagSz)),
|
||||
Bits#(reqT, _reqSz),
|
||||
Bits#(resultT, _resultTSz)
|
||||
);
|
||||
// EHR ports
|
||||
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
|
||||
Integer cRqTransfer_port = 2;
|
||||
Integer sendRsToC_port = 1; // create a bypass behavior from pipelineResp to sendRsToC (to save a cycle)
|
||||
Integer pipelineResp_port = 0;
|
||||
Integer sendRqToP_port = 0; // sendRqToP is read only
|
||||
Integer sendRsToP_cRq_port = 0; // sendRsToP_cRq is read only
|
||||
Integer flush_port = 0; // flush port is read only
|
||||
|
||||
// MSHR entry state
|
||||
Vector#(cRqNum, Ehr#(3, ICRqState)) stateVec <- replicateM(mkEhr(Empty));
|
||||
// cRq req contents
|
||||
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
|
||||
// cRq mshr slots
|
||||
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(defaultValue));
|
||||
// result
|
||||
Vector#(cRqNum, Ehr#(3, Maybe#(resultT))) resultVec <- replicateM(mkEhr(Invalid));
|
||||
// successor valid bit
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) succValidVec <- replicateM(mkEhr(False));
|
||||
// successor MSHR index
|
||||
RegFile#(cRqIndexT, cRqIndexT) succFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
|
||||
// empty entry FIFO
|
||||
FIFO#(cRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(cRqNum));
|
||||
|
||||
// empty entry FIFO needs initialization
|
||||
Reg#(Bool) inited <- mkReg(False);
|
||||
Reg#(cRqIndexT) initIdx <- mkReg(0);
|
||||
|
||||
rule initEmptyEntry(!inited);
|
||||
emptyEntryQ.enq(initIdx);
|
||||
initIdx <= initIdx + 1;
|
||||
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
|
||||
inited <= True;
|
||||
$display("%t ICRqMshrSafe %m: init empty entry done", $time);
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
|
||||
FIFO#(ICRqMshrStuck#(reqT)) stuckQ <- mkFIFO1;
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock;
|
||||
let stuckIdx <- checker.getStuckIdx;
|
||||
if(stuckIdx matches tagged Valid .n) begin
|
||||
stuckQ.enq(ICRqMshrStuck {
|
||||
req: reqVec[n][0],
|
||||
state: stateVec[n][0],
|
||||
waitP: slotVec[n][0].waitP
|
||||
});
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r) if(inited);
|
||||
emptyEntryQ.deq;
|
||||
cRqIndexT n = emptyEntryQ.first;
|
||||
stateVec[n][cRqTransfer_port] <= Init;
|
||||
slotVec[n][cRqTransfer_port] <= defaultValue;
|
||||
resultVec[n][cRqTransfer_port] <= Invalid;
|
||||
succValidVec[n][cRqTransfer_port] <= False;
|
||||
reqVec[n][cRqTransfer_port] <= r;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.initEntry(n);
|
||||
`endif
|
||||
return n;
|
||||
endmethod
|
||||
|
||||
interface ICRqMshr_sendRsToC sendRsToC;
|
||||
method Action releaseEntry(cRqIndexT n) if(inited);
|
||||
emptyEntryQ.enq(n);
|
||||
stateVec[n][sendRsToC_port] <= Empty;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method Maybe#(resultT) getResult(Bit#(TLog#(cRqNum)) n);
|
||||
return resultVec[n][sendRsToC_port];
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface ICRqMshr_sendRsToP_cRq sendRsToP_cRq;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRsToP_cRq_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendRsToP_cRq_port];
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface ICRqMshr_sendRqToP sendRqToP;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRqToP_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendRqToP_port];
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface ICRqMshr_pipelineResp pipelineResp;
|
||||
method ICRqState getState(cRqIndexT n);
|
||||
return stateVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method Action setStateSlot(cRqIndexT n, ICRqState state, slotT slot);
|
||||
doAssert(state != Empty, "use releaseEntry to set state to Empty");
|
||||
stateVec[n][pipelineResp_port] <= state;
|
||||
slotVec[n][pipelineResp_port] <= slot;
|
||||
endmethod
|
||||
|
||||
method Action setResult(cRqIndexT n, resultT r);
|
||||
resultVec[n][pipelineResp_port] <= Valid (r);
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) getSucc(cRqIndexT n);
|
||||
return succValidVec[n][pipelineResp_port] ? (Valid (succFile.sub(n))) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action setSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
|
||||
succValidVec[n][pipelineResp_port] <= isValid(succ);
|
||||
succFile.upd(n, fromMaybe(?, succ));
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
|
||||
function Bool isEndOfChain(Integer i);
|
||||
// check entry i is end of chain or not
|
||||
ICRqState state = stateVec[i][pipelineResp_port];
|
||||
Bool notDone = state != Done;
|
||||
Bool processedOnce = state != Empty && state != Init;
|
||||
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
|
||||
Bool noSucc = !succValidVec[i][pipelineResp_port];
|
||||
return notDone && processedOnce && addrMatch && noSucc;
|
||||
endfunction
|
||||
Vector#(cRqNum, Integer) idxVec = genVector;
|
||||
return searchIndex(isEndOfChain, idxVec);
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
method Bool emptyForFlush;
|
||||
function Bool isEmpty(Integer i) = stateVec[i][flush_port] == Empty;
|
||||
Vector#(cRqNum, Integer) idxVec = genVector;
|
||||
return all(isEmpty, idxVec);
|
||||
endmethod
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface stuck = toGet(stuckQ);
|
||||
`else
|
||||
interface stuck = nullGet;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
// exported version
|
||||
module mkICRqMshr#(
|
||||
function Addr getAddrFromReq(reqT r)
|
||||
)(
|
||||
ICRqMshr#(cRqNum, wayT, tagT, reqT, resultT)
|
||||
) provisos (
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(tagT, Bit#(_tagSz)),
|
||||
Bits#(reqT, _reqSz),
|
||||
Bits#(resultT, _resultTSz)
|
||||
);
|
||||
let m <- mkICRqMshrSafe(getAddrFromReq);
|
||||
return m;
|
||||
endmodule
|
||||
214
src_Core/RISCY_OOO/coherence/src/IPRqMshr.bsv
Normal file
214
src_Core/RISCY_OOO/coherence/src/IPRqMshr.bsv
Normal file
@@ -0,0 +1,214 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import RegFile::*;
|
||||
import FIFO::*;
|
||||
import FShow::*;
|
||||
import GetPut::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import DefaultValue::*;
|
||||
import Ehr::*;
|
||||
import Fifo::*;
|
||||
import MshrDeadlockChecker::*;
|
||||
|
||||
// MSHR dependency chain invariant:
|
||||
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
|
||||
|
||||
// in L1, pRq is always directly handled at the end of pipeline
|
||||
|
||||
// PRq MSHR entry state
|
||||
typedef enum {
|
||||
Empty,
|
||||
Init,
|
||||
Done
|
||||
} IPRqState deriving (Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
IPRqState state;
|
||||
} IPRqMshrStuck deriving(Bits, Eq, FShow);
|
||||
|
||||
interface IPRqMshr_sendRsToP_pRq#(numeric type pRqNum);
|
||||
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
|
||||
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
|
||||
endinterface
|
||||
|
||||
interface IPRqMshr_pipelineResp#(numeric type pRqNum);
|
||||
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
|
||||
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
|
||||
method Action setDone(Bit#(TLog#(pRqNum)) n);
|
||||
`ifdef SECURITY
|
||||
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
interface IPRqMshr#(numeric type pRqNum);
|
||||
// port for pRqTransfer
|
||||
method ActionValue#(Bit#(TLog#(pRqNum))) getEmptyEntryInit(PRqMsg#(void) r);
|
||||
|
||||
// port for sendRsToP_pRq
|
||||
interface IPRqMshr_sendRsToP_pRq#(pRqNum) sendRsToP_pRq;
|
||||
|
||||
// port for pipelineResp
|
||||
interface IPRqMshr_pipelineResp#(pRqNum) pipelineResp;
|
||||
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(IPRqMshrStuck) stuck;
|
||||
endinterface
|
||||
|
||||
|
||||
//////////////////
|
||||
// safe version //
|
||||
//////////////////
|
||||
module mkIPRqMshrSafe(
|
||||
IPRqMshr#(pRqNum)
|
||||
) provisos(
|
||||
Alias#(pRqIndexT, Bit#(TLog#(pRqNum)))
|
||||
);
|
||||
// EHR port
|
||||
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
|
||||
Integer pRqTransfer_port = 2;
|
||||
Integer sendRsToP_pRq_port = 1;
|
||||
Integer pipelineResp_port = 0;
|
||||
|
||||
// MSHR entry state
|
||||
Vector#(pRqNum, Ehr#(3, IPRqState)) stateVec <- replicateM(mkEhr(Empty));
|
||||
Vector#(pRqNum, Ehr#(3, PRqMsg#(void))) reqVec <- replicateM(mkEhr(?));
|
||||
// empty entry FIFO
|
||||
FIFO#(pRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(pRqNum));
|
||||
|
||||
// empty entry FIFO needs initialization
|
||||
Reg#(Bool) inited <- mkReg(False);
|
||||
Reg#(pRqIndexT) initIdx <- mkReg(0);
|
||||
|
||||
// released entry index fifos
|
||||
Fifo#(1, pRqIndexT) releaseEntryQ_sendRsToP_pRq <- mkBypassFifo;
|
||||
Fifo#(1, pRqIndexT) releaseEntryQ_pipelineResp <- mkBypassFifo;
|
||||
|
||||
rule initEmptyEntry(!inited);
|
||||
emptyEntryQ.enq(initIdx);
|
||||
initIdx <= initIdx + 1;
|
||||
if(initIdx == fromInteger(valueOf(pRqNum) - 1)) begin
|
||||
inited <= True;
|
||||
$display("%t IPRqMshrSafe %m: init empty entry done", $time);
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
MshrDeadlockChecker#(pRqNum) checker <- mkMshrDeadlockChecker;
|
||||
FIFO#(IPRqMshrStuck) stuckQ <- mkFIFO1;
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock;
|
||||
let stuckIdx <- checker.getStuckIdx;
|
||||
if(stuckIdx matches tagged Valid .n) begin
|
||||
stuckQ.enq(IPRqMshrStuck {
|
||||
addr: reqVec[n][0].addr,
|
||||
toState: reqVec[n][0].toState,
|
||||
state: stateVec[n][0]
|
||||
});
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
rule doReleaseEntry_sendRsToP_pRq(inited);
|
||||
let n <- toGet(releaseEntryQ_sendRsToP_pRq).get;
|
||||
emptyEntryQ.enq(n);
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
(* descending_urgency = "doReleaseEntry_sendRsToP_pRq, doReleaseEntry_pipelineResp" *)
|
||||
rule doReleaseEntry_pipelineResp(inited);
|
||||
let n <- toGet(releaseEntryQ_pipelineResp).get;
|
||||
emptyEntryQ.enq(n);
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
method ActionValue#(pRqIndexT) getEmptyEntryInit(PRqMsg#(void) r) if(inited);
|
||||
emptyEntryQ.deq;
|
||||
pRqIndexT n = emptyEntryQ.first;
|
||||
stateVec[n][pRqTransfer_port] <= Init;
|
||||
reqVec[n][pRqTransfer_port] <= r;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.initEntry(n);
|
||||
`endif
|
||||
return n;
|
||||
endmethod
|
||||
|
||||
interface IPRqMshr_sendRsToP_pRq sendRsToP_pRq;
|
||||
method PRqMsg#(void) getRq(pRqIndexT n);
|
||||
return reqVec[n][sendRsToP_pRq_port];
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(pRqIndexT n) if(inited);
|
||||
releaseEntryQ_sendRsToP_pRq.enq(n);
|
||||
stateVec[n][sendRsToP_pRq_port] <= Empty;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface IPRqMshr_pipelineResp pipelineResp;
|
||||
method PRqMsg#(void) getRq(pRqIndexT n);
|
||||
return reqVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method Action setDone(pRqIndexT n);
|
||||
stateVec[n][pipelineResp_port] <= Done;
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(pRqIndexT n) if(inited);
|
||||
releaseEntryQ_pipelineResp.enq(n);
|
||||
stateVec[n][pipelineResp_port] <= Empty;
|
||||
endmethod
|
||||
|
||||
`ifdef SECURITY
|
||||
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
|
||||
reqVec[n][pipelineResp_port] <= PRqMsg {
|
||||
addr: a,
|
||||
toState: I,
|
||||
child: ?
|
||||
};
|
||||
endmethod
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface stuck = toGet(stuckQ);
|
||||
`else
|
||||
interface stuck = nullGet;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
|
||||
// exportd version
|
||||
module mkIPRqMshr(IPRqMshr#(pRqNum));
|
||||
let m <- mkIPRqMshrSafe;
|
||||
return m;
|
||||
endmodule
|
||||
1260
src_Core/RISCY_OOO/coherence/src/L1Bank.bsv
Normal file
1260
src_Core/RISCY_OOO/coherence/src/L1Bank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
378
src_Core/RISCY_OOO/coherence/src/L1CRqMshr.bsv
Normal file
378
src_Core/RISCY_OOO/coherence/src/L1CRqMshr.bsv
Normal file
@@ -0,0 +1,378 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import RegFile::*;
|
||||
import FIFO::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import DefaultValue::*;
|
||||
import Ehr::*;
|
||||
import MshrDeadlockChecker::*;
|
||||
|
||||
// MSHR dependency chain invariant:
|
||||
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
|
||||
|
||||
// in L1, pRq is always handled immediately, so cRq never depends on pRq and vice versa
|
||||
|
||||
// CRq MSHR entry state
|
||||
typedef enum {
|
||||
Empty,
|
||||
Init,
|
||||
WaitNewTag, // waiting replacement resp to send (but tag in RAM is already updated)
|
||||
WaitSt, // wait pRs/cRs to come
|
||||
Done, // resp is in index FIFO
|
||||
Depend
|
||||
} L1CRqState deriving(Bits, Eq, FShow);
|
||||
|
||||
// CRq info returned to outside
|
||||
typedef struct {
|
||||
wayT way; // the way to occupy
|
||||
Msi cs; // current cache MSI, used in sending upgrade req to parent
|
||||
tagT repTag; // tag being replaced: only valid in WaitOld/NewTag states
|
||||
Bool waitP; // waiting for parent resp
|
||||
} L1CRqSlot#(type wayT, type tagT) deriving(Bits, Eq, FShow);
|
||||
|
||||
instance DefaultValue#(L1CRqSlot#(wayT, tagT));
|
||||
defaultValue = L1CRqSlot {
|
||||
way: ?,
|
||||
cs: ?,
|
||||
repTag: ?,
|
||||
waitP: False
|
||||
};
|
||||
endinstance
|
||||
|
||||
typedef struct {
|
||||
reqT req;
|
||||
L1CRqState state;
|
||||
Msi slotCs;
|
||||
Bool waitP;
|
||||
} L1CRqMshrStuck#(type reqT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// MSHR data is purely for replacement resp to parent
|
||||
// (resp to processor is done immediately, no data buffering needed)
|
||||
|
||||
// port for cRqTransfer and retry
|
||||
interface L1CRqMshr_transfer#(
|
||||
numeric type cRqNum,
|
||||
type reqT
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r);
|
||||
endinterface
|
||||
|
||||
// port for sendRsToP_cRq
|
||||
interface L1CRqMshr_sendRsToP_cRq#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT
|
||||
);
|
||||
method L1CRqState getState(Bit#(TLog#(cRqNum)) n);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
|
||||
|
||||
method Action setWaitSt_setSlot_clearData(
|
||||
Bit#(TLog#(cRqNum)) n,
|
||||
L1CRqSlot#(wayT, tagT) slot
|
||||
);
|
||||
// data is set to invalid, state is set to WaitSt here
|
||||
endinterface
|
||||
|
||||
// port for sendRqToP
|
||||
interface L1CRqMshr_sendRqToP#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
// port for pipelineResp
|
||||
interface L1CRqMshr_pipelineResp#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT
|
||||
);
|
||||
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
|
||||
|
||||
method L1CRqState getState(Bit#(TLog#(cRqNum)) n);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method L1CRqSlot#(wayT, tagT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
|
||||
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
|
||||
method Action setStateSlot(
|
||||
Bit#(TLog#(cRqNum)) n,
|
||||
L1CRqState state,
|
||||
L1CRqSlot#(wayT, tagT) slot
|
||||
);
|
||||
// can only change state to NON-Empty state
|
||||
// cannot be used to release MSHR entry (use releaseSlot instead)
|
||||
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) getSucc(Bit#(TLog#(cRqNum)) n);
|
||||
method Action setSucc(Bit#(TLog#(cRqNum)) n, Maybe#(Bit#(TLog#(cRqNum))) succ);
|
||||
// index in setSucc is usually different from other getXXX methods
|
||||
|
||||
// find existing cRq which has gone through pipeline, but not in Done state, and has not successor
|
||||
// i.e. search the end of dependency chain
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
|
||||
endinterface
|
||||
|
||||
interface L1CRqMshr#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type reqT // child req type
|
||||
);
|
||||
// port for cRqTransfer and retry
|
||||
interface L1CRqMshr_transfer#(cRqNum, reqT) cRqTransfer;
|
||||
|
||||
// port for sendRsToP_cRq
|
||||
interface L1CRqMshr_sendRsToP_cRq#(cRqNum, wayT, tagT, reqT) sendRsToP_cRq;
|
||||
|
||||
// port for sendRqToP
|
||||
interface L1CRqMshr_sendRqToP#(cRqNum, wayT, tagT, reqT) sendRqToP;
|
||||
|
||||
// port for pipelineResp
|
||||
interface L1CRqMshr_pipelineResp#(cRqNum, wayT, tagT, reqT) pipelineResp;
|
||||
|
||||
// port for security flush
|
||||
method Bool emptyForFlush;
|
||||
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(L1CRqMshrStuck#(reqT)) stuck;
|
||||
endinterface
|
||||
|
||||
|
||||
//////////////////
|
||||
// safe version //
|
||||
//////////////////
|
||||
module mkL1CRqMshrSafe#(
|
||||
function Addr getAddrFromReq(reqT r)
|
||||
)(
|
||||
L1CRqMshr#(cRqNum, wayT, tagT, reqT)
|
||||
) provisos (
|
||||
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
|
||||
Alias#(slotT, L1CRqSlot#(wayT, tagT)),
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(tagT, Bit#(_tagSz)),
|
||||
Bits#(reqT, _reqSz)
|
||||
);
|
||||
// EHR ports
|
||||
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
|
||||
Integer flush_port = 0; // flush port is read only
|
||||
Integer sendRqToP_port = 0; // sendRqToP is read only
|
||||
Integer sendRsToP_cRq_port = 0;
|
||||
Integer pipelineResp_port = 1;
|
||||
Integer cRqTransfer_port = 2;
|
||||
|
||||
// MSHR entry state
|
||||
Vector#(cRqNum, Ehr#(3, L1CRqState)) stateVec <- replicateM(mkEhr(Empty));
|
||||
// cRq req contents
|
||||
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
|
||||
// cRq mshr slots
|
||||
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(defaultValue));
|
||||
// data valid bit
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
|
||||
// data values
|
||||
RegFile#(cRqIndexT, Line) dataFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
|
||||
// successor valid bit
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) succValidVec <- replicateM(mkEhr(False));
|
||||
// successor MSHR index
|
||||
RegFile#(cRqIndexT, cRqIndexT) succFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
|
||||
// empty entry FIFO
|
||||
FIFO#(cRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(cRqNum));
|
||||
|
||||
// empty entry FIFO needs initialization
|
||||
Reg#(Bool) inited <- mkReg(False);
|
||||
Reg#(cRqIndexT) initIdx <- mkReg(0);
|
||||
|
||||
rule initEmptyEntry(!inited);
|
||||
emptyEntryQ.enq(initIdx);
|
||||
initIdx <= initIdx + 1;
|
||||
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
|
||||
inited <= True;
|
||||
$display("%t L1CRqMshrSafe %m: init empty entry done", $time);
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
|
||||
FIFO#(L1CRqMshrStuck#(reqT)) stuckQ <- mkFIFO1;
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock;
|
||||
let stuckIdx <- checker.getStuckIdx;
|
||||
if(stuckIdx matches tagged Valid .n) begin
|
||||
stuckQ.enq(L1CRqMshrStuck {
|
||||
req: reqVec[n][0],
|
||||
state: stateVec[n][0],
|
||||
slotCs: slotVec[n][0].cs,
|
||||
waitP: slotVec[n][0].waitP
|
||||
});
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
interface L1CRqMshr_transfer cRqTransfer;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][cRqTransfer_port];
|
||||
endmethod
|
||||
|
||||
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r) if(inited);
|
||||
emptyEntryQ.deq;
|
||||
cRqIndexT n = emptyEntryQ.first;
|
||||
stateVec[n][cRqTransfer_port] <= Init;
|
||||
slotVec[n][cRqTransfer_port] <= defaultValue;
|
||||
dataValidVec[n][cRqTransfer_port] <= False;
|
||||
succValidVec[n][cRqTransfer_port] <= False;
|
||||
reqVec[n][cRqTransfer_port] <= r;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.initEntry(n);
|
||||
`endif
|
||||
return n;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface L1CRqMshr_sendRsToP_cRq sendRsToP_cRq;
|
||||
method L1CRqState getState(cRqIndexT n);
|
||||
return stateVec[n][sendRsToP_cRq_port];
|
||||
endmethod
|
||||
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRsToP_cRq_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendRsToP_cRq_port];
|
||||
endmethod
|
||||
|
||||
method Maybe#(Line) getData(cRqIndexT n);
|
||||
return dataValidVec[n][sendRsToP_cRq_port] ? (Valid (dataFile.sub(n))) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action setWaitSt_setSlot_clearData(cRqIndexT n, slotT s);
|
||||
stateVec[n][sendRsToP_cRq_port] <= WaitSt;
|
||||
slotVec[n][sendRsToP_cRq_port] <= s;
|
||||
dataValidVec[n][sendRsToP_cRq_port] <= False;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface L1CRqMshr_sendRqToP sendRqToP;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRqToP_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendRqToP_port];
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface L1CRqMshr_pipelineResp pipelineResp;
|
||||
method L1CRqState getState(cRqIndexT n);
|
||||
return stateVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(cRqIndexT n) if(inited);
|
||||
emptyEntryQ.enq(n);
|
||||
stateVec[n][pipelineResp_port] <= Empty;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method Action setStateSlot(cRqIndexT n, L1CRqState state, slotT slot);
|
||||
doAssert(state != Empty, "use releaseEntry to set state to Empty");
|
||||
stateVec[n][pipelineResp_port] <= state;
|
||||
slotVec[n][pipelineResp_port] <= slot;
|
||||
endmethod
|
||||
|
||||
method Action setData(cRqIndexT n, Maybe#(Line) line);
|
||||
dataValidVec[n][pipelineResp_port] <= isValid(line);
|
||||
dataFile.upd(n, fromMaybe(?, line));
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) getSucc(cRqIndexT n);
|
||||
return succValidVec[n][pipelineResp_port] ? (Valid (succFile.sub(n))) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action setSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
|
||||
succValidVec[n][pipelineResp_port] <= isValid(succ);
|
||||
succFile.upd(n, fromMaybe(?, succ));
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
|
||||
function Bool isEndOfChain(Integer i);
|
||||
// check entry i is end of chain or not
|
||||
L1CRqState state = stateVec[i][pipelineResp_port];
|
||||
Bool notDone = state != Done;
|
||||
Bool processedOnce = state != Empty && state != Init;
|
||||
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
|
||||
Bool noSucc = !succValidVec[i][pipelineResp_port];
|
||||
return notDone && processedOnce && addrMatch && noSucc;
|
||||
endfunction
|
||||
Vector#(cRqNum, Integer) idxVec = genVector;
|
||||
return searchIndex(isEndOfChain, idxVec);
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
method Bool emptyForFlush;
|
||||
function Bool isEmpty(Integer i) = stateVec[i][flush_port] == Empty;
|
||||
Vector#(cRqNum, Integer) idxVec = genVector;
|
||||
return all(isEmpty, idxVec);
|
||||
endmethod
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface stuck = toGet(stuckQ);
|
||||
`else
|
||||
interface stuck = nullGet;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
// exported version
|
||||
module mkL1CRqMshr#(
|
||||
function Addr getAddrFromReq(reqT r)
|
||||
)(
|
||||
L1CRqMshr#(cRqNum, wayT, tagT, reqT)
|
||||
) provisos (
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(tagT, Bit#(_tagSz)),
|
||||
Bits#(reqT, _reqSz)
|
||||
);
|
||||
let m <- mkL1CRqMshrSafe(getAddrFromReq);
|
||||
return m;
|
||||
endmodule
|
||||
231
src_Core/RISCY_OOO/coherence/src/L1PRqMshr.bsv
Normal file
231
src_Core/RISCY_OOO/coherence/src/L1PRqMshr.bsv
Normal file
@@ -0,0 +1,231 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import RegFile::*;
|
||||
import FIFO::*;
|
||||
import FShow::*;
|
||||
import GetPut::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import DefaultValue::*;
|
||||
import Ehr::*;
|
||||
import Fifo::*;
|
||||
import MshrDeadlockChecker::*;
|
||||
|
||||
// MSHR dependency chain invariant:
|
||||
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
|
||||
|
||||
// in L1, pRq is always directly handled at the end of pipeline
|
||||
|
||||
// PRq MSHR entry state
|
||||
typedef enum {
|
||||
Empty,
|
||||
Init,
|
||||
Done
|
||||
} L1PRqState deriving (Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
L1PRqState state;
|
||||
} L1PRqMshrStuck deriving(Bits, Eq, FShow);
|
||||
|
||||
interface L1PRqMshr_sendRsToP_pRq#(numeric type pRqNum);
|
||||
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
|
||||
method Maybe#(Line) getData(Bit#(TLog#(pRqNum)) n);
|
||||
|
||||
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
|
||||
endinterface
|
||||
|
||||
interface L1PRqMshr_pipelineResp#(numeric type pRqNum);
|
||||
method PRqMsg#(void) getRq(Bit#(TLog#(pRqNum)) n);
|
||||
method L1PRqState getState(Bit#(TLog#(pRqNum)) n);
|
||||
|
||||
method Action releaseEntry(Bit#(TLog#(pRqNum)) n);
|
||||
method Action setDone_setData(Bit#(TLog#(pRqNum)) n, Maybe#(Line) d);
|
||||
`ifdef SECURITY
|
||||
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
interface L1PRqMshr#(numeric type pRqNum);
|
||||
// port for pRqTransfer
|
||||
method ActionValue#(Bit#(TLog#(pRqNum))) getEmptyEntryInit(PRqMsg#(void) r);
|
||||
|
||||
// port for sendRsToP_pRq
|
||||
interface L1PRqMshr_sendRsToP_pRq#(pRqNum) sendRsToP_pRq;
|
||||
|
||||
// port for pipelineResp
|
||||
interface L1PRqMshr_pipelineResp#(pRqNum) pipelineResp;
|
||||
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(L1PRqMshrStuck) stuck;
|
||||
endinterface
|
||||
|
||||
|
||||
//////////////////
|
||||
// safe version //
|
||||
//////////////////
|
||||
module mkL1PRqMshrSafe(
|
||||
L1PRqMshr#(pRqNum)
|
||||
) provisos(
|
||||
Alias#(pRqIndexT, Bit#(TLog#(pRqNum)))
|
||||
);
|
||||
// EHR port
|
||||
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
|
||||
Integer sendRsToP_pRq_port = 0;
|
||||
Integer pipelineResp_port = 1;
|
||||
Integer pRqTransfer_port = 2;
|
||||
|
||||
// MSHR entry state
|
||||
Vector#(pRqNum, Ehr#(3, L1PRqState)) stateVec <- replicateM(mkEhr(Empty));
|
||||
Vector#(pRqNum, Ehr#(3, PRqMsg#(void))) reqVec <- replicateM(mkEhr(?));
|
||||
// data valid bits
|
||||
Vector#(pRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
|
||||
// data values
|
||||
RegFile#(pRqIndexT, Line) dataFile <- mkRegFile(0, fromInteger(valueOf(pRqNum) - 1));
|
||||
// empty entry FIFO
|
||||
FIFO#(pRqIndexT) emptyEntryQ <- mkSizedFIFO(valueOf(pRqNum));
|
||||
|
||||
// empty entry FIFO needs initialization
|
||||
Reg#(Bool) inited <- mkReg(False);
|
||||
Reg#(pRqIndexT) initIdx <- mkReg(0);
|
||||
|
||||
// released entry index fifos
|
||||
Fifo#(1, pRqIndexT) releaseEntryQ_sendRsToP_pRq <- mkBypassFifo;
|
||||
Fifo#(1, pRqIndexT) releaseEntryQ_pipelineResp <- mkBypassFifo;
|
||||
|
||||
rule initEmptyEntry(!inited);
|
||||
emptyEntryQ.enq(initIdx);
|
||||
initIdx <= initIdx + 1;
|
||||
if(initIdx == fromInteger(valueOf(pRqNum) - 1)) begin
|
||||
inited <= True;
|
||||
$display("%t L1PRqMshrSafe %m: init empty entry done", $time);
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
MshrDeadlockChecker#(pRqNum) checker <- mkMshrDeadlockChecker;
|
||||
FIFO#(L1PRqMshrStuck) stuckQ <- mkFIFO1;
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock;
|
||||
let stuckIdx <- checker.getStuckIdx;
|
||||
if(stuckIdx matches tagged Valid .n) begin
|
||||
stuckQ.enq(L1PRqMshrStuck {
|
||||
addr: reqVec[n][0].addr,
|
||||
toState: reqVec[n][0].toState,
|
||||
state: stateVec[n][0]
|
||||
});
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
rule doReleaseEntry_sendRsToP_pRq(inited);
|
||||
let n <- toGet(releaseEntryQ_sendRsToP_pRq).get;
|
||||
emptyEntryQ.enq(n);
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
(* descending_urgency = "doReleaseEntry_sendRsToP_pRq, doReleaseEntry_pipelineResp" *)
|
||||
rule doReleaseEntry_pipelineResp(inited);
|
||||
let n <- toGet(releaseEntryQ_pipelineResp).get;
|
||||
emptyEntryQ.enq(n);
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
method ActionValue#(pRqIndexT) getEmptyEntryInit(PRqMsg#(void) r) if(inited);
|
||||
emptyEntryQ.deq;
|
||||
pRqIndexT n = emptyEntryQ.first;
|
||||
stateVec[n][pRqTransfer_port] <= Init;
|
||||
reqVec[n][pRqTransfer_port] <= r;
|
||||
dataValidVec[n][pRqTransfer_port] <= False;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.initEntry(n);
|
||||
`endif
|
||||
return n;
|
||||
endmethod
|
||||
|
||||
interface L1PRqMshr_sendRsToP_pRq sendRsToP_pRq;
|
||||
method PRqMsg#(void) getRq(pRqIndexT n);
|
||||
return reqVec[n][sendRsToP_pRq_port];
|
||||
endmethod
|
||||
|
||||
method Maybe#(Line) getData(pRqIndexT n);
|
||||
return dataValidVec[n][sendRsToP_pRq_port] ? (Valid (dataFile.sub(n))) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(pRqIndexT n) if(inited);
|
||||
releaseEntryQ_sendRsToP_pRq.enq(n);
|
||||
stateVec[n][sendRsToP_pRq_port] <= Empty;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface L1PRqMshr_pipelineResp pipelineResp;
|
||||
method PRqMsg#(void) getRq(pRqIndexT n);
|
||||
return reqVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method L1PRqState getState(pRqIndexT n);
|
||||
return stateVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method Action setDone_setData(pRqIndexT n, Maybe#(Line) line);
|
||||
stateVec[n][pipelineResp_port] <= Done;
|
||||
dataValidVec[n][pipelineResp_port] <= isValid(line);
|
||||
dataFile.upd(n, fromMaybe(?, line));
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(pRqIndexT n) if(inited);
|
||||
releaseEntryQ_pipelineResp.enq(n);
|
||||
stateVec[n][pipelineResp_port] <= Empty;
|
||||
endmethod
|
||||
|
||||
`ifdef SECURITY
|
||||
method Action setFlushAddr(Bit#(TLog#(pRqNum)) n, Addr a);
|
||||
reqVec[n][pipelineResp_port] <= PRqMsg {
|
||||
addr: a,
|
||||
toState: I,
|
||||
child: ?
|
||||
};
|
||||
endmethod
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface stuck = toGet(stuckQ);
|
||||
`else
|
||||
interface stuck = nullGet;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
// exportd version
|
||||
module mkL1PRqMshr(L1PRqMshr#(pRqNum));
|
||||
let m <- mkL1PRqMshrSafe;
|
||||
return m;
|
||||
endmodule
|
||||
391
src_Core/RISCY_OOO/coherence/src/L1Pipe.bsv
Normal file
391
src_Core/RISCY_OOO/coherence/src/L1Pipe.bsv
Normal file
@@ -0,0 +1,391 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import CCPipe::*;
|
||||
import RWBramCore::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export L1PipeRqIn(..);
|
||||
export L1PipePRsIn(..);
|
||||
export L1PipeFlushIn(..);
|
||||
export L1PipeIn(..);
|
||||
export L1FlushCmd(..);
|
||||
export L1Cmd(..);
|
||||
export L1Pipe(..);
|
||||
export mkL1Pipe;
|
||||
|
||||
// type param ordering: bank < way < index < tag < cRq < pRq
|
||||
|
||||
// in L1 cache, only cRq can occupy cache line (pRq handled immediately)
|
||||
// replacement is always done immediately (never have replacing line)
|
||||
// so cache owner type is simply Maybe#(cRqIdxT)
|
||||
|
||||
// input types
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
rqIdxT mshrIdx;
|
||||
} L1PipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
Maybe#(Line) data;
|
||||
wayT way;
|
||||
} L1PipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
indexT index;
|
||||
wayT way;
|
||||
pRqIdxT mshrIdx;
|
||||
} L1PipeFlushIn#(
|
||||
type wayT,
|
||||
type indexT,
|
||||
type pRqIdxT
|
||||
) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
L1PipeRqIn#(cRqIdxT) CRq;
|
||||
L1PipeRqIn#(pRqIdxT) PRq;
|
||||
L1PipePRsIn#(wayT) PRs;
|
||||
`ifdef SECURITY
|
||||
L1PipeFlushIn#(wayT, indexT, pRqIdxT) Flush;
|
||||
`endif
|
||||
} L1PipeIn#(
|
||||
type wayT,
|
||||
type indexT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// output cmd to the processing rule in L1$
|
||||
typedef struct {
|
||||
indexT index;
|
||||
pRqIdxT mshrIdx;
|
||||
} L1FlushCmd#(type indexT, type pRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
cRqIdxT L1CRq;
|
||||
pRqIdxT L1PRq;
|
||||
void L1PRs;
|
||||
`ifdef SECURITY
|
||||
L1FlushCmd#(indexT, pRqIdxT) L1Flush;
|
||||
`endif
|
||||
} L1Cmd#(
|
||||
type indexT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
interface L1Pipe#(
|
||||
numeric type lgBankNum,
|
||||
numeric type wayNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
);
|
||||
method Action send(L1PipeIn#(Bit#(TLog#(wayNum)), indexT, cRqIdxT, pRqIdxT) r);
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, void, // no dir
|
||||
Maybe#(cRqIdxT), void, RandRepInfo, // no other
|
||||
Line, L1Cmd#(indexT, cRqIdxT, pRqIdxT)
|
||||
) first;
|
||||
method Action deqWrite(
|
||||
Maybe#(cRqIdxT) swapRq,
|
||||
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), void, Line) wrRam, // always write BRAM
|
||||
Bool updateRep
|
||||
);
|
||||
endinterface
|
||||
|
||||
// real cmd used in pipeline
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
wayT way;
|
||||
} L1PipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
L1PipeRqIn#(cRqIdxT) CRq;
|
||||
L1PipeRqIn#(pRqIdxT) PRq;
|
||||
L1PipePRsCmd#(wayT) PRs;
|
||||
`ifdef SECURITY
|
||||
L1PipeFlushIn#(wayT, indexT, pRqIdxT) Flush;
|
||||
`endif
|
||||
} L1PipeCmd#(
|
||||
type wayT,
|
||||
type indexT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
module mkL1Pipe(
|
||||
L1Pipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT, pRqIdxT)
|
||||
) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(dirT, void), // no directory
|
||||
Alias#(ownerT, Maybe#(cRqIdxT)),
|
||||
Alias#(otherT, void), // no other cache info
|
||||
Alias#(repT, RandRepInfo), // use random replace
|
||||
Alias#(pipeInT, L1PipeIn#(wayT, indexT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(pipeCmdT, L1PipeCmd#(wayT, indexT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(l1CmdT, L1Cmd#(indexT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, l1CmdT)), // output type
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
|
||||
Alias#(respStateT, RespState#(Msi)),
|
||||
Alias#(tagMatchResT, TagMatchResult#(wayT)),
|
||||
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
|
||||
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
|
||||
// requirement
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
|
||||
Alias#(pRqIdxT, Bit#(pRqIdxSz)),
|
||||
Add#(indexSz, a__, AddrSz),
|
||||
Add#(tagSz, b__, AddrSz)
|
||||
);
|
||||
// RAMs
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
|
||||
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
|
||||
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
|
||||
|
||||
// initialize RAM
|
||||
Reg#(Bool) initDone <- mkReg(False);
|
||||
Reg#(indexT) initIndex <- mkReg(0);
|
||||
|
||||
rule doInit(!initDone);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].wrReq(initIndex, CacheInfo {
|
||||
tag: 0,
|
||||
cs: I,
|
||||
dir: ?,
|
||||
owner: Invalid,
|
||||
other: ?
|
||||
});
|
||||
end
|
||||
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
|
||||
initIndex <= initIndex + 1;
|
||||
if(initIndex == maxBound) begin
|
||||
initDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
// random replacement
|
||||
RandomReplace#(wayNum) randRep <- mkRandomReplace;
|
||||
|
||||
// functions
|
||||
function Addr getAddrFromCmd(pipeCmdT cmd);
|
||||
return (case(cmd) matches
|
||||
tagged CRq .r: r.addr;
|
||||
tagged PRq .r: r.addr;
|
||||
tagged PRs .r: r.addr;
|
||||
`ifdef SECURITY
|
||||
// fake an address for flush req that has the same index
|
||||
tagged Flush .r: (zeroExtend(r.index) << (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
`endif
|
||||
default: ?;
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
function indexT getIndex(pipeCmdT cmd);
|
||||
Addr a = getAddrFromCmd(cmd);
|
||||
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
endfunction
|
||||
|
||||
function ActionValue#(tagMatchResT) tagMatch(
|
||||
pipeCmdT cmd,
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, Msi) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
);
|
||||
return actionvalue
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
$display("%t L1 %m tagMatch: ", $time,
|
||||
fshow(cmd), " ; ",
|
||||
fshow(getTag(getAddrFromCmd(cmd))),
|
||||
fshow(tagVec), " ; ",
|
||||
fshow(csVec), " ; ",
|
||||
fshow(ownerVec), " ; "
|
||||
);
|
||||
if(cmd matches tagged PRs .rs) begin
|
||||
// PRs directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: rs.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
`ifdef SECURITY
|
||||
else if(cmd matches tagged Flush .flush) begin
|
||||
// flush directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: flush.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
`endif
|
||||
else begin
|
||||
// CRq/PRq: need tag matching
|
||||
Addr addr = getAddrFromCmd(cmd);
|
||||
tagT tag = getTag(addr);
|
||||
// find hit way (nothing is being replaced)
|
||||
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
|
||||
match {.cs, .t} = csTag;
|
||||
return cs > I && t == tag;
|
||||
endfunction
|
||||
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
|
||||
if(hitWay matches tagged Valid .w) begin
|
||||
return TagMatchResult {
|
||||
way: w,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else if(cmd matches tagged PRq .rq) begin
|
||||
// pRq miss
|
||||
return TagMatchResult {
|
||||
way: 0, // default to 0
|
||||
pRqMiss: True
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// find a unlocked way to replace for cRq
|
||||
Vector#(wayNum, Bool) unlocked = ?;
|
||||
Vector#(wayNum, Bool) invalid = ?;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
invalid[i] = csVec[i] == I;
|
||||
unlocked[i] = !isValid(ownerVec[i]);
|
||||
end
|
||||
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
|
||||
// sanity check: repWay must be valid
|
||||
if(!isValid(repWay)) begin
|
||||
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
|
||||
$finish;
|
||||
end
|
||||
return TagMatchResult {
|
||||
way: fromMaybe(?, repWay),
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
end
|
||||
endactionvalue;
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByUpCsT) updateByUpCs(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
|
||||
);
|
||||
actionvalue
|
||||
doAssert(toState > oldCs, "should truly upgrade cs");
|
||||
doAssert((oldCs == I) == dataV, "valid resp data for upgrade from I");
|
||||
return UpdateByUpCs {cs: toState};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByDownDirT) updateByDownDir(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
|
||||
);
|
||||
actionvalue
|
||||
doAssert(False, "L1 does not have dir");
|
||||
return UpdateByDownDir {cs: oldCs, dir: oldDir};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
|
||||
actionvalue
|
||||
return ?; // random replace does not have bookkeeping
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
CCPipe#(
|
||||
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
|
||||
) pipe <- mkCCPipe(
|
||||
regToReadOnly(initDone), getIndex, tagMatch,
|
||||
updateByUpCs, updateByDownDir, updateRepInfo,
|
||||
infoRam, repRam, dataRam
|
||||
);
|
||||
|
||||
method Action send(pipeInT req);
|
||||
case(req) matches
|
||||
tagged CRq .rq: begin
|
||||
pipe.enq(CRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged PRq .rq: begin
|
||||
pipe.enq(PRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged PRs .rs: begin
|
||||
pipe.enq(PRs (L1PipePRsCmd {
|
||||
addr: rs.addr,
|
||||
way: rs.way
|
||||
}), rs.data, UpCs (rs.toState));
|
||||
end
|
||||
`ifdef SECURITY
|
||||
tagged Flush .flush: begin
|
||||
pipe.enq(Flush (flush), Invalid, Invalid);
|
||||
end
|
||||
`endif
|
||||
endcase
|
||||
endmethod
|
||||
|
||||
// need to adapt pipeline output to real output format
|
||||
method pipeOutT first;
|
||||
let pout = pipe.first;
|
||||
return PipeOut {
|
||||
cmd: (case(pout.cmd) matches
|
||||
tagged CRq .rq: L1CRq (rq.mshrIdx);
|
||||
tagged PRq .rq: L1PRq (rq.mshrIdx);
|
||||
tagged PRs .rs: L1PRs;
|
||||
`ifdef SECURITY
|
||||
tagged Flush .flush: L1Flush (L1FlushCmd {
|
||||
index: flush.index,
|
||||
mshrIdx: flush.mshrIdx
|
||||
});
|
||||
`endif
|
||||
default: ?;
|
||||
endcase),
|
||||
way: pout.way,
|
||||
pRqMiss: pout.pRqMiss,
|
||||
ram: pout.ram,
|
||||
repInfo: pout.repInfo
|
||||
};
|
||||
endmethod
|
||||
|
||||
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
|
||||
// get new cmd
|
||||
Maybe#(pipeCmdT) newCmd = Invalid;
|
||||
if(swapRq matches tagged Valid .idx) begin // swap in cRq
|
||||
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
|
||||
newCmd = Valid (CRq (L1PipeRqIn {addr: addr, mshrIdx: idx}));
|
||||
`ifdef SECURITY
|
||||
doAssert(pipe.first.cmd matches tagged Flush .f ? False : True,
|
||||
"Cannot swap after a flush req");
|
||||
`endif
|
||||
end
|
||||
// call pipe
|
||||
pipe.deqWrite(newCmd, wrRam, updateRep);
|
||||
endmethod
|
||||
endmodule
|
||||
1544
src_Core/RISCY_OOO/coherence/src/LLBank.bsv
Normal file
1544
src_Core/RISCY_OOO/coherence/src/LLBank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
447
src_Core/RISCY_OOO/coherence/src/LLCRqMshr.bsv
Normal file
447
src_Core/RISCY_OOO/coherence/src/LLCRqMshr.bsv
Normal file
@@ -0,0 +1,447 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import RegFile::*;
|
||||
import FIFO::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import DefaultValue::*;
|
||||
import Ehr::*;
|
||||
import Fifo::*;
|
||||
import MshrDeadlockChecker::*;
|
||||
|
||||
// MSHR dependency chain invariant:
|
||||
// every cRq and pRq (for same addr) which has gone through pipeline once will be linked into the chain
|
||||
|
||||
// in LLC, the head (h1) of a chain may be linked as the successor of the head (h2) of another chain
|
||||
// when h2 is replacing the addr of h1
|
||||
// h1 should be waken up and sent to pipeline when replacement is done (i.e. h2 gets to WaitSt)
|
||||
|
||||
// CRq MSHR entry state
|
||||
typedef enum {
|
||||
Empty,
|
||||
Init,
|
||||
WaitOldTag,
|
||||
WaitSt,
|
||||
Done,
|
||||
Depend
|
||||
} LLCRqState deriving(Bits, Eq, FShow);
|
||||
|
||||
// we split data from slot info
|
||||
// because data may be used to buffer mem resp data
|
||||
typedef struct {
|
||||
wayT way; // the way to occupy
|
||||
tagT repTag; // tag being replaced, used in sending down req to children
|
||||
Bool waitP; // wait parent resp
|
||||
dirPendT dirPend; // pending child downgrade
|
||||
} LLCRqSlot#(type wayT, type tagT, type dirPendT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
reqT req;
|
||||
LLCRqState state;
|
||||
Bool waitP;
|
||||
dirPendT dirPend;
|
||||
} LLCRqMshrStuck#(type dirPendT, type reqT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// interface for cRq/mRs/cRsTransfer
|
||||
interface LLCRqMshr_transfer#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type dirPendT,
|
||||
type reqT // child req type
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
method ActionValue#(Bit#(TLog#(cRqNum))) getEmptyEntryInit(reqT r, Maybe#(Line) d);
|
||||
// check if any empty MSHR entry is available in order to get MSHR blocking
|
||||
// stats. The argument is not really used here, just in case some other
|
||||
// MSHR implementations may bank entries based on addr.
|
||||
method Bool hasEmptyEntry(reqT r);
|
||||
endinterface
|
||||
|
||||
// interface for mRsDeq
|
||||
interface LLCRqMshr_mRsDeq#(numeric type cRqNum);
|
||||
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
|
||||
endinterface
|
||||
|
||||
// interface for sendToM
|
||||
interface LLCRqMshr_sendToM#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type dirPendT,
|
||||
type reqT // child req type
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
// interface for sendRsToDma and sendRsToC
|
||||
interface LLCRqMshr_sendRsToDmaC#(
|
||||
numeric type cRqNum,
|
||||
type reqT // child req type
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
|
||||
method Action releaseEntry(Bit#(TLog#(cRqNum)) n);
|
||||
endinterface
|
||||
|
||||
// interface for sendRqToC
|
||||
interface LLCRqMshr_sendRqToC#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type dirPendT,
|
||||
type reqT // child req type
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqState getState(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
method Action setSlot(Bit#(TLog#(cRqNum)) n, LLCRqSlot#(wayT, tagT, dirPendT) s);
|
||||
// find cRq that needs to send req to child to downgrade
|
||||
// (either replacement, or incompatible children states)
|
||||
// we can pass in a suggested req idx (which will have priority)
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) searchNeedRqChild(Maybe#(Bit#(TLog#(cRqNum))) suggestIdx);
|
||||
endinterface
|
||||
|
||||
// interface for pipelineResp_xxx
|
||||
interface LLCRqMshr_pipelineResp#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type dirPendT,
|
||||
type reqT // child req type
|
||||
);
|
||||
method reqT getRq(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqState getState(Bit#(TLog#(cRqNum)) n);
|
||||
method LLCRqSlot#(wayT, tagT, dirPendT) getSlot(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Line) getData(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) getAddrSucc(Bit#(TLog#(cRqNum)) n);
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) getRepSucc(Bit#(TLog#(cRqNum)) n);
|
||||
method Action setData(Bit#(TLog#(cRqNum)) n, Maybe#(Line) d);
|
||||
method Action setStateSlot(
|
||||
Bit#(TLog#(cRqNum)) n, LLCRqState state,
|
||||
LLCRqSlot#(wayT, tagT, dirPendT) slot
|
||||
);
|
||||
method Action setAddrSucc( // same address successor
|
||||
Bit#(TLog#(cRqNum)) n,
|
||||
Maybe#(Bit#(TLog#(cRqNum))) succ
|
||||
);
|
||||
method Action setRepSucc( // successor due to replacement
|
||||
Bit#(TLog#(cRqNum)) n,
|
||||
Maybe#(Bit#(TLog#(cRqNum))) succ
|
||||
);
|
||||
// find existing cRq which has gone through pipeline, but not in Done state, and has no addr successor
|
||||
// (it could have rep successor)
|
||||
// i.e. search the end of dependency chain for req to the same addr
|
||||
method Maybe#(Bit#(TLog#(cRqNum))) searchEndOfChain(Addr addr);
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr#(
|
||||
numeric type cRqNum,
|
||||
type wayT,
|
||||
type tagT,
|
||||
type dirPendT,
|
||||
type reqT // child req type
|
||||
);
|
||||
interface LLCRqMshr_transfer#(cRqNum, wayT, tagT, dirPendT, reqT) transfer;
|
||||
interface LLCRqMshr_mRsDeq#(cRqNum) mRsDeq;
|
||||
interface LLCRqMshr_sendToM#(cRqNum, wayT, tagT, dirPendT, reqT) sendToM;
|
||||
interface LLCRqMshr_sendRsToDmaC#(cRqNum, reqT) sendRsToDmaC;
|
||||
interface LLCRqMshr_sendRqToC#(cRqNum, wayT, tagT, dirPendT, reqT) sendRqToC;
|
||||
interface LLCRqMshr_pipelineResp#(cRqNum, wayT, tagT, dirPendT, reqT) pipelineResp;
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(LLCRqMshrStuck#(dirPendT, reqT)) stuck;
|
||||
endinterface
|
||||
|
||||
function LLCRqSlot#(wayT, tagT, dirPendT) getLLCRqSlotInitVal(dirPendT dirPendInitVal);
|
||||
return LLCRqSlot {
|
||||
way: ?,
|
||||
repTag: ?,
|
||||
waitP: False,
|
||||
dirPend: dirPendInitVal
|
||||
};
|
||||
endfunction
|
||||
|
||||
//////////////////
|
||||
// safe version //
|
||||
//////////////////
|
||||
module mkLLCRqMshr#(
|
||||
function Addr getAddrFromReq(reqT r),
|
||||
function Bool needDownReq(dirPendT dirPend),
|
||||
dirPendT dirPendInitVal
|
||||
)(
|
||||
LLCRqMshr#(cRqNum, wayT, tagT, dirPendT, reqT)
|
||||
) provisos (
|
||||
Alias#(cRqIndexT, Bit#(TLog#(cRqNum))),
|
||||
Alias#(slotT, LLCRqSlot#(wayT, tagT, dirPendT)),
|
||||
Alias#(wayT, Bit#(_waySz)),
|
||||
Alias#(tagT, Bit#(_tagSz)),
|
||||
Bits#(dirPendT, _dirPendSz),
|
||||
Bits#(reqT, _reqSz)
|
||||
);
|
||||
slotT slotInitVal = getLLCRqSlotInitVal(dirPendInitVal);
|
||||
|
||||
// logical ordering: sendToM < sendRqToC < sendRsToDma/C < mRsDeq < pipelineResp < transfer
|
||||
// We put pipelineResp < transfer to cater for deq < enq of cache pipeline
|
||||
// EHR ports
|
||||
Integer sendToM_port = 0; // sendToM is read-only, so use port 0
|
||||
Integer sendRqToC_port = 0; // read req/state/slot, write slot
|
||||
Integer sendRsToDmaC_port = 0; // sendRsToDma/C read req/data, write state
|
||||
Integer mRsDeq_port = 0; // mRsDeq only writes data
|
||||
Integer pipelineResp_port = 1; // read/write lots of things
|
||||
Integer transfer_port = 2; // cRqTransfer_xx, mRsTransfer_send, read/write lots of things
|
||||
|
||||
// cRq req contents
|
||||
Vector#(cRqNum, Ehr#(3, reqT)) reqVec <- replicateM(mkEhr(?));
|
||||
// MSHR entry state
|
||||
Vector#(cRqNum, Ehr#(3, LLCRqState)) stateVec <- replicateM(mkEhr(Empty));
|
||||
// summary bit of dirPend in each entry: asserted when some dirPend[i] = ToSend
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) needReqChildVec <- replicateM(mkEhr(False));
|
||||
// cRq mshr slots
|
||||
Vector#(cRqNum, Ehr#(3, slotT)) slotVec <- replicateM(mkEhr(slotInitVal));
|
||||
// data valid bit
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) dataValidVec <- replicateM(mkEhr(False));
|
||||
// data values
|
||||
Vector#(cRqNum, Ehr#(3, Line)) dataVec <- replicateM(mkEhr(?));
|
||||
// successor valid bit
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) addrSuccValidVec <- replicateM(mkEhr(False));
|
||||
Vector#(cRqNum, Ehr#(3, Bool)) repSuccValidVec <- replicateM(mkEhr(False));
|
||||
// successor MSHR index
|
||||
RegFile#(cRqIndexT, cRqIndexT) addrSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
|
||||
RegFile#(cRqIndexT, cRqIndexT) repSuccFile <- mkRegFile(0, fromInteger(valueOf(cRqNum) - 1));
|
||||
// empty entry FIFO
|
||||
Fifo#(cRqNum, cRqIndexT) emptyEntryQ <- mkCFFifo;
|
||||
|
||||
// empty entry FIFO needs initialization
|
||||
Reg#(Bool) inited <- mkReg(False);
|
||||
Reg#(cRqIndexT) initIdx <- mkReg(0);
|
||||
|
||||
rule initEmptyEntry(!inited);
|
||||
emptyEntryQ.enq(initIdx);
|
||||
initIdx <= initIdx + 1;
|
||||
if(initIdx == fromInteger(valueOf(cRqNum) - 1)) begin
|
||||
inited <= True;
|
||||
$display("%t LLCRqMshrSafe %m: init empty entry done", $time);
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
MshrDeadlockChecker#(cRqNum) checker <- mkMshrDeadlockChecker;
|
||||
FIFO#(LLCRqMshrStuck#(dirPendT, reqT)) stuckQ <- mkFIFO1;
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock;
|
||||
let stuckIdx <- checker.getStuckIdx;
|
||||
if(stuckIdx matches tagged Valid .n) begin
|
||||
stuckQ.enq(LLCRqMshrStuck {
|
||||
req: reqVec[n][0],
|
||||
state: stateVec[n][0],
|
||||
waitP: slotVec[n][0].waitP,
|
||||
dirPend: slotVec[n][0].dirPend
|
||||
});
|
||||
end
|
||||
endrule
|
||||
`endif
|
||||
|
||||
function Action writeSlot(Integer ehrPort, cRqIndexT n, slotT s);
|
||||
action
|
||||
slotVec[n][ehrPort] <= s;
|
||||
// set dirPend summary bit
|
||||
needReqChildVec[n][ehrPort] <= needDownReq(s.dirPend);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
interface LLCRqMshr_transfer transfer;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][transfer_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][transfer_port];
|
||||
endmethod
|
||||
|
||||
method ActionValue#(cRqIndexT) getEmptyEntryInit(reqT r, Maybe#(Line) d) if(inited);
|
||||
emptyEntryQ.deq;
|
||||
cRqIndexT n = emptyEntryQ.first;
|
||||
reqVec[n][transfer_port] <= r;
|
||||
stateVec[n][transfer_port] <= Init;
|
||||
writeSlot(transfer_port, n, slotInitVal);
|
||||
dataValidVec[n][transfer_port] <= isValid(d);
|
||||
dataVec[n][transfer_port] <= validValue(d);
|
||||
addrSuccValidVec[n][transfer_port] <= False;
|
||||
repSuccValidVec[n][transfer_port] <= False;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.initEntry(n);
|
||||
`endif
|
||||
return n;
|
||||
endmethod
|
||||
|
||||
method Bool hasEmptyEntry(reqT r);
|
||||
return emptyEntryQ.notEmpty;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr_mRsDeq mRsDeq;
|
||||
method Action setData(cRqIndexT n, Maybe#(Line) d);
|
||||
dataValidVec[n][mRsDeq_port] <= isValid(d);
|
||||
dataVec[n][mRsDeq_port] <= fromMaybe(?, d);
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr_sendToM sendToM;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendToM_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendToM_port];
|
||||
endmethod
|
||||
|
||||
method Maybe#(Line) getData(cRqIndexT n);
|
||||
return dataValidVec[n][sendToM_port] ? Valid (dataVec[n][sendToM_port]) : Invalid;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr_sendRsToDmaC sendRsToDmaC;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRsToDmaC_port];
|
||||
endmethod
|
||||
|
||||
method Maybe#(Line) getData(cRqIndexT n);
|
||||
return dataValidVec[n][sendRsToDmaC_port] ? Valid (dataVec[n][sendRsToDmaC_port]) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(cRqIndexT n) if(inited);
|
||||
emptyEntryQ.enq(n);
|
||||
stateVec[n][sendRsToDmaC_port] <= Empty;
|
||||
`ifdef CHECK_DEADLOCK
|
||||
checker.releaseEntry(n);
|
||||
`endif
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr_sendRqToC sendRqToC;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][sendRqToC_port];
|
||||
endmethod
|
||||
|
||||
method LLCRqState getState(cRqIndexT n);
|
||||
return stateVec[n][sendRqToC_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][sendRqToC_port];
|
||||
endmethod
|
||||
|
||||
method Action setSlot(cRqIndexT n, slotT s);
|
||||
writeSlot(sendRqToC_port, n, s);
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) searchNeedRqChild(Maybe#(cRqIndexT) suggestIdx);
|
||||
function Bool isNeedRqChild(cRqIndexT i);
|
||||
return (stateVec[i][sendRqToC_port] == WaitOldTag || stateVec[i][sendRqToC_port] == WaitSt)
|
||||
&& needReqChildVec[i][sendRqToC_port];
|
||||
endfunction
|
||||
if(suggestIdx matches tagged Valid .idx &&& isNeedRqChild(idx)) begin
|
||||
return suggestIdx;
|
||||
end
|
||||
else begin
|
||||
Vector#(cRqNum, cRqIndexT) idxVec = genWith(fromInteger);
|
||||
return searchIndex(isNeedRqChild, idxVec);
|
||||
end
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface LLCRqMshr_pipelineResp pipelineResp;
|
||||
method reqT getRq(cRqIndexT n);
|
||||
return reqVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method LLCRqState getState(cRqIndexT n);
|
||||
return stateVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method slotT getSlot(cRqIndexT n);
|
||||
return slotVec[n][pipelineResp_port];
|
||||
endmethod
|
||||
|
||||
method Maybe#(Line) getData(cRqIndexT n);
|
||||
return dataValidVec[n][pipelineResp_port] ? Valid (dataVec[n][pipelineResp_port]) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) getAddrSucc(cRqIndexT n);
|
||||
return addrSuccValidVec[n][pipelineResp_port] ? Valid (addrSuccFile.sub(n)) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) getRepSucc(cRqIndexT n);
|
||||
return repSuccValidVec[n][pipelineResp_port] ? Valid (repSuccFile.sub(n)) : Invalid;
|
||||
endmethod
|
||||
|
||||
method Action setData(cRqIndexT n, Maybe#(Line) d);
|
||||
dataValidVec[n][pipelineResp_port] <= isValid(d);
|
||||
dataVec[n][pipelineResp_port] <= fromMaybe(?, d);
|
||||
endmethod
|
||||
|
||||
method Action setStateSlot(cRqIndexT n, LLCRqState state, slotT slot);
|
||||
stateVec[n][pipelineResp_port] <= state;
|
||||
writeSlot(pipelineResp_port, n, slot);
|
||||
endmethod
|
||||
|
||||
method Action setAddrSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
|
||||
addrSuccValidVec[n][pipelineResp_port] <= isValid(succ);
|
||||
addrSuccFile.upd(n, fromMaybe(?, succ));
|
||||
endmethod
|
||||
|
||||
method Action setRepSucc(cRqIndexT n, Maybe#(cRqIndexT) succ);
|
||||
repSuccValidVec[n][pipelineResp_port] <= isValid(succ);
|
||||
repSuccFile.upd(n, fromMaybe(?, succ));
|
||||
endmethod
|
||||
|
||||
method Maybe#(cRqIndexT) searchEndOfChain(Addr addr);
|
||||
function Bool isEndOfChain(Integer i);
|
||||
// check entry i is end of chain or not
|
||||
let state = stateVec[i][pipelineResp_port];
|
||||
Bool notDone = state != Done;
|
||||
Bool processedOnce = state != Empty && state != Init;
|
||||
Bool addrMatch = getLineAddr(getAddrFromReq(reqVec[i][pipelineResp_port])) == getLineAddr(addr);
|
||||
Bool noAddrSucc = !addrSuccValidVec[i][pipelineResp_port];
|
||||
return notDone && processedOnce && addrMatch && noAddrSucc;
|
||||
endfunction
|
||||
Vector#(cRqNum, Integer) idxVec = genVector;
|
||||
return searchIndex(isEndOfChain, idxVec);
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface stuck = toGet(stuckQ);
|
||||
`else
|
||||
interface stuck = nullGet;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
371
src_Core/RISCY_OOO/coherence/src/LLPipe.bsv
Normal file
371
src_Core/RISCY_OOO/coherence/src/LLPipe.bsv
Normal file
@@ -0,0 +1,371 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import CCPipe::*;
|
||||
import RWBramCore::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export LLPipeCRqIn(..);
|
||||
export LLPipeMRsIn(..);
|
||||
export LLPipeIn(..);
|
||||
export LLCmd(..);
|
||||
export LLPipe(..);
|
||||
export mkLLPipe;
|
||||
|
||||
// type param ordering: bank < child < way < index < tag < cRq
|
||||
|
||||
// input types
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
cRqIdxT mshrIdx;
|
||||
} LLPipeCRqIn#(type cRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState; // come from req in MSHR (E or M)
|
||||
Line data; // come from memory must be valid
|
||||
wayT way; // come from MSHR
|
||||
} LLPipeMRsIn#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
LLPipeCRqIn#(cRqIdxT) CRq;
|
||||
CRsMsg#(childT) CRs;
|
||||
LLPipeMRsIn#(wayT) MRs;
|
||||
} LLPipeIn#(
|
||||
type childT,
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// output cmd to the processing rule in LLC
|
||||
typedef union tagged {
|
||||
cRqIdxT LLCRq; // mshr idx of the cRq
|
||||
childT LLCRs; // which child is downgrading
|
||||
void LLMRs;
|
||||
} LLCmd#(type childT, type cRqIdxT) deriving (Bits, Eq, FShow);
|
||||
|
||||
interface LLPipe#(
|
||||
numeric type lgBankNum,
|
||||
numeric type childNum,
|
||||
numeric type wayNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type cRqIdxT
|
||||
);
|
||||
method Action send(LLPipeIn#(Bit#(TLog#(childNum)), Bit#(TLog#(wayNum)), cRqIdxT) r);
|
||||
method Bool notEmpty;
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, Vector#(childNum, Msi),
|
||||
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
|
||||
Line, LLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
|
||||
) first;
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, Vector#(childNum, Msi),
|
||||
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
|
||||
Line, LLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
|
||||
) unguard_first;
|
||||
method Action deqWrite(
|
||||
Maybe#(cRqIdxT) swapRq,
|
||||
RamData#(tagT, Msi, Vector#(childNum, Msi), Maybe#(CRqOwner#(cRqIdxT)), void, Line) wrRam, // always write BRAM
|
||||
Bool updateRep
|
||||
);
|
||||
endinterface
|
||||
|
||||
// real cmd used in pipeline
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
childT child;
|
||||
} LLPipeCRsCmd#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
wayT way;
|
||||
} LLPipeMRsCmd#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
LLPipeCRqIn#(cRqIdxT) CRq;
|
||||
LLPipeCRsCmd#(childT) CRs;
|
||||
LLPipeMRsCmd#(wayT) MRs;
|
||||
} LLPipeCmd#(
|
||||
type childT,
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
module mkLLPipe(
|
||||
LLPipe#(lgBankNum, childNum, wayNum, indexT, tagT, cRqIdxT)
|
||||
) provisos(
|
||||
Alias#(childT, Bit#(TLog#(childNum))),
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(dirT, Vector#(childNum, Msi)),
|
||||
Alias#(ownerT, Maybe#(CRqOwner#(cRqIdxT))),
|
||||
Alias#(otherT, void), // no other cache info
|
||||
Alias#(repT, RandRepInfo), // use random replace
|
||||
Alias#(pipeInT, LLPipeIn#(childT, wayT, cRqIdxT)),
|
||||
Alias#(pipeCmdT, LLPipeCmd#(childT, wayT, cRqIdxT)),
|
||||
Alias#(llCmdT, LLCmd#(childT, cRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, llCmdT)), // output type
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
|
||||
Alias#(respStateT, RespState#(Msi)),
|
||||
Alias#(tagMatchResT, TagMatchResult#(wayT)),
|
||||
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
|
||||
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
|
||||
// requirement
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(_cRqIdxSz)),
|
||||
Add#(indexSz, a__, AddrSz),
|
||||
Add#(tagSz, b__, AddrSz)
|
||||
);
|
||||
// RAMs
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
|
||||
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
|
||||
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
|
||||
|
||||
// initialize RAM
|
||||
Reg#(Bool) initDone <- mkReg(False);
|
||||
Reg#(indexT) initIndex <- mkReg(0);
|
||||
|
||||
rule doInit(!initDone);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].wrReq(initIndex, CacheInfo {
|
||||
tag: 0,
|
||||
cs: I,
|
||||
dir: replicate(I),
|
||||
owner: Invalid,
|
||||
other: ?
|
||||
});
|
||||
end
|
||||
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
|
||||
initIndex <= initIndex + 1;
|
||||
if(initIndex == maxBound) begin
|
||||
initDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
// random replacement
|
||||
RandomReplace#(wayNum) randRep <- mkRandomReplace;
|
||||
|
||||
// functions
|
||||
function Addr getAddrFromCmd(pipeCmdT cmd);
|
||||
return (case(cmd) matches
|
||||
tagged CRq .r: r.addr;
|
||||
tagged CRs .r: r.addr;
|
||||
tagged MRs .r: r.addr;
|
||||
default: ?;
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
function indexT getIndex(pipeCmdT cmd);
|
||||
Addr a = getAddrFromCmd(cmd);
|
||||
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
endfunction
|
||||
|
||||
function ActionValue#(tagMatchResT) tagMatch(
|
||||
pipeCmdT cmd,
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, Msi) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
);
|
||||
return actionvalue
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
$display("%t LL %m tagMatch: ", $time,
|
||||
fshow(cmd), " ; ",
|
||||
fshow(getTag(getAddrFromCmd(cmd))), " ; ",
|
||||
fshow(tagVec), " ; ",
|
||||
fshow(csVec), " ; ",
|
||||
fshow(ownerVec)
|
||||
);
|
||||
if(cmd matches tagged MRs .rs) begin
|
||||
// MRs directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: rs.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// CRq/CRs: need tag matching
|
||||
Addr addr = getAddrFromCmd(cmd);
|
||||
tagT tag = getTag(addr);
|
||||
// find hit way (we do not check replacing bit in LLC)
|
||||
// this makes <cRq a> blocked by other <cRq b> which is replacing addr a
|
||||
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
|
||||
match {.cs, .t} = csTag;
|
||||
return cs > I && t == tag;
|
||||
endfunction
|
||||
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
|
||||
if(hitWay matches tagged Valid .w) begin
|
||||
return TagMatchResult {
|
||||
way: w,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// cRs must hit, so only cRq cannot enter here
|
||||
doAssert(cmd matches tagged CRq ._rq ? True : False,
|
||||
"only cRq can tag match miss"
|
||||
);
|
||||
// find a unlocked way to replace for cRq
|
||||
Vector#(wayNum, Bool) unlocked = ?;
|
||||
Vector#(wayNum, Bool) invalid = ?;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
invalid[i] = csVec[i] == I;
|
||||
unlocked[i] = !isValid(ownerVec[i]);
|
||||
end
|
||||
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
|
||||
// sanity check: repWay must be valid
|
||||
doAssert(isValid(repWay), "should always find a way to replace");
|
||||
return TagMatchResult {
|
||||
way: fromMaybe(?, repWay),
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
end
|
||||
endactionvalue;
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByUpCsT) updateByUpCs(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
|
||||
);
|
||||
actionvalue
|
||||
doAssert(toState > oldCs, "should truly upgrade cs");
|
||||
doAssert((oldCs == I) && dataV, "LLC mRs always has data");
|
||||
return UpdateByUpCs {cs: toState};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByDownDirT) updateByDownDir(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
|
||||
);
|
||||
actionvalue
|
||||
// update dir
|
||||
dirT newDir = oldDir;
|
||||
if(cmd matches tagged CRs .cRs) begin
|
||||
if(dataV) begin
|
||||
doAssert(oldDir[cRs.child] >= E, "cRs with data, dir must >= E");
|
||||
end
|
||||
else begin
|
||||
doAssert(oldDir[cRs.child] < M, "cRs without data, dir must < M");
|
||||
end
|
||||
if(oldDir[cRs.child] == M) begin
|
||||
doAssert(dataV, "must have data");
|
||||
end
|
||||
newDir[cRs.child] = toState;
|
||||
end
|
||||
else begin
|
||||
// should not happen
|
||||
doAssert(False, "only cRs updates dir");
|
||||
end
|
||||
// update cs
|
||||
// XXX since child can upgrade from E to M silently, use data valid
|
||||
// to determine if we need to upgrade to M. Note that the data
|
||||
// valid field has not been overwritten by bypass in CCPipe.
|
||||
Msi newCs = oldCs;
|
||||
if(dataV) begin
|
||||
doAssert(oldCs >= E, "cRs has data, cs must >= E");
|
||||
newCs = M;
|
||||
end
|
||||
return UpdateByDownDir {cs: newCs, dir: newDir};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
|
||||
actionvalue
|
||||
return ?; // random replace does not have bookkeeping
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
CCPipe#(wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pipe <- mkCCPipe(
|
||||
regToReadOnly(initDone), getIndex, tagMatch,
|
||||
updateByUpCs, updateByDownDir, updateRepInfo,
|
||||
infoRam, repRam, dataRam
|
||||
);
|
||||
|
||||
// get first output from CCPipe output
|
||||
function pipeOutT getFirst(PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pout);
|
||||
return PipeOut {
|
||||
cmd: (case(pout.cmd) matches
|
||||
tagged CRq .rq: LLCRq (rq.mshrIdx);
|
||||
tagged CRs .rs: LLCRs (rs.child);
|
||||
tagged MRs .rs: LLMRs;
|
||||
default: ?;
|
||||
endcase),
|
||||
way: pout.way,
|
||||
pRqMiss: pout.pRqMiss,
|
||||
ram: pout.ram,
|
||||
repInfo: pout.repInfo
|
||||
};
|
||||
endfunction
|
||||
|
||||
method Action send(pipeInT req);
|
||||
case(req) matches
|
||||
tagged CRq .rq: begin
|
||||
pipe.enq(CRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged CRs .rs: begin
|
||||
pipe.enq(CRs (LLPipeCRsCmd {
|
||||
addr: rs.addr,
|
||||
child: rs.child
|
||||
}), rs.data, DownDir (rs.toState));
|
||||
end
|
||||
tagged MRs .rs: begin
|
||||
pipe.enq(MRs (LLPipeMRsCmd {
|
||||
addr: rs.addr,
|
||||
way: rs.way
|
||||
}), Valid (rs.data), UpCs (rs.toState));
|
||||
end
|
||||
endcase
|
||||
endmethod
|
||||
|
||||
// need to adapt pipeline output to real output format
|
||||
method pipeOutT first;
|
||||
return getFirst(pipe.first); // guarded version
|
||||
endmethod
|
||||
|
||||
method pipeOutT unguard_first;
|
||||
return getFirst(pipe.unguard_first); // unguarded version
|
||||
endmethod
|
||||
|
||||
method notEmpty = pipe.notEmpty;
|
||||
|
||||
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
|
||||
// get new cmd
|
||||
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
|
||||
Maybe#(pipeCmdT) newCmd = Invalid;
|
||||
if(swapRq matches tagged Valid .idx) begin
|
||||
newCmd = Valid (CRq (LLPipeCRqIn {addr: addr, mshrIdx: idx}));
|
||||
end
|
||||
// call pipe
|
||||
pipe.deqWrite(newCmd, wrRam, updateRep);
|
||||
endmethod
|
||||
endmodule
|
||||
83
src_Core/RISCY_OOO/coherence/src/MshrDeadlockChecker.bsv
Normal file
83
src_Core/RISCY_OOO/coherence/src/MshrDeadlockChecker.bsv
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Ehr::*;
|
||||
import CCTypes::*;
|
||||
import Vector::*;
|
||||
import ProcTypes::*;
|
||||
|
||||
interface MshrDeadlockChecker#(numeric type num);
|
||||
method ActionValue#(Maybe#(Bit#(TLog#(num)))) getStuckIdx; // get deadlock MSHR idx
|
||||
method Action initEntry(Bit#(TLog#(num)) n); // new MSHR entry allocated
|
||||
method Action releaseEntry(Bit#(TLog#(num)) n); // existing MSHR entry released
|
||||
endinterface
|
||||
|
||||
module mkMshrDeadlockChecker(MshrDeadlockChecker#(num)) provisos(
|
||||
Alias#(idxT, Bit#(TLog#(num)))
|
||||
);
|
||||
Integer check_port = 0;
|
||||
Integer incr_port = 1;
|
||||
// timer for each entry to detect deadlock: being processed for 64M cycles
|
||||
Vector#(num, Ehr#(2, Maybe#(DeadlockTimer))) timer <- replicateM(mkEhr(Invalid));
|
||||
// when new entry is allocated, init timer
|
||||
Vector#(num, PulseWire) init <- replicateM(mkPulseWire);
|
||||
// when existing entry is released, end the timer
|
||||
Vector#(num, PulseWire) done <- replicateM(mkPulseWire);
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incrTimer;
|
||||
for(Integer i = 0; i < valueof(num); i = i+1) begin
|
||||
if(init[i]) begin
|
||||
timer[i][incr_port] <= Valid (0);
|
||||
end
|
||||
else if(done[i]) begin
|
||||
timer[i][incr_port] <= Invalid;
|
||||
end
|
||||
else if(timer[i][incr_port] matches tagged Valid .t &&& t != maxBound) begin
|
||||
timer[i][incr_port] <= Valid (t + 1);
|
||||
end
|
||||
end
|
||||
endrule
|
||||
|
||||
method ActionValue#(Maybe#(idxT)) getStuckIdx;
|
||||
function Bool isDeadlock(Integer i);
|
||||
return timer[i][check_port] == Valid (maxBound);
|
||||
endfunction
|
||||
Vector#(num, Integer) idxVec = genVector;
|
||||
if(searchIndex(isDeadlock, idxVec) matches tagged Valid .n) begin
|
||||
timer[n][check_port] <= Valid (0);
|
||||
return Valid (n);
|
||||
end
|
||||
else begin
|
||||
return Invalid;
|
||||
end
|
||||
endmethod
|
||||
|
||||
method Action initEntry(idxT n);
|
||||
init[n].send;
|
||||
endmethod
|
||||
|
||||
method Action releaseEntry(idxT n);
|
||||
done[n].send;
|
||||
endmethod
|
||||
endmodule
|
||||
64
src_Core/RISCY_OOO/coherence/src/RWBramCore.bsv
Normal file
64
src_Core/RISCY_OOO/coherence/src/RWBramCore.bsv
Normal file
@@ -0,0 +1,64 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import BRAMCore::*;
|
||||
import Fifo::*;
|
||||
|
||||
interface RWBramCore#(type addrT, type dataT);
|
||||
method Action wrReq(addrT a, dataT d);
|
||||
method Action rdReq(addrT a);
|
||||
method dataT rdResp;
|
||||
method Bool rdRespValid;
|
||||
method Action deqRdResp;
|
||||
endinterface
|
||||
|
||||
module mkRWBramCore(RWBramCore#(addrT, dataT)) provisos(
|
||||
Bits#(addrT, addrSz), Bits#(dataT, dataSz)
|
||||
);
|
||||
BRAM_DUAL_PORT#(addrT, dataT) bram <- mkBRAMCore2(valueOf(TExp#(addrSz)), False);
|
||||
BRAM_PORT#(addrT, dataT) wrPort = bram.a;
|
||||
BRAM_PORT#(addrT, dataT) rdPort = bram.b;
|
||||
// 1 elem pipeline fifo to add guard for read req/resp
|
||||
// must be 1 elem to make sure rdResp is not corrupted
|
||||
// BRAMCore should not change output if no req is made
|
||||
Fifo#(1, void) rdReqQ <- mkPipelineFifo;
|
||||
|
||||
method Action wrReq(addrT a, dataT d);
|
||||
wrPort.put(True, a, d);
|
||||
endmethod
|
||||
|
||||
method Action rdReq(addrT a);
|
||||
rdReqQ.enq(?);
|
||||
rdPort.put(False, a, ?);
|
||||
endmethod
|
||||
|
||||
method dataT rdResp if(rdReqQ.notEmpty);
|
||||
return rdPort.read;
|
||||
endmethod
|
||||
|
||||
method rdRespValid = rdReqQ.notEmpty;
|
||||
|
||||
method Action deqRdResp;
|
||||
rdReqQ.deq;
|
||||
endmethod
|
||||
endmodule
|
||||
90
src_Core/RISCY_OOO/coherence/src/RandomReplace.bsv
Normal file
90
src_Core/RISCY_OOO/coherence/src/RandomReplace.bsv
Normal file
@@ -0,0 +1,90 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import Fifo::*;
|
||||
import CCTypes::*;
|
||||
import RWBramCore::*;
|
||||
|
||||
// random replace does not need any bookkeeping
|
||||
typedef void RandRepInfo;
|
||||
|
||||
function RandRepInfo randRepInitInfo;
|
||||
return ?;
|
||||
endfunction
|
||||
|
||||
module mkRandRepRam(RWBramCore#(indexT, RandRepInfo));
|
||||
Fifo#(1, void) rdReqQ <- mkPipelineFifo;
|
||||
|
||||
method Action wrReq(indexT a, RandRepInfo d);
|
||||
noAction;
|
||||
endmethod
|
||||
method Action rdReq(indexT a);
|
||||
rdReqQ.enq(?);
|
||||
endmethod
|
||||
method RandRepInfo rdResp if(rdReqQ.notEmpty);
|
||||
return ?;
|
||||
endmethod
|
||||
method rdRespValid = rdReqQ.notEmpty;
|
||||
method deqRdResp = rdReqQ.deq;
|
||||
endmodule
|
||||
|
||||
interface RandomReplace#(numeric type wayNum);
|
||||
// find a way to replace, which is not locked
|
||||
// and Invalid way has priority
|
||||
method Maybe#(Bit#(TLog#(wayNum))) getReplaceWay(
|
||||
Vector#(wayNum, Bool) unlocked,
|
||||
Vector#(wayNum, Bool) invalid
|
||||
);
|
||||
endinterface
|
||||
|
||||
module mkRandomReplace(RandomReplace#(wayNum)) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum)))
|
||||
);
|
||||
Reg#(wayT) randWay <- mkReg(0);
|
||||
|
||||
rule tick;
|
||||
randWay <= randWay == fromInteger(valueOf(wayNum) - 1) ? 0 : randWay + 1;
|
||||
endrule
|
||||
|
||||
method Maybe#(wayT) getReplaceWay(Vector#(wayNum, Bool) unlocked, Vector#(wayNum, Bool) invalid);
|
||||
// first search for invalid & unlocked way
|
||||
function Bool isInvUnlock(Integer i);
|
||||
return unlocked[i] && invalid[i];
|
||||
endfunction
|
||||
Vector#(wayNum, Integer) idxVec = genVector;
|
||||
Maybe#(wayT) repWay = searchIndex(isInvUnlock, idxVec);
|
||||
if(!isValid(repWay)) begin
|
||||
// check whether random way is unlocked
|
||||
if(unlocked[randWay]) begin
|
||||
repWay = Valid (randWay);
|
||||
end
|
||||
else begin
|
||||
// just find a unlocked way
|
||||
repWay = searchIndex(id, unlocked);
|
||||
end
|
||||
end
|
||||
return repWay;
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
629
src_Core/RISCY_OOO/coherence/src/SelfInvIBank.bsv
Normal file
629
src_Core/RISCY_OOO/coherence/src/SelfInvIBank.bsv
Normal file
@@ -0,0 +1,629 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
|
||||
import Types::*;
|
||||
import MemoryTypes::*;
|
||||
import Amo::*;
|
||||
|
||||
import Cntrs::*;
|
||||
import Vector::*;
|
||||
import ConfigReg::*;
|
||||
import FIFO::*;
|
||||
import GetPut::*;
|
||||
import ClientServer::*;
|
||||
import CCTypes::*;
|
||||
import ICRqMshr::*;
|
||||
import CCPipe::*;
|
||||
import SelfInvIPipe ::*;
|
||||
import FShow::*;
|
||||
import DefaultValue::*;
|
||||
import Fifo::*;
|
||||
import CacheUtils::*;
|
||||
import Performance::*;
|
||||
import LatencyTimer::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export SelfInvICRqStuck(..);
|
||||
export SelfInvIPRqStuck(..);
|
||||
export SelfInvIBank(..);
|
||||
export mkSelfInvIBank;
|
||||
|
||||
// L1 I$, no pRq
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
ICRqState state;
|
||||
Bool waitP;
|
||||
} SelfInvICRqStuck deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef void SelfInvIPRqStuck; // not used
|
||||
|
||||
interface SelfInvIBank#(
|
||||
numeric type supSz, // superscalar size
|
||||
numeric type lgBankNum,
|
||||
numeric type wayNum,
|
||||
numeric type indexSz,
|
||||
numeric type tagSz,
|
||||
numeric type cRqNum
|
||||
);
|
||||
interface ChildCacheToParent#(Bit#(TLog#(wayNum)), void) to_parent;
|
||||
interface InstServer#(supSz) to_proc; // to child, i.e. processor
|
||||
// detect deadlock: only in use when macro CHECK_DEADLOCK is defined
|
||||
interface Get#(SelfInvICRqStuck) cRqStuck;
|
||||
interface Get#(SelfInvIPRqStuck) pRqStuck;
|
||||
// security: flush (not implemented)
|
||||
method Action flush;
|
||||
method Bool flush_done;
|
||||
// reconcile
|
||||
method Action reconcile;
|
||||
method Bool reconcile_done;
|
||||
// performance
|
||||
method Action setPerfStatus(Bool stats);
|
||||
method Data getPerfData(L1IPerfType t);
|
||||
endinterface
|
||||
|
||||
module mkSelfInvIBank#(
|
||||
Bit#(lgBankNum) bankId,
|
||||
module#(ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT)) mkICRqMshrLocal,
|
||||
module#(SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT)) mkIPipeline
|
||||
)(
|
||||
SelfInvIBank#(supSz, lgBankNum, wayNum, indexSz, tagSz, cRqNum)
|
||||
) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(TLog#(cRqNum))),
|
||||
Alias#(pRqIdxT, Bit#(TLog#(pRqNum))),
|
||||
Alias#(cacheOwnerT, Maybe#(cRqIdxT)), // owner cannot be pRq
|
||||
Alias#(otherT, void),
|
||||
Alias#(cacheInfoT, CacheInfo#(tagT, Msi, void, cacheOwnerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, void, cacheOwnerT, otherT, Line)),
|
||||
Alias#(procRqT, ProcRqToI),
|
||||
Alias#(cRqToPT, CRqMsg#(wayT, void)),
|
||||
Alias#(cRsToPT, CRsMsg#(void)),
|
||||
Alias#(pRqFromPT, PRqMsg#(void)),
|
||||
Alias#(pRsFromPT, PRsMsg#(wayT, void)),
|
||||
Alias#(pRqRsFromPT, PRqRsMsg#(wayT, void)),
|
||||
Alias#(cRqSlotT, ICRqSlot#(wayT, tagT)), // cRq MSHR slot
|
||||
Alias#(iCmdT, SelfInvICmd#(cRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, void, cacheOwnerT, otherT, RandRepInfo, Line, iCmdT)),
|
||||
Alias#(resultT, Vector#(supSz, Maybe#(Instruction))),
|
||||
// requirements
|
||||
FShow#(pipeOutT),
|
||||
Add#(tagSz, a__, AddrSz),
|
||||
// make sure: cRqNum <= wayNum
|
||||
Add#(cRqNum, b__, wayNum),
|
||||
Add#(TAdd#(tagSz, indexSz), TAdd#(lgBankNum, LgLineSzBytes), AddrSz)
|
||||
);
|
||||
|
||||
ICRqMshr#(cRqNum, wayT, tagT, procRqT, resultT) cRqMshr <- mkICRqMshrLocal;
|
||||
|
||||
SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT) pipeline <- mkIPipeline;
|
||||
|
||||
Fifo#(1, Addr) rqFromCQ <- mkBypassFifo;
|
||||
|
||||
Fifo#(2, cRqToPT) rqToPQ <- mkCFFifo;
|
||||
Fifo#(2, pRqRsFromPT) fromPQ <- mkCFFifo;
|
||||
|
||||
FIFO#(cRqIdxT) rqToPIndexQ <- mkSizedFIFO(valueOf(cRqNum));
|
||||
|
||||
// index Q to order all in flight cRq for in-order resp
|
||||
FIFO#(cRqIdxT) cRqIndexQ <- mkSizedFIFO(valueof(cRqNum));
|
||||
|
||||
// Reconcile states
|
||||
Reg#(Bool) needReconcile <- mkReg(False);
|
||||
Reg#(Bool) waitReconcileDone <- mkReg(False);
|
||||
|
||||
`ifdef DEBUG_ICACHE
|
||||
// id for each cRq, incremented when each new req comes
|
||||
Reg#(Bit#(64)) cRqId <- mkReg(0);
|
||||
// FIFO to signal the id of cRq that is performed
|
||||
// FIFO has 0 cycle latency to match L1 D$ resp latency
|
||||
Fifo#(1, DebugICacheResp) cRqDoneQ <- mkBypassFifo;
|
||||
`endif
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
Reg#(Bool) doStats <- mkConfigReg(False);
|
||||
Count#(Data) ldCnt <- mkCount(0);
|
||||
Count#(Data) ldMissCnt <- mkCount(0);
|
||||
Count#(Data) ldMissLat <- mkCount(0);
|
||||
Count#(Data) reconcileCnt <- mkCount(0);
|
||||
|
||||
LatencyTimer#(cRqNum, 10) latTimer <- mkLatencyTimer;
|
||||
|
||||
function Action incrReqCnt;
|
||||
action
|
||||
if(doStats) begin
|
||||
ldCnt.incr(1);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
function Action incrMissCnt(cRqIdxT idx);
|
||||
action
|
||||
let lat <- latTimer.done(idx);
|
||||
if(doStats) begin
|
||||
ldMissLat.incr(zeroExtend(lat));
|
||||
ldMissCnt.incr(1);
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
`endif
|
||||
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
// XXX since I$ may be requested by processor constantly
|
||||
// cRq may come at every cycle, so we must make cRq has lower priority than pRq/pRs
|
||||
// otherwise the whole system may deadlock/livelock
|
||||
// we stop accepting cRq when we need to reconcile
|
||||
rule cRqTransfer(!needReconcile);
|
||||
Addr addr <- toGet(rqFromCQ).get;
|
||||
`ifdef DEBUG_ICACHE
|
||||
procRqT r = ProcRqToI {addr: addr, id: cRqId};
|
||||
cRqId <= cRqId + 1;
|
||||
`else
|
||||
procRqT r = ProcRqToI {addr: addr};
|
||||
`endif
|
||||
cRqIdxT n <- cRqMshr.getEmptyEntryInit(r);
|
||||
// send to pipeline
|
||||
pipeline.send(CRq (SelfInvIPipeRqIn {
|
||||
addr: r.addr,
|
||||
mshrIdx: n
|
||||
}));
|
||||
// enq to indexQ for in order resp
|
||||
cRqIndexQ.enq(n);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: cRq type
|
||||
incrReqCnt;
|
||||
`endif
|
||||
$display("%t I %m cRqTransfer: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(r)
|
||||
);
|
||||
endrule
|
||||
|
||||
// this descending urgency is necessary to avoid deadlock/livelock
|
||||
// pRq cannot happen, because I$ is never exclusive
|
||||
(* descending_urgency = "pRqTransfer, cRqTransfer" *)
|
||||
rule pRqTransfer(fromPQ.first matches tagged PRq .req);
|
||||
fromPQ.deq;
|
||||
$display("%t I %m pRqTransfer: ", $time, fshow(req));
|
||||
doAssert(False, "should not have pRq");
|
||||
endrule
|
||||
|
||||
// this descending urgency is necessary to avoid deadlock/livelock
|
||||
(* descending_urgency = "pRsTransfer, cRqTransfer" *)
|
||||
rule pRsTransfer(fromPQ.first matches tagged PRs .resp);
|
||||
fromPQ.deq;
|
||||
pipeline.send(PRs (SelfInvIPipePRsIn {
|
||||
addr: resp.addr,
|
||||
toState: S,
|
||||
data: resp.data,
|
||||
way: resp.id
|
||||
}));
|
||||
$display("%t I %m pRsTransfer: ", $time, fshow(resp));
|
||||
doAssert(resp.toState == S && isValid(resp.data), "I$ must upgrade to S with data");
|
||||
endrule
|
||||
|
||||
rule sendRqToP;
|
||||
rqToPIndexQ.deq;
|
||||
cRqIdxT n = rqToPIndexQ.first;
|
||||
procRqT req = cRqMshr.sendRqToP.getRq(n);
|
||||
cRqSlotT slot = cRqMshr.sendRqToP.getSlot(n);
|
||||
cRqToPT cRqToP = CRqMsg {
|
||||
addr: req.addr,
|
||||
fromState: I, // I$ upgrade from I
|
||||
toState: S, // I$ upgrade to S
|
||||
canUpToE: False,
|
||||
id: slot.way,
|
||||
child: ?
|
||||
};
|
||||
rqToPQ.enq(cRqToP);
|
||||
$display("%t I %m sendRqToP: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(req), " ; ",
|
||||
fshow(slot), " ; ",
|
||||
fshow(cRqToP)
|
||||
);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: start miss timer
|
||||
latTimer.start(n);
|
||||
`endif
|
||||
endrule
|
||||
|
||||
// last stage of pipeline: process req
|
||||
|
||||
// XXX: in L1, pRq cannot exist in dependency chain
|
||||
// because there are only two ways to include pRq into chain
|
||||
// (1) append to a cRq that could finish, but such cRq must have been directly reponded
|
||||
// (2) overtake cRq (S->M), but such downgrade can be done instaneously without the need of chaining
|
||||
// (this cannot happen in I$)
|
||||
// Thus, dependency chain in L1 only contains cRq
|
||||
|
||||
// pipeline outputs
|
||||
pipeOutT pipeOut = pipeline.first;
|
||||
ramDataT ram = pipeOut.ram;
|
||||
// get proc req to select from cRqMshr
|
||||
procRqT pipeOutCRq = cRqMshr.pipelineResp.getRq(
|
||||
case(pipeOut.cmd) matches
|
||||
tagged ICRq .n: (n);
|
||||
default: (fromMaybe(0, ram.info.owner)); // L1PRs
|
||||
endcase
|
||||
);
|
||||
|
||||
// function to get superscaler inst read result
|
||||
function resultT readInst(Line line, Addr addr);
|
||||
Vector#(LineSzInst, Instruction) instVec = unpack(pack(line));
|
||||
// the start offset for reading inst
|
||||
LineInstOffset startSel = getLineInstOffset(addr);
|
||||
// calculate the maximum inst count that could be read from line
|
||||
LineInstOffset maxCntMinusOne = maxBound - startSel;
|
||||
// read inst superscalaer
|
||||
resultT val = ?;
|
||||
for(Integer i = 0; i < valueof(supSz); i = i+1) begin
|
||||
if(fromInteger(i) <= maxCntMinusOne) begin
|
||||
LineInstOffset sel = startSel + fromInteger(i);
|
||||
val[i] = Valid (instVec[sel]);
|
||||
end
|
||||
else begin
|
||||
val[i] = Invalid;
|
||||
end
|
||||
end
|
||||
return val;
|
||||
endfunction
|
||||
|
||||
// function to process cRq hit (MSHR slot may have garbage)
|
||||
function Action cRqHit(cRqIdxT n, procRqT req);
|
||||
action
|
||||
$display("%t I %m pipelineResp: Hit func: ", $time,
|
||||
fshow(n), " ; ",
|
||||
fshow(req)
|
||||
);
|
||||
// check tag & cs: even this function is called by pRs, tag should match,
|
||||
// because tag is written into cache before sending req to parent
|
||||
doAssert(ram.info.tag == getTag(req.addr) && ram.info.cs == S,
|
||||
"cRqHit but tag or cs incorrect"
|
||||
);
|
||||
// deq pipeline or swap in successor
|
||||
Maybe#(cRqIdxT) succ = cRqMshr.pipelineResp.getSucc(n);
|
||||
pipeline.deqWrite(succ, RamData {
|
||||
info: CacheInfo {
|
||||
tag: getTag(req.addr), // should be the same as original tag
|
||||
cs: ram.info.cs, // use cs in ram
|
||||
dir: ?,
|
||||
owner: succ,
|
||||
other: ?
|
||||
},
|
||||
line: ram.line
|
||||
}, True); // hit, so update rep info
|
||||
// process req to get superscalar inst read results
|
||||
// set MSHR entry as Done & save inst results
|
||||
let instResult = readInst(ram.line, req.addr);
|
||||
cRqMshr.pipelineResp.setResult(n, instResult);
|
||||
cRqMshr.pipelineResp.setStateSlot(n, Done, ?);
|
||||
$display("%t I %m pipelineResp: Hit func: update ram: ", $time,
|
||||
fshow(succ), " ; ", fshow(instResult)
|
||||
);
|
||||
`ifdef DEBUG_ICACHE
|
||||
// signal that this req is performed
|
||||
cRqDoneQ.enq(DebugICacheResp {
|
||||
id: req.id,
|
||||
line: ram.line
|
||||
});
|
||||
`endif
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
rule pipelineResp_cRq(pipeOut.cmd matches tagged ICRq .n);
|
||||
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
|
||||
|
||||
procRqT procRq = pipeOutCRq;
|
||||
$display("%t I %m pipelineResp: cRq: ", $time, fshow(n), " ; ", fshow(procRq));
|
||||
|
||||
// find end of dependency chain
|
||||
Maybe#(cRqIdxT) cRqEOC = cRqMshr.pipelineResp.searchEndOfChain(procRq.addr);
|
||||
|
||||
// function to process cRq miss without replacement (MSHR slot may have garbage)
|
||||
// We never replace in self-inv I$, because all S lines can be replaced silently
|
||||
function Action cRqMissNoReplacement;
|
||||
action
|
||||
cRqSlotT cSlot = cRqMshr.pipelineResp.getSlot(n);
|
||||
// it is impossible in L1 to have slot.waitP == True in this function
|
||||
// because cRq is not set to Depend when pRq invalidates it (pRq just directly resp)
|
||||
doAssert(!cSlot.waitP, "waitP must be false");
|
||||
// No exclusive in I$
|
||||
doAssert(ram.info.cs <= S, "no exclusive in I$");
|
||||
// This cannot be a hit
|
||||
doAssert(ram.info.cs == I || ram.info.tag != getTag(procRq.addr), "cannot hit");
|
||||
// Thus we must send req to parent
|
||||
rqToPIndexQ.enq(n);
|
||||
// update mshr
|
||||
cRqMshr.pipelineResp.setStateSlot(n, WaitSt, ICRqSlot {
|
||||
way: pipeOut.way, // use way from pipeline
|
||||
repTag: ?, // no replacement
|
||||
waitP: True // must fetch from parent
|
||||
});
|
||||
// deq pipeline & set owner, tag
|
||||
pipeline.deqWrite(Invalid, RamData {
|
||||
info: CacheInfo {
|
||||
tag: getTag(procRq.addr), // tag may be garbage if cs == I or silent replace
|
||||
cs: I, // line must be invalid
|
||||
dir: ?,
|
||||
owner: Valid (n), // owner is req itself
|
||||
other: ?
|
||||
},
|
||||
line: ram.line
|
||||
}, False);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// function to set cRq to Depend, and make no further change to cache
|
||||
function Action cRqSetDepNoCacheChange;
|
||||
action
|
||||
cRqMshr.pipelineResp.setStateSlot(n, Depend, defaultValue);
|
||||
pipeline.deqWrite(Invalid, pipeOut.ram, False);
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
if(ram.info.owner matches tagged Valid .cOwner) begin
|
||||
if(cOwner != n) begin
|
||||
// owner is another cRq, so must just go through tag match
|
||||
// tag match must be hit (because replacement algo won't give a way with owner)
|
||||
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
|
||||
"cRq should hit in tag match"
|
||||
);
|
||||
// should be added to a cRq in dependency chain & deq from pipeline
|
||||
doAssert(isValid(cRqEOC), "cRq hit on another cRq, cRqEOC must be true");
|
||||
cRqMshr.pipelineResp.setSucc(fromMaybe(?, cRqEOC), Valid (n));
|
||||
cRqSetDepNoCacheChange;
|
||||
$display("%t I %m pipelineResp: cRq: own by other cRq ", $time,
|
||||
fshow(cOwner), ", depend on cRq ", fshow(cRqEOC)
|
||||
);
|
||||
end
|
||||
else begin
|
||||
// owner is myself, so must be swapped in
|
||||
// tag should match, since always swapped in by cRq, cs = S
|
||||
// Reconcile happens only when no cRq in MSHR, so it won't affect swap
|
||||
doAssert(ram.info.tag == getTag(procRq.addr) && ram.info.cs == S,
|
||||
"cRq swapped in by previous cRq, tag must match & cs = S"
|
||||
);
|
||||
// Hit
|
||||
$display("%t I %m pipelineResp: cRq: own by itself, hit", $time);
|
||||
cRqHit(n, procRq);
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// cache has no owner, cRq must just go through tag match
|
||||
// check for cRqEOC to append to dependency chain
|
||||
if(cRqEOC matches tagged Valid .k) begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, depend on cRq ", $time, fshow(k));
|
||||
cRqMshr.pipelineResp.setSucc(k, Valid (n));
|
||||
cRqSetDepNoCacheChange;
|
||||
end
|
||||
else if(ram.info.cs > I && ram.info.tag == getTag(procRq.addr)) begin
|
||||
$display("%t I %m pipelineResp: cRq: no owner, hit", $time);
|
||||
cRqHit(n, procRq);
|
||||
end
|
||||
else begin
|
||||
// can always sliently replace
|
||||
$display("%t I %m pipelineResp: cRq: no owner, miss no replace", $time);
|
||||
cRqMissNoReplacement;
|
||||
end
|
||||
end
|
||||
endrule
|
||||
|
||||
rule pipelineResp_pRs(pipeOut.cmd == IPRs);
|
||||
$display("%t I %m pipelineResp: ", $time, fshow(pipeOut));
|
||||
$display("%t I %m pipelineResp: pRs: ", $time);
|
||||
|
||||
if(ram.info.owner matches tagged Valid .cOwner) begin
|
||||
procRqT procRq = pipeOutCRq;
|
||||
doAssert(ram.info.cs == S && ram.info.tag == getTag(procRq.addr),
|
||||
"pRs must be a hit"
|
||||
);
|
||||
cRqHit(cOwner, procRq);
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: miss cRq
|
||||
incrMissCnt(cOwner);
|
||||
`endif
|
||||
end
|
||||
else begin
|
||||
doAssert(False, ("pRs owner must match some cRq"));
|
||||
end
|
||||
endrule
|
||||
|
||||
// Reconcile lines in S state: start after cRq MSHR is empty
|
||||
// Since cRqTransfer rule cannot fire when needReconcile is true, we use a
|
||||
// wire to catch cRqMshr.empty to avoid scheduling cycles
|
||||
PulseWire cRqMshrEmpty <- mkPulseWire;
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule setCRqMshrEmpty(cRqMshr.emptyForFlush);
|
||||
cRqMshrEmpty.send;
|
||||
endrule
|
||||
rule startReconcile(needReconcile && !waitReconcileDone && cRqMshrEmpty);
|
||||
pipeline.reconcile;
|
||||
waitReconcileDone <= True;
|
||||
$display("%t I %m startReconcile", $time);
|
||||
`ifdef PERF_COUNT
|
||||
if(doStats) begin
|
||||
reconcileCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
endrule
|
||||
rule completeReconcile(needReconcile && waitReconcileDone && pipeline.reconcile_done);
|
||||
needReconcile <= False;
|
||||
waitReconcileDone <= False;
|
||||
$display("%t I %m completeReconcile", $time);
|
||||
endrule
|
||||
|
||||
interface ChildCacheToParent to_parent;
|
||||
interface rsToP = nullFifoDeq;
|
||||
interface rqToP = toFifoDeq(rqToPQ);
|
||||
interface fromP = toFifoEnq(fromPQ);
|
||||
endinterface
|
||||
|
||||
interface InstServer to_proc;
|
||||
interface Put req;
|
||||
method Action put(Addr addr);
|
||||
rqFromCQ.enq(addr);
|
||||
endmethod
|
||||
endinterface
|
||||
interface Get resp;
|
||||
method ActionValue#(resultT) get if(
|
||||
cRqMshr.sendRsToC.getResult(cRqIndexQ.first) matches tagged Valid .inst
|
||||
);
|
||||
cRqIndexQ.deq;
|
||||
cRqMshr.sendRsToC.releaseEntry(cRqIndexQ.first); // release MSHR entry
|
||||
$display("%t I %m sendRsToC: ", $time,
|
||||
fshow(cRqIndexQ.first), " ; ",
|
||||
fshow(inst)
|
||||
);
|
||||
return inst;
|
||||
endmethod
|
||||
endinterface
|
||||
`ifdef DEBUG_ICACHE
|
||||
interface done = toGet(cRqDoneQ);
|
||||
`endif
|
||||
endinterface
|
||||
|
||||
interface Get cRqStuck;
|
||||
method ActionValue#(SelfInvICRqStuck) get;
|
||||
let s <- cRqMshr.stuck.get;
|
||||
return SelfInvICRqStuck {
|
||||
addr: s.req.addr,
|
||||
state: s.state,
|
||||
waitP: s.waitP
|
||||
};
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface pRqStuck = nullGet;
|
||||
|
||||
`ifdef SECURITY
|
||||
method Action flush if(flushDone);
|
||||
flushDone <= False;
|
||||
endmethod
|
||||
method flush_done = flushDone._read;
|
||||
`else
|
||||
method flush = noAction;
|
||||
method flush_done = True;
|
||||
`endif
|
||||
|
||||
method Action reconcile if(!needReconcile);
|
||||
needReconcile <= True;
|
||||
endmethod
|
||||
method Bool reconcile_done;
|
||||
return !needReconcile;
|
||||
endmethod
|
||||
|
||||
method Action setPerfStatus(Bool stats);
|
||||
`ifdef PERF_COUNT
|
||||
doStats <= stats;
|
||||
`else
|
||||
noAction;
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method Data getPerfData(L1IPerfType t);
|
||||
return (case(t)
|
||||
`ifdef PERF_COUNT
|
||||
L1ILdCnt: ldCnt;
|
||||
L1ILdMissCnt: ldMissCnt;
|
||||
L1ILdMissLat: ldMissLat;
|
||||
L1IReconcileCnt: reconcileCnt;
|
||||
`endif
|
||||
default: 0;
|
||||
endcase);
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
|
||||
// Scheduling note
|
||||
|
||||
// cRqTransfer (toC.req.put): write new cRq MSHR entry, cRqMshr.getEmptyEntry
|
||||
|
||||
// pRqTransfer: write new pRq MSHR entry, pRqMshr.getEmptyEntry
|
||||
|
||||
// pRsTransfer: -
|
||||
|
||||
// sendRsToC (toC.resp.get): read cRq MSHR result, releaseEntry
|
||||
|
||||
// sendRsToP_cRq: read cRq MSHR req/slot that is replacing
|
||||
|
||||
// sendRsToP_pRq: read pRq MSHR entry that is responding, pRqMshr.releaseEntry
|
||||
|
||||
// sendRqToP: read cRq MSHR req/slot that is requesting parent
|
||||
|
||||
// pipelineResp_cRq:
|
||||
// -- read cRq MSHR req/state/slot currently processed
|
||||
// -- write cRq MSHR state/slot/result currently processed
|
||||
// -- write succ of some existing cRq MSHR entry (in WaitNewTag or WaitSt)
|
||||
// -- read all state/req/succ in cRq MSHR entry (searchEOC)
|
||||
// -- not affected by write in cRqTransfer (state change is Empty->Init)
|
||||
// -- not affected by write in sendRsC (state change is Done->Empty)
|
||||
|
||||
// pipelineResp_pRs:
|
||||
// -- read cRq MSHR req/succ, write cRq MSHR state/slot/result
|
||||
|
||||
// pipelineResp_pRq:
|
||||
// -- r/w pRq MSHR entry, pRqMshr.releaseEntry
|
||||
|
||||
// ---- conflict analysis ----
|
||||
|
||||
// XXXTransfer is conflict with each other
|
||||
// Impl of getEmptyEntry and releaseEntry ensures that they are not on the same entry (e.g. cRqTransfer v.s. sendRsToC)
|
||||
// XXXTransfer should operate on different cRq/pRq from other rules
|
||||
|
||||
// sendRsToC is ordered after pipelineResp to save 1 cycle in I$ latency
|
||||
|
||||
// sendRqToP and sendRsToP_cRq are read only
|
||||
|
||||
// sendRsToP_pRq is operating on different pRq from pipelineResp_pRq (since we use CF index FIFO)
|
||||
|
||||
// ---- conclusion ----
|
||||
|
||||
// rules/methods are operating on different MSHR entries, except pipelineResp v.s. sendRsToC
|
||||
|
||||
// we have 5 ports from cRq MSHR
|
||||
// 1. cRqTransfer
|
||||
// 2. sendRsToC
|
||||
// 3. sendRsToP_cRq
|
||||
// 4. sendRqToP
|
||||
// 5. pipelineResp
|
||||
|
||||
// we have 3 ports from pRq MSHR
|
||||
// 1. pRqTransfer
|
||||
// 2. sendRsToP_pRq
|
||||
// 3. pipelineResp
|
||||
|
||||
// safe version: use EHR ports
|
||||
// sendRsToP_cRq/sendRqToP/pipelineResp < sendRsToC < cRqTransfer
|
||||
// pipelineResp < sendRsToP_pRq < pRqTransfer
|
||||
// (note there is no bypass path from pipelineResp to sendRsToP_pRq since sendRsToP_pRq only reads pRq)
|
||||
|
||||
// unsafe version: all reads read the original reg value, except sendRsToC, which should bypass from pipelineResp
|
||||
// all writes are cononicalized. NOTE: writes of sendRsToC should be after pipelineResp
|
||||
// we maintain the logical ordering in safe version
|
||||
|
||||
466
src_Core/RISCY_OOO/coherence/src/SelfInvIPipe.bsv
Normal file
466
src_Core/RISCY_OOO/coherence/src/SelfInvIPipe.bsv
Normal file
@@ -0,0 +1,466 @@
|
||||
|
||||
// Copyright (c) 2019 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Assert::*;
|
||||
import ConfigReg::*;
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import Fifo::*;
|
||||
import CCTypes::*;
|
||||
import CCPipe::*;
|
||||
import RWBramCore::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export SelfInvIPipeRqIn(..);
|
||||
export SelfInvIPipePRsIn(..);
|
||||
export SelfInvIPipeIn(..);
|
||||
export SelfInvICmd(..);
|
||||
export SelfInvIPipe(..);
|
||||
export mkSelfInvIPipe;
|
||||
|
||||
// type param ordering: bank < way < index < tag < cRq
|
||||
|
||||
// In I cache, only cRq can occupy cache line, there is no pRq or explicity
|
||||
// replacement, so cache owner type is simply Maybe#(cRqIdxT)
|
||||
|
||||
// input types
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
rqIdxT mshrIdx;
|
||||
} SelfInvIPipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
Maybe#(Line) data;
|
||||
wayT way;
|
||||
} SelfInvIPipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvIPipeRqIn#(cRqIdxT) CRq;
|
||||
SelfInvIPipePRsIn#(wayT) PRs;
|
||||
} SelfInvIPipeIn#(
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// output cmd to the processing rule in I$
|
||||
typedef union tagged {
|
||||
cRqIdxT ICRq;
|
||||
void IPRs;
|
||||
} SelfInvICmd#(
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
interface SelfInvIPipe#(
|
||||
numeric type lgBankNum,
|
||||
numeric type wayNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type cRqIdxT
|
||||
);
|
||||
method Action send(SelfInvIPipeIn#(Bit#(TLog#(wayNum)), cRqIdxT) r);
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, void, // no dir
|
||||
Maybe#(cRqIdxT), void, RandRepInfo, // no other
|
||||
Line, SelfInvICmd#(cRqIdxT)
|
||||
) first;
|
||||
method Action deqWrite(
|
||||
Maybe#(cRqIdxT) swapRq,
|
||||
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), void, Line) wrRam, // always write BRAM
|
||||
Bool updateRep
|
||||
);
|
||||
// drop stale clean cache lines
|
||||
method Action reconcile;
|
||||
method Bool reconcile_done;
|
||||
endinterface
|
||||
|
||||
// real cmd used in pipeline
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
wayT way;
|
||||
} SelfInvIPipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvIPipeRqIn#(cRqIdxT) CRq;
|
||||
SelfInvIPipePRsCmd#(wayT) PRs;
|
||||
} SelfInvIPipeCmd#(
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// cache state array with reconcile port
|
||||
// ram sub interface has same scheduling as RWBramCore:
|
||||
// - wrReq CF rdReq (read cannot see write)
|
||||
// - deqRdResp < rdReq
|
||||
interface CacheStateArray#(type indexT);
|
||||
interface RWBramCore#(indexT, Msi) ram;
|
||||
method Action reconcile;
|
||||
endinterface
|
||||
|
||||
module mkCacheStateArray(CacheStateArray#(indexT)) provisos(
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
NumAlias#(size, TExp#(indexSz))
|
||||
);
|
||||
staticAssert(pack(Msi'(I)) == 2'd0, "I = 0");
|
||||
staticAssert(pack(Msi'(S)) == 2'd1, "S = 0");
|
||||
|
||||
Vector#(size, Reg#(Bit#(1))) state <- replicateM(mkConfigReg(0));
|
||||
Fifo#(1, Msi) rdRespQ <- mkPipelineFifo;
|
||||
|
||||
interface RWBramCore ram;
|
||||
method Action wrReq(indexT idx, Msi s);
|
||||
doAssert(s == I || s == S, "only I or S");
|
||||
state[idx] <= truncate(pack(s));
|
||||
endmethod
|
||||
method Action rdReq(indexT idx);
|
||||
rdRespQ.enq(unpack(zeroExtend(state[idx])));
|
||||
endmethod
|
||||
method rdResp = rdRespQ.first;
|
||||
method rdRespValid = rdRespQ.notEmpty;
|
||||
method deqRdResp = rdRespQ.deq;
|
||||
endinterface
|
||||
|
||||
method Action reconcile;
|
||||
function Action resetS(Reg#(Bit#(1)) s);
|
||||
action
|
||||
s <= 0;
|
||||
endaction
|
||||
endfunction
|
||||
joinActions(map(resetS, state));
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
// Put cache state array and tag+owner ram together to form an array
|
||||
typedef struct {
|
||||
tagT tag;
|
||||
ownerT owner;
|
||||
otherT other;
|
||||
} TagOwnerOther#(type tagT, type ownerT, type otherT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface CacheInfoArray#(type indexT, type tagT, type ownerT, type otherT);
|
||||
interface RWBramCore#(indexT, CacheInfo#(tagT, Msi, void, ownerT, otherT)) ram;
|
||||
method Action reconcile;
|
||||
endinterface
|
||||
|
||||
module mkCacheInfoArray(CacheInfoArray#(indexT, tagT, ownerT, otherT)) provisos(
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
NumAlias#(size, TExp#(indexSz)),
|
||||
Alias#(tagOwnerOtherT, TagOwnerOther#(tagT, ownerT, otherT)),
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, void, ownerT, otherT)),
|
||||
Bits#(tagT, _tagSz),
|
||||
Bits#(ownerT, _ownerSz),
|
||||
Bits#(otherT, _otherSz)
|
||||
);
|
||||
RWBramCore#(indexT, tagOwnerOtherT) tagOwnerOtherRam <- mkRWBramCore;
|
||||
CacheStateArray#(indexT) csArray <- mkCacheStateArray;
|
||||
|
||||
interface RWBramCore ram;
|
||||
method Action wrReq(indexT idx, infoT x);
|
||||
tagOwnerOtherRam.wrReq(idx, TagOwnerOther {
|
||||
tag: x.tag,
|
||||
owner: x.owner,
|
||||
other: x.other
|
||||
});
|
||||
csArray.ram.wrReq(idx, x.cs);
|
||||
endmethod
|
||||
method Action rdReq(indexT idx);
|
||||
tagOwnerOtherRam.rdReq(idx);
|
||||
csArray.ram.rdReq(idx);
|
||||
endmethod
|
||||
method infoT rdResp;
|
||||
tagOwnerOtherT tagOwnerOther = tagOwnerOtherRam.rdResp;
|
||||
Msi cs = csArray.ram.rdResp;
|
||||
return CacheInfo {
|
||||
tag: tagOwnerOther.tag,
|
||||
cs: cs,
|
||||
dir: ?,
|
||||
owner: tagOwnerOther.owner,
|
||||
other: tagOwnerOther.other
|
||||
};
|
||||
endmethod
|
||||
method Bool rdRespValid;
|
||||
return tagOwnerOtherRam.rdRespValid && csArray.ram.rdRespValid;
|
||||
endmethod
|
||||
method Action deqRdResp;
|
||||
tagOwnerOtherRam.deqRdResp;
|
||||
csArray.ram.deqRdResp;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
method reconcile = csArray.reconcile;
|
||||
endmodule
|
||||
|
||||
module mkSelfInvIPipe(
|
||||
SelfInvIPipe#(lgBankNum, wayNum, indexT, tagT, cRqIdxT)
|
||||
) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(dirT, void), // no directory
|
||||
Alias#(ownerT, Maybe#(cRqIdxT)),
|
||||
Alias#(otherT, void),
|
||||
Alias#(repT, RandRepInfo),
|
||||
Alias#(pipeInT, SelfInvIPipeIn#(wayT, cRqIdxT)),
|
||||
Alias#(pipeCmdT, SelfInvIPipeCmd#(wayT, cRqIdxT)),
|
||||
Alias#(iCmdT, SelfInvICmd#(cRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, iCmdT)), // output type
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
|
||||
Alias#(respStateT, RespState#(Msi)),
|
||||
Alias#(tagMatchResT, TagMatchResult#(wayT)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
|
||||
// requirement
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
|
||||
Add#(indexSz, a__, AddrSz),
|
||||
Add#(tagSz, b__, AddrSz)
|
||||
);
|
||||
// info RAM
|
||||
Vector#(wayNum, CacheInfoArray#(indexT, tagT, ownerT, otherT)) infoArray <- replicateM(mkCacheInfoArray);
|
||||
function RWBramCore#(indexT, infoT) getInfoRam(Integer i) = infoArray[i].ram;
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam = map(getInfoRam, genVector);
|
||||
// rep RAM (dummy)
|
||||
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
|
||||
// data RAM
|
||||
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
|
||||
|
||||
// initialize RAM
|
||||
Reg#(Bool) initDone <- mkReg(False);
|
||||
Reg#(indexT) initIndex <- mkReg(0);
|
||||
|
||||
rule doInit(!initDone);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].wrReq(initIndex, CacheInfo {
|
||||
tag: 0,
|
||||
cs: I,
|
||||
dir: ?,
|
||||
owner: Invalid,
|
||||
other: ?
|
||||
});
|
||||
end
|
||||
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
|
||||
initIndex <= initIndex + 1;
|
||||
if(initIndex == maxBound) begin
|
||||
initDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
// random replacement
|
||||
RandomReplace#(wayNum) randRep <- mkRandomReplace;
|
||||
|
||||
// functions
|
||||
function Addr getAddrFromCmd(pipeCmdT cmd);
|
||||
return (case(cmd) matches
|
||||
tagged CRq .r: r.addr;
|
||||
tagged PRs .r: r.addr;
|
||||
default: ?;
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
function indexT getIndex(pipeCmdT cmd);
|
||||
Addr a = getAddrFromCmd(cmd);
|
||||
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
endfunction
|
||||
|
||||
function ActionValue#(tagMatchResT) tagMatch(
|
||||
pipeCmdT cmd,
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, Msi) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
);
|
||||
return actionvalue
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
$display("%t L1 %m tagMatch: ", $time,
|
||||
fshow(cmd), " ; ",
|
||||
fshow(getTag(getAddrFromCmd(cmd))),
|
||||
fshow(tagVec), " ; ",
|
||||
fshow(csVec), " ; ",
|
||||
fshow(ownerVec), " ; "
|
||||
);
|
||||
if(cmd matches tagged PRs .rs) begin
|
||||
// PRs directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: rs.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
doAssert(cmd matches tagged CRq .rq ? True : False, "must be cRq");
|
||||
// CRq: need tag matching
|
||||
Addr addr = getAddrFromCmd(cmd);
|
||||
tagT tag = getTag(addr);
|
||||
// find hit way (nothing is being replaced)
|
||||
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
|
||||
match {.cs, .t} = csTag;
|
||||
return cs > I && t == tag;
|
||||
endfunction
|
||||
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
|
||||
if(hitWay matches tagged Valid .w) begin
|
||||
return TagMatchResult {
|
||||
way: w,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// find a unlocked way to replace for cRq
|
||||
Vector#(wayNum, Bool) unlocked = ?;
|
||||
Vector#(wayNum, Bool) invalid = ?;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
invalid[i] = csVec[i] == I;
|
||||
unlocked[i] = !isValid(ownerVec[i]);
|
||||
end
|
||||
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
|
||||
// sanity check: repWay must be valid
|
||||
if(!isValid(repWay)) begin
|
||||
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
|
||||
$finish;
|
||||
end
|
||||
return TagMatchResult {
|
||||
way: fromMaybe(?, repWay),
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
end
|
||||
endactionvalue;
|
||||
endfunction
|
||||
|
||||
function ActionValue#(UpdateByUpCs#(Msi)) updateByUpCs(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
|
||||
);
|
||||
actionvalue
|
||||
doAssert(toState == S && oldCs == I, "upgrade cs I->S");
|
||||
doAssert(dataV, "self inv L1 always needs data resp");
|
||||
return UpdateByUpCs {cs: toState};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(UpdateByDownDir#(Msi, dirT)) updateByDownDir(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
|
||||
);
|
||||
actionvalue
|
||||
doAssert(False, "L1 should not have cRs");
|
||||
return UpdateByDownDir {cs: oldCs, dir: oldDir};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
|
||||
actionvalue
|
||||
return ?; // random replace does not have bookkeeping
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
CCPipe#(
|
||||
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
|
||||
) pipe <- mkCCPipe(
|
||||
regToReadOnly(initDone), getIndex, tagMatch,
|
||||
updateByUpCs, updateByDownDir, updateRepInfo,
|
||||
infoRam, repRam, dataRam
|
||||
);
|
||||
|
||||
// reconcile: wait until pipeline empty and drop all S states. Stall
|
||||
// pipeline enq while we are waiting. Make the reconcile rule conflict with
|
||||
// pipeline deq to remove any possible race (the guard of reconcile rule
|
||||
// actually should have done the job).
|
||||
// Since send method will not fire when needReconcile, we can use a wire to
|
||||
// catch pipeline empty signal to avoid scheduling issue
|
||||
Reg#(Bool) needReconcile <- mkReg(False);
|
||||
|
||||
RWire#(void) conflict_reconcile_deq <- mkRWire;
|
||||
|
||||
PulseWire pipeEmpty <- mkPulseWire;
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule setPipeEmpty(pipe.emptyForFlush);
|
||||
pipeEmpty.send;
|
||||
endrule
|
||||
|
||||
rule doReconcile(initDone && needReconcile && pipeEmpty);
|
||||
function Action flush(CacheInfoArray#(indexT, tagT, ownerT, otherT) ifc);
|
||||
action
|
||||
ifc.reconcile;
|
||||
endaction
|
||||
endfunction
|
||||
joinActions(map(flush, infoArray));
|
||||
// reconcile is done
|
||||
needReconcile <= False;
|
||||
// conflict with deq
|
||||
conflict_reconcile_deq.wset(?);
|
||||
$display("%t I %m doReconcile", $time);
|
||||
endrule
|
||||
|
||||
// stall enq for reconcile
|
||||
method Action send(pipeInT req) if(!needReconcile);
|
||||
case(req) matches
|
||||
tagged CRq .rq: begin
|
||||
pipe.enq(CRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged PRs .rs: begin
|
||||
pipe.enq(PRs (SelfInvIPipePRsCmd {
|
||||
addr: rs.addr,
|
||||
way: rs.way
|
||||
}), rs.data, UpCs (rs.toState));
|
||||
end
|
||||
endcase
|
||||
endmethod
|
||||
|
||||
// need to adapt pipeline output to real output format
|
||||
method pipeOutT first;
|
||||
let pout = pipe.first;
|
||||
return PipeOut {
|
||||
cmd: (case(pout.cmd) matches
|
||||
tagged CRq .rq: ICRq (rq.mshrIdx);
|
||||
tagged PRs .rs: IPRs;
|
||||
default: ?;
|
||||
endcase),
|
||||
way: pout.way,
|
||||
pRqMiss: pout.pRqMiss,
|
||||
ram: pout.ram,
|
||||
repInfo: pout.repInfo
|
||||
};
|
||||
endmethod
|
||||
|
||||
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
|
||||
// get new cmd
|
||||
Maybe#(pipeCmdT) newCmd = Invalid;
|
||||
if(swapRq matches tagged Valid .idx) begin // swap in cRq
|
||||
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
|
||||
newCmd = Valid (CRq (SelfInvIPipeRqIn {addr: addr, mshrIdx: idx}));
|
||||
end
|
||||
// call pipe
|
||||
pipe.deqWrite(newCmd, wrRam, updateRep);
|
||||
// conflict with reconcile
|
||||
conflict_reconcile_deq.wset(?);
|
||||
endmethod
|
||||
|
||||
method Action reconcile if(!needReconcile);
|
||||
needReconcile <= True;
|
||||
endmethod
|
||||
|
||||
method Bool reconcile_done;
|
||||
return !needReconcile;
|
||||
endmethod
|
||||
endmodule
|
||||
1234
src_Core/RISCY_OOO/coherence/src/SelfInvL1Bank.bsv
Normal file
1234
src_Core/RISCY_OOO/coherence/src/SelfInvL1Bank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
492
src_Core/RISCY_OOO/coherence/src/SelfInvL1Pipe.bsv
Normal file
492
src_Core/RISCY_OOO/coherence/src/SelfInvL1Pipe.bsv
Normal file
@@ -0,0 +1,492 @@
|
||||
|
||||
// Copyright (c) 2019 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Assert::*;
|
||||
import ConfigReg::*;
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import Fifo::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import CCPipe::*;
|
||||
import RWBramCore::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
export SelfInvL1PipeRqIn(..);
|
||||
export SelfInvL1PipePRsIn(..);
|
||||
export SelfInvL1PipeIn(..);
|
||||
export SelfInvL1Cmd(..);
|
||||
export SelfInvL1Hits(..);
|
||||
export SelfInvL1Pipe(..);
|
||||
export mkSelfInvL1Pipe;
|
||||
|
||||
// type param ordering: bank < way < index < tag < cRq < pRq
|
||||
|
||||
// in L1 cache, only cRq can occupy cache line (pRq handled immediately)
|
||||
// replacement is always done immediately (never have replacing line)
|
||||
// so cache owner type is simply Maybe#(cRqIdxT)
|
||||
|
||||
// input types
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
rqIdxT mshrIdx;
|
||||
} SelfInvL1PipeRqIn#(type rqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState;
|
||||
Maybe#(Line) data;
|
||||
wayT way;
|
||||
} SelfInvL1PipePRsIn#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvL1PipeRqIn#(cRqIdxT) CRq;
|
||||
SelfInvL1PipeRqIn#(pRqIdxT) PRq;
|
||||
SelfInvL1PipePRsIn#(wayT) PRs;
|
||||
} SelfInvL1PipeIn#(
|
||||
type wayT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// output cmd to the processing rule in L1$
|
||||
typedef union tagged {
|
||||
cRqIdxT L1CRq;
|
||||
pRqIdxT L1PRq;
|
||||
void L1PRs;
|
||||
} SelfInvL1Cmd#(
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// The "other" field in CacheInfo tracks the number of hits on the cache line
|
||||
// before self inv
|
||||
typedef struct {
|
||||
Bit#(TLog#(maxHitNum)) hits;
|
||||
} SelfInvL1Hits#(numeric type maxHitNum) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface SelfInvL1Pipe#(
|
||||
numeric type lgBankNum,
|
||||
numeric type wayNum,
|
||||
numeric type maxHitNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
);
|
||||
method Action send(SelfInvL1PipeIn#(Bit#(TLog#(wayNum)), cRqIdxT, pRqIdxT) r);
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, void, // no dir
|
||||
Maybe#(cRqIdxT), SelfInvL1Hits#(maxHitNum), RandRepInfo,
|
||||
Line, SelfInvL1Cmd#(cRqIdxT, pRqIdxT)
|
||||
) first;
|
||||
method Action deqWrite(
|
||||
Maybe#(cRqIdxT) swapRq,
|
||||
RamData#(tagT, Msi, void, Maybe#(cRqIdxT), SelfInvL1Hits#(maxHitNum), Line) wrRam, // always write BRAM
|
||||
Bool updateRep
|
||||
);
|
||||
// drop stale clean cache lines
|
||||
method Action reconcile;
|
||||
method Bool reconcile_done;
|
||||
endinterface
|
||||
|
||||
// real cmd used in pipeline
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
wayT way;
|
||||
} SelfInvL1PipePRsCmd#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvL1PipeRqIn#(cRqIdxT) CRq;
|
||||
SelfInvL1PipeRqIn#(pRqIdxT) PRq;
|
||||
SelfInvL1PipePRsCmd#(wayT) PRs;
|
||||
} SelfInvL1PipeCmd#(
|
||||
type wayT,
|
||||
type cRqIdxT,
|
||||
type pRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// cache state array with reconcile port
|
||||
// ram sub interface has same scheduling as RWBramCore:
|
||||
// - wrReq CF rdReq (read cannot see write)
|
||||
// - deqRdResp < rdReq
|
||||
interface CacheStateArray#(type indexT);
|
||||
interface RWBramCore#(indexT, Msi) ram;
|
||||
method Action reconcile;
|
||||
endinterface
|
||||
|
||||
module mkCacheStateArray(CacheStateArray#(indexT)) provisos(
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
NumAlias#(size, TExp#(indexSz))
|
||||
);
|
||||
Vector#(size, Reg#(Msi)) state <- replicateM(mkConfigReg(I));
|
||||
Fifo#(1, Msi) rdRespQ <- mkPipelineFifo;
|
||||
|
||||
interface RWBramCore ram;
|
||||
method Action wrReq(indexT idx, Msi s);
|
||||
state[idx] <= s;
|
||||
endmethod
|
||||
method Action rdReq(indexT idx);
|
||||
rdRespQ.enq(state[idx]);
|
||||
endmethod
|
||||
method rdResp = rdRespQ.first;
|
||||
method rdRespValid = rdRespQ.notEmpty;
|
||||
method deqRdResp = rdRespQ.deq;
|
||||
endinterface
|
||||
|
||||
method Action reconcile;
|
||||
function Action resetS(Reg#(Msi) s);
|
||||
action
|
||||
if(s == S) begin
|
||||
s <= I;
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
joinActions(map(resetS, state));
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
// Put cache state array and tag+owner ram together to form an array
|
||||
typedef struct {
|
||||
tagT tag;
|
||||
ownerT owner;
|
||||
otherT other;
|
||||
} TagOwnerOther#(type tagT, type ownerT, type otherT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface CacheInfoArray#(type indexT, type tagT, type ownerT, type otherT);
|
||||
interface RWBramCore#(indexT, CacheInfo#(tagT, Msi, void, ownerT, otherT)) ram;
|
||||
method Action reconcile;
|
||||
endinterface
|
||||
|
||||
module mkCacheInfoArray(CacheInfoArray#(indexT, tagT, ownerT, otherT)) provisos(
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
NumAlias#(size, TExp#(indexSz)),
|
||||
Alias#(tagOwnerOtherT, TagOwnerOther#(tagT, ownerT, otherT)),
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, void, ownerT, otherT)),
|
||||
Bits#(tagT, _tagSz),
|
||||
Bits#(ownerT, _ownerSz),
|
||||
Bits#(otherT, _otherSz)
|
||||
);
|
||||
RWBramCore#(indexT, tagOwnerOtherT) tagOwnerOtherRam <- mkRWBramCore;
|
||||
CacheStateArray#(indexT) csArray <- mkCacheStateArray;
|
||||
|
||||
interface RWBramCore ram;
|
||||
method Action wrReq(indexT idx, infoT x);
|
||||
tagOwnerOtherRam.wrReq(idx, TagOwnerOther {
|
||||
tag: x.tag,
|
||||
owner: x.owner,
|
||||
other: x.other
|
||||
});
|
||||
csArray.ram.wrReq(idx, x.cs);
|
||||
endmethod
|
||||
method Action rdReq(indexT idx);
|
||||
tagOwnerOtherRam.rdReq(idx);
|
||||
csArray.ram.rdReq(idx);
|
||||
endmethod
|
||||
method infoT rdResp;
|
||||
tagOwnerOtherT tagOwnerOther = tagOwnerOtherRam.rdResp;
|
||||
Msi cs = csArray.ram.rdResp;
|
||||
return CacheInfo {
|
||||
tag: tagOwnerOther.tag,
|
||||
cs: cs,
|
||||
dir: ?,
|
||||
owner: tagOwnerOther.owner,
|
||||
other: tagOwnerOther.other
|
||||
};
|
||||
endmethod
|
||||
method Bool rdRespValid;
|
||||
return tagOwnerOtherRam.rdRespValid && csArray.ram.rdRespValid;
|
||||
endmethod
|
||||
method Action deqRdResp;
|
||||
tagOwnerOtherRam.deqRdResp;
|
||||
csArray.ram.deqRdResp;
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
method reconcile = csArray.reconcile;
|
||||
endmodule
|
||||
|
||||
module mkSelfInvL1Pipe(
|
||||
SelfInvL1Pipe#(lgBankNum, wayNum, maxHitNum, indexT, tagT, cRqIdxT, pRqIdxT)
|
||||
) provisos(
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(dirT, void), // no directory
|
||||
Alias#(ownerT, Maybe#(cRqIdxT)),
|
||||
Alias#(otherT, SelfInvL1Hits#(maxHitNum)),
|
||||
Alias#(repT, RandRepInfo),
|
||||
Alias#(pipeInT, SelfInvL1PipeIn#(wayT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(pipeCmdT, SelfInvL1PipeCmd#(wayT, cRqIdxT, pRqIdxT)),
|
||||
Alias#(l1CmdT, SelfInvL1Cmd#(cRqIdxT, pRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, l1CmdT)), // output type
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
|
||||
Alias#(respStateT, RespState#(Msi)),
|
||||
Alias#(tagMatchResT, TagMatchResult#(wayT)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
|
||||
// requirement
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(cRqIdxSz)),
|
||||
Alias#(pRqIdxT, Bit#(pRqIdxSz)),
|
||||
Add#(indexSz, a__, AddrSz),
|
||||
Add#(tagSz, b__, AddrSz)
|
||||
);
|
||||
// info RAM
|
||||
Vector#(wayNum, CacheInfoArray#(indexT, tagT, ownerT, otherT)) infoArray <- replicateM(mkCacheInfoArray);
|
||||
function RWBramCore#(indexT, infoT) getInfoRam(Integer i) = infoArray[i].ram;
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam = map(getInfoRam, genVector);
|
||||
// rep RAM (dummy)
|
||||
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
|
||||
// data RAM
|
||||
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
|
||||
|
||||
// initialize RAM
|
||||
Reg#(Bool) initDone <- mkReg(False);
|
||||
Reg#(indexT) initIndex <- mkReg(0);
|
||||
|
||||
rule doInit(!initDone);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].wrReq(initIndex, CacheInfo {
|
||||
tag: 0,
|
||||
cs: I,
|
||||
dir: ?,
|
||||
owner: Invalid,
|
||||
other: SelfInvL1Hits {hits: 0}
|
||||
});
|
||||
end
|
||||
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
|
||||
initIndex <= initIndex + 1;
|
||||
if(initIndex == maxBound) begin
|
||||
initDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
// random replacement
|
||||
RandomReplace#(wayNum) randRep <- mkRandomReplace;
|
||||
|
||||
// functions
|
||||
function Addr getAddrFromCmd(pipeCmdT cmd);
|
||||
return (case(cmd) matches
|
||||
tagged CRq .r: r.addr;
|
||||
tagged PRq .r: r.addr;
|
||||
tagged PRs .r: r.addr;
|
||||
default: ?;
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
function indexT getIndex(pipeCmdT cmd);
|
||||
Addr a = getAddrFromCmd(cmd);
|
||||
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
endfunction
|
||||
|
||||
function ActionValue#(tagMatchResT) tagMatch(
|
||||
pipeCmdT cmd,
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, Msi) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
);
|
||||
return actionvalue
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
$display("%t L1 %m tagMatch: ", $time,
|
||||
fshow(cmd), " ; ",
|
||||
fshow(getTag(getAddrFromCmd(cmd))),
|
||||
fshow(tagVec), " ; ",
|
||||
fshow(csVec), " ; ",
|
||||
fshow(ownerVec), " ; "
|
||||
);
|
||||
if(cmd matches tagged PRs .rs) begin
|
||||
// PRs directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: rs.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// CRq/PRq: need tag matching
|
||||
Addr addr = getAddrFromCmd(cmd);
|
||||
tagT tag = getTag(addr);
|
||||
// find hit way (nothing is being replaced)
|
||||
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
|
||||
match {.cs, .t} = csTag;
|
||||
return cs > I && t == tag;
|
||||
endfunction
|
||||
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
|
||||
if(hitWay matches tagged Valid .w) begin
|
||||
return TagMatchResult {
|
||||
way: w,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else if(cmd matches tagged PRq .rq) begin
|
||||
// pRq miss
|
||||
return TagMatchResult {
|
||||
way: 0, // default to 0
|
||||
pRqMiss: True
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// find a unlocked way to replace for cRq
|
||||
Vector#(wayNum, Bool) unlocked = ?;
|
||||
Vector#(wayNum, Bool) invalid = ?;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
invalid[i] = csVec[i] == I;
|
||||
unlocked[i] = !isValid(ownerVec[i]);
|
||||
end
|
||||
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
|
||||
// sanity check: repWay must be valid
|
||||
if(!isValid(repWay)) begin
|
||||
$fwrite(stderr, "[L1Pipe] ERROR: ", fshow(cmd), " cannot find way to replace\n");
|
||||
$finish;
|
||||
end
|
||||
return TagMatchResult {
|
||||
way: fromMaybe(?, repWay),
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
end
|
||||
endactionvalue;
|
||||
endfunction
|
||||
|
||||
function ActionValue#(UpdateByUpCs#(Msi)) updateByUpCs(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
|
||||
);
|
||||
actionvalue
|
||||
doAssert(toState > oldCs, "should truly upgrade cs");
|
||||
doAssert(dataV, "self inv L1 always needs data resp");
|
||||
return UpdateByUpCs {cs: toState};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(UpdateByDownDir#(Msi, dirT)) updateByDownDir(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
|
||||
);
|
||||
actionvalue
|
||||
doAssert(False, "L1 should not have cRs");
|
||||
return UpdateByDownDir {cs: oldCs, dir: oldDir};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
|
||||
actionvalue
|
||||
return ?; // random replace does not have bookkeeping
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
CCPipe#(
|
||||
wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT
|
||||
) pipe <- mkCCPipe(
|
||||
regToReadOnly(initDone), getIndex, tagMatch,
|
||||
updateByUpCs, updateByDownDir, updateRepInfo,
|
||||
infoRam, repRam, dataRam
|
||||
);
|
||||
|
||||
// reconcile: wait until pipeline empty and drop all S states. Stall
|
||||
// pipeline enq while we are waiting. Make the reconcile rule conflict with
|
||||
// pipeline deq to remove any possible race (the guard of reconcile rule
|
||||
// actually should have done the job).
|
||||
// Since send method will not fire when needReconcile, we can use a wire to
|
||||
// catch pipeline empty signal to avoid scheduling issue
|
||||
Reg#(Bool) needReconcile <- mkReg(False);
|
||||
|
||||
RWire#(void) conflict_reconcile_deq <- mkRWire;
|
||||
|
||||
PulseWire pipeEmpty <- mkPulseWire;
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule setPipeEmpty(pipe.emptyForFlush);
|
||||
pipeEmpty.send;
|
||||
endrule
|
||||
|
||||
rule doReconcile(initDone && needReconcile && pipeEmpty);
|
||||
function Action flush(CacheInfoArray#(indexT, tagT, ownerT, otherT) ifc);
|
||||
action
|
||||
ifc.reconcile;
|
||||
endaction
|
||||
endfunction
|
||||
joinActions(map(flush, infoArray));
|
||||
// reconcile is done
|
||||
needReconcile <= False;
|
||||
// conflict with deq
|
||||
conflict_reconcile_deq.wset(?);
|
||||
$display("%t L1 %m doReconcile", $time);
|
||||
endrule
|
||||
|
||||
// stall enq for reconcile
|
||||
method Action send(pipeInT req) if(!needReconcile);
|
||||
case(req) matches
|
||||
tagged CRq .rq: begin
|
||||
pipe.enq(CRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged PRq .rq: begin
|
||||
pipe.enq(PRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged PRs .rs: begin
|
||||
pipe.enq(PRs (SelfInvL1PipePRsCmd {
|
||||
addr: rs.addr,
|
||||
way: rs.way
|
||||
}), rs.data, UpCs (rs.toState));
|
||||
end
|
||||
endcase
|
||||
endmethod
|
||||
|
||||
// need to adapt pipeline output to real output format
|
||||
method pipeOutT first;
|
||||
let pout = pipe.first;
|
||||
return PipeOut {
|
||||
cmd: (case(pout.cmd) matches
|
||||
tagged CRq .rq: L1CRq (rq.mshrIdx);
|
||||
tagged PRq .rq: L1PRq (rq.mshrIdx);
|
||||
tagged PRs .rs: L1PRs;
|
||||
default: ?;
|
||||
endcase),
|
||||
way: pout.way,
|
||||
pRqMiss: pout.pRqMiss,
|
||||
ram: pout.ram,
|
||||
repInfo: pout.repInfo
|
||||
};
|
||||
endmethod
|
||||
|
||||
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
|
||||
// get new cmd
|
||||
Maybe#(pipeCmdT) newCmd = Invalid;
|
||||
if(swapRq matches tagged Valid .idx) begin // swap in cRq
|
||||
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
|
||||
newCmd = Valid (CRq (SelfInvL1PipeRqIn {addr: addr, mshrIdx: idx}));
|
||||
end
|
||||
// call pipe
|
||||
pipe.deqWrite(newCmd, wrRam, updateRep);
|
||||
// conflict with reconcile
|
||||
conflict_reconcile_deq.wset(?);
|
||||
endmethod
|
||||
|
||||
method Action reconcile if(!needReconcile);
|
||||
needReconcile <= True;
|
||||
endmethod
|
||||
|
||||
method Bool reconcile_done;
|
||||
return !needReconcile;
|
||||
endmethod
|
||||
endmodule
|
||||
1494
src_Core/RISCY_OOO/coherence/src/SelfInvLLBank.bsv
Normal file
1494
src_Core/RISCY_OOO/coherence/src/SelfInvLLBank.bsv
Normal file
File diff suppressed because it is too large
Load Diff
366
src_Core/RISCY_OOO/coherence/src/SelfInvLLPipe.bsv
Normal file
366
src_Core/RISCY_OOO/coherence/src/SelfInvLLPipe.bsv
Normal file
@@ -0,0 +1,366 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import FShow::*;
|
||||
import Types::*;
|
||||
import CCTypes::*;
|
||||
import CCPipe::*;
|
||||
import RWBramCore::*;
|
||||
import RandomReplace::*;
|
||||
|
||||
// type param ordering: bank < child < way < index < tag < cRq
|
||||
|
||||
// input types
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
cRqIdxT mshrIdx;
|
||||
} SelfInvLLPipeCRqIn#(type cRqIdxT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
Msi toState; // come from req in MSHR (E or M)
|
||||
Line data; // come from memory must be valid
|
||||
wayT way; // come from MSHR
|
||||
} SelfInvLLPipeMRsIn#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvLLPipeCRqIn#(cRqIdxT) CRq;
|
||||
CRsMsg#(childT) CRs;
|
||||
SelfInvLLPipeMRsIn#(wayT) MRs;
|
||||
} SelfInvLLPipeIn#(
|
||||
type childT,
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
// output cmd to the processing rule in LLC
|
||||
typedef union tagged {
|
||||
cRqIdxT LLCRq; // mshr idx of the cRq
|
||||
childT LLCRs; // which child is downgrading
|
||||
void LLMRs;
|
||||
} SelfInvLLCmd#(type childT, type cRqIdxT) deriving (Bits, Eq, FShow);
|
||||
|
||||
// Track only exclusive child in directory
|
||||
typedef struct {
|
||||
childT exChild;
|
||||
Msi state; // I/E/M, we don't track S
|
||||
} SelfInvDir#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface SelfInvLLPipe#(
|
||||
numeric type lgBankNum,
|
||||
numeric type childNum,
|
||||
numeric type wayNum,
|
||||
type indexT,
|
||||
type tagT,
|
||||
type cRqIdxT
|
||||
);
|
||||
method Action send(SelfInvLLPipeIn#(Bit#(TLog#(childNum)), Bit#(TLog#(wayNum)), cRqIdxT) r);
|
||||
method Bool notEmpty;
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))),
|
||||
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
|
||||
Line, SelfInvLLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
|
||||
) first;
|
||||
method PipeOut#(
|
||||
Bit#(TLog#(wayNum)),
|
||||
tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))),
|
||||
Maybe#(CRqOwner#(cRqIdxT)), void, RandRepInfo, // no other
|
||||
Line, SelfInvLLCmd#(Bit#(TLog#(childNum)), cRqIdxT)
|
||||
) unguard_first;
|
||||
method Action deqWrite(
|
||||
Maybe#(cRqIdxT) swapRq,
|
||||
RamData#(tagT, Msi, SelfInvDir#(Bit#(TLog#(childNum))), Maybe#(CRqOwner#(cRqIdxT)), void, Line) wrRam, // always write BRAM
|
||||
Bool updateRep
|
||||
);
|
||||
endinterface
|
||||
|
||||
// real cmd used in pipeline
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
childT child;
|
||||
} SelfInvLLPipeCRsCmd#(type childT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr addr;
|
||||
wayT way;
|
||||
} SelfInvLLPipeMRsCmd#(type wayT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef union tagged {
|
||||
SelfInvLLPipeCRqIn#(cRqIdxT) CRq;
|
||||
SelfInvLLPipeCRsCmd#(childT) CRs;
|
||||
SelfInvLLPipeMRsCmd#(wayT) MRs;
|
||||
} SelfInvLLPipeCmd#(
|
||||
type childT,
|
||||
type wayT,
|
||||
type cRqIdxT
|
||||
) deriving (Bits, Eq, FShow);
|
||||
|
||||
module mkSelfInvLLPipe(
|
||||
SelfInvLLPipe#(lgBankNum, childNum, wayNum, indexT, tagT, cRqIdxT)
|
||||
) provisos(
|
||||
Alias#(childT, Bit#(TLog#(childNum))),
|
||||
Alias#(wayT, Bit#(TLog#(wayNum))),
|
||||
Alias#(dirT, SelfInvDir#(childT)),
|
||||
Alias#(ownerT, Maybe#(CRqOwner#(cRqIdxT))),
|
||||
Alias#(otherT, void), // no other cache info
|
||||
Alias#(repT, RandRepInfo), // use random replace
|
||||
Alias#(pipeInT, SelfInvLLPipeIn#(childT, wayT, cRqIdxT)),
|
||||
Alias#(pipeCmdT, SelfInvLLPipeCmd#(childT, wayT, cRqIdxT)),
|
||||
Alias#(llCmdT, SelfInvLLCmd#(childT, cRqIdxT)),
|
||||
Alias#(pipeOutT, PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, llCmdT)), // output type
|
||||
Alias#(infoT, CacheInfo#(tagT, Msi, dirT, ownerT, otherT)),
|
||||
Alias#(ramDataT, RamData#(tagT, Msi, dirT, ownerT, otherT, Line)),
|
||||
Alias#(respStateT, RespState#(Msi)),
|
||||
Alias#(tagMatchResT, TagMatchResult#(wayT)),
|
||||
Alias#(updateByUpCsT, UpdateByUpCs#(Msi)),
|
||||
Alias#(updateByDownDirT, UpdateByDownDir#(Msi, dirT)),
|
||||
Alias#(dataIndexT, Bit#(TAdd#(TLog#(wayNum), indexSz))),
|
||||
// requirement
|
||||
Alias#(indexT, Bit#(indexSz)),
|
||||
Alias#(tagT, Bit#(tagSz)),
|
||||
Alias#(cRqIdxT, Bit#(_cRqIdxSz)),
|
||||
Add#(indexSz, a__, AddrSz),
|
||||
Add#(tagSz, b__, AddrSz)
|
||||
);
|
||||
// RAMs
|
||||
Vector#(wayNum, RWBramCore#(indexT, infoT)) infoRam <- replicateM(mkRWBramCore);
|
||||
RWBramCore#(indexT, repT) repRam <- mkRandRepRam;
|
||||
RWBramCore#(dataIndexT, Line) dataRam <- mkRWBramCore;
|
||||
|
||||
// initialize RAM
|
||||
Reg#(Bool) initDone <- mkReg(False);
|
||||
Reg#(indexT) initIndex <- mkReg(0);
|
||||
|
||||
rule doInit(!initDone);
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
infoRam[i].wrReq(initIndex, CacheInfo {
|
||||
tag: 0,
|
||||
cs: I,
|
||||
dir: SelfInvDir {exChild: ?, state: I},
|
||||
owner: Invalid,
|
||||
other: ?
|
||||
});
|
||||
end
|
||||
repRam.wrReq(initIndex, randRepInitInfo); // useless for random replace
|
||||
initIndex <= initIndex + 1;
|
||||
if(initIndex == maxBound) begin
|
||||
initDone <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
// random replacement
|
||||
RandomReplace#(wayNum) randRep <- mkRandomReplace;
|
||||
|
||||
// functions
|
||||
function Addr getAddrFromCmd(pipeCmdT cmd);
|
||||
return (case(cmd) matches
|
||||
tagged CRq .r: r.addr;
|
||||
tagged CRs .r: r.addr;
|
||||
tagged MRs .r: r.addr;
|
||||
default: ?;
|
||||
endcase);
|
||||
endfunction
|
||||
|
||||
function indexT getIndex(pipeCmdT cmd);
|
||||
Addr a = getAddrFromCmd(cmd);
|
||||
return truncate(a >> (valueOf(LgLineSzBytes) + valueOf(lgBankNum)));
|
||||
endfunction
|
||||
|
||||
function ActionValue#(tagMatchResT) tagMatch(
|
||||
pipeCmdT cmd,
|
||||
Vector#(wayNum, tagT) tagVec,
|
||||
Vector#(wayNum, Msi) csVec,
|
||||
Vector#(wayNum, ownerT) ownerVec,
|
||||
repT repInfo
|
||||
);
|
||||
return actionvalue
|
||||
function tagT getTag(Addr a) = truncateLSB(a);
|
||||
|
||||
$display("%t LL %m tagMatch: ", $time,
|
||||
fshow(cmd), " ; ",
|
||||
fshow(getTag(getAddrFromCmd(cmd))), " ; ",
|
||||
fshow(tagVec), " ; ",
|
||||
fshow(csVec), " ; ",
|
||||
fshow(ownerVec)
|
||||
);
|
||||
if(cmd matches tagged MRs .rs) begin
|
||||
// MRs directly read from cmd
|
||||
return TagMatchResult {
|
||||
way: rs.way,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// CRq/CRs: need tag matching
|
||||
Addr addr = getAddrFromCmd(cmd);
|
||||
tagT tag = getTag(addr);
|
||||
// find hit way (we do not check replacing bit in LLC)
|
||||
// this makes <cRq a> blocked by other <cRq b> which is replacing addr a
|
||||
function Bool isMatch(Tuple2#(Msi, tagT) csTag);
|
||||
match {.cs, .t} = csTag;
|
||||
return cs > I && t == tag;
|
||||
endfunction
|
||||
Maybe#(wayT) hitWay = searchIndex(isMatch, zip(csVec, tagVec));
|
||||
if(hitWay matches tagged Valid .w) begin
|
||||
return TagMatchResult {
|
||||
way: w,
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
else begin
|
||||
// cRs must hit, so only cRq cannot enter here
|
||||
doAssert(cmd matches tagged CRq ._rq ? True : False,
|
||||
"only cRq can tag match miss"
|
||||
);
|
||||
// find a unlocked way to replace for cRq
|
||||
Vector#(wayNum, Bool) unlocked = ?;
|
||||
Vector#(wayNum, Bool) invalid = ?;
|
||||
for(Integer i = 0; i < valueOf(wayNum); i = i+1) begin
|
||||
invalid[i] = csVec[i] == I;
|
||||
unlocked[i] = !isValid(ownerVec[i]);
|
||||
end
|
||||
Maybe#(wayT) repWay = randRep.getReplaceWay(unlocked, invalid);
|
||||
// sanity check: repWay must be valid
|
||||
doAssert(isValid(repWay), "should always find a way to replace");
|
||||
return TagMatchResult {
|
||||
way: fromMaybe(?, repWay),
|
||||
pRqMiss: False
|
||||
};
|
||||
end
|
||||
end
|
||||
endactionvalue;
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByUpCsT) updateByUpCs(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs
|
||||
);
|
||||
actionvalue
|
||||
doAssert(toState > oldCs, "should truly upgrade cs");
|
||||
doAssert((oldCs == I) && dataV, "LLC mRs always has data");
|
||||
return UpdateByUpCs {cs: toState};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(updateByDownDirT) updateByDownDir(
|
||||
pipeCmdT cmd, Msi toState, Bool dataV, Msi oldCs, dirT oldDir
|
||||
);
|
||||
actionvalue
|
||||
// update dir
|
||||
// The exclusive child is downgraded. Since we don't track S, just make
|
||||
// dir invalid, child field is useless for I
|
||||
dirT newDir = SelfInvDir {exChild: ?, state: I};
|
||||
doAssert(oldDir.state >= E && toState <= S, "no more exclusive child");
|
||||
doAssert(cmd matches tagged CRs .cRs &&& oldDir.exChild == cRs.child ? True : False,
|
||||
"cRs child should match");
|
||||
if(!dataV) begin
|
||||
doAssert(oldDir.state < M, "cRs without data, dir must < M");
|
||||
end
|
||||
if(oldDir.state == M) begin
|
||||
doAssert(dataV, "downgrade from M must have data");
|
||||
doAssert(oldCs == M, "cs must be M");
|
||||
end
|
||||
// update cs
|
||||
// XXX since child can upgrade from E to M silently, use data valid
|
||||
// to determine if we need to upgrade to M. Note that the data
|
||||
// valid field has not been overwritten by bypass in CCPipe.
|
||||
Msi newCs = oldCs;
|
||||
if(dataV) begin
|
||||
doAssert(oldCs >= E, "cRs has data, cs must >= E");
|
||||
newCs = M;
|
||||
end
|
||||
return UpdateByDownDir {cs: newCs, dir: newDir};
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function ActionValue#(repT) updateRepInfo(repT r, wayT w);
|
||||
actionvalue
|
||||
return ?; // random replace does not have bookkeeping
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
CCPipe#(wayNum, indexT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pipe <- mkCCPipe(
|
||||
regToReadOnly(initDone), getIndex, tagMatch,
|
||||
updateByUpCs, updateByDownDir, updateRepInfo,
|
||||
infoRam, repRam, dataRam
|
||||
);
|
||||
|
||||
// get first output from CCPipe output
|
||||
function pipeOutT getFirst(PipeOut#(wayT, tagT, Msi, dirT, ownerT, otherT, repT, Line, pipeCmdT) pout);
|
||||
return PipeOut {
|
||||
cmd: (case(pout.cmd) matches
|
||||
tagged CRq .rq: LLCRq (rq.mshrIdx);
|
||||
tagged CRs .rs: LLCRs (rs.child);
|
||||
tagged MRs .rs: LLMRs;
|
||||
default: ?;
|
||||
endcase),
|
||||
way: pout.way,
|
||||
pRqMiss: pout.pRqMiss,
|
||||
ram: pout.ram,
|
||||
repInfo: pout.repInfo
|
||||
};
|
||||
endfunction
|
||||
|
||||
method Action send(pipeInT req);
|
||||
case(req) matches
|
||||
tagged CRq .rq: begin
|
||||
pipe.enq(CRq (rq), Invalid, Invalid);
|
||||
end
|
||||
tagged CRs .rs: begin
|
||||
pipe.enq(CRs (SelfInvLLPipeCRsCmd {
|
||||
addr: rs.addr,
|
||||
child: rs.child
|
||||
}), rs.data, DownDir (rs.toState));
|
||||
end
|
||||
tagged MRs .rs: begin
|
||||
pipe.enq(MRs (SelfInvLLPipeMRsCmd {
|
||||
addr: rs.addr,
|
||||
way: rs.way
|
||||
}), Valid (rs.data), UpCs (rs.toState));
|
||||
end
|
||||
endcase
|
||||
endmethod
|
||||
|
||||
// need to adapt pipeline output to real output format
|
||||
method pipeOutT first;
|
||||
return getFirst(pipe.first); // guarded version
|
||||
endmethod
|
||||
|
||||
method pipeOutT unguard_first;
|
||||
return getFirst(pipe.unguard_first); // unguarded version
|
||||
endmethod
|
||||
|
||||
method notEmpty = pipe.notEmpty;
|
||||
|
||||
method Action deqWrite(Maybe#(cRqIdxT) swapRq, ramDataT wrRam, Bool updateRep);
|
||||
// get new cmd
|
||||
Addr addr = getAddrFromCmd(pipe.first.cmd); // inherit addr
|
||||
Maybe#(pipeCmdT) newCmd = Invalid;
|
||||
if(swapRq matches tagged Valid .idx) begin
|
||||
newCmd = Valid (CRq (SelfInvLLPipeCRqIn {addr: addr, mshrIdx: idx}));
|
||||
end
|
||||
// call pipe
|
||||
pipe.deqWrite(newCmd, wrRam, updateRep);
|
||||
endmethod
|
||||
endmodule
|
||||
178
src_Core/RISCY_OOO/connectal/bsv/ConnectalBramFifo.bsv
Normal file
178
src_Core/RISCY_OOO/connectal/bsv/ConnectalBramFifo.bsv
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright (c) 2015 Quanta Research Cambridge, Inc.
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Clocks::*;
|
||||
import Vector::*;
|
||||
import FIFO::*;
|
||||
import FIFOF::*;
|
||||
import BRAMFIFO::*;
|
||||
import CBus::*; // extendNP and truncateNP
|
||||
|
||||
`include "ConnectalProjectConfig.bsv"
|
||||
import Arith::*;
|
||||
import ConnectalClocks::*;
|
||||
|
||||
`ifdef SIMULATION
|
||||
`ifdef BOARD_xsim
|
||||
`define USE_XILINX_MACRO
|
||||
`endif // xsim
|
||||
`else // not SIMULATION
|
||||
`ifdef XILINX
|
||||
`define USE_XILINX_MACRO
|
||||
`endif // XILINX
|
||||
`endif // not SIMULATION
|
||||
|
||||
`ifdef USE_XILINX_MACRO
|
||||
(* always_ready, always_enabled *)
|
||||
interface X7FifoSyncMacro#(numeric type data_width);
|
||||
method Bit#(1) empty();
|
||||
method Bit#(1) full();
|
||||
method Action din(Bit#(data_width) v);
|
||||
method Action wren(Bit#(1) wren);
|
||||
method Bit#(data_width) dout();
|
||||
method Action rden(Bit#(1) rden);
|
||||
method Bit#(9) wrcount();
|
||||
method Bit#(9) rdcount();
|
||||
endinterface
|
||||
|
||||
import "BVI" FIFO_DUALCLOCK_MACRO =
|
||||
module vmkBramFifo#(String fifo_size, Clock wrClock, Reset wrReset, Clock rdClock, Reset rdReset)(X7FifoSyncMacro#(data_width));
|
||||
`ifndef BSV_POSITIVE_RESET
|
||||
let rdReset1 <- mkSyncReset(10, rdReset, wrClock);
|
||||
let eitherReset <- mkResetEither(wrReset, rdReset1, clocked_by wrClock);
|
||||
let positiveReset <- mkPositiveReset(10, eitherReset, wrClock);
|
||||
// rst must be asserted for 5 read and write clock cycles
|
||||
let fifoReset = positiveReset.positiveReset;
|
||||
`else
|
||||
let fifoReset = wrReset;
|
||||
`endif
|
||||
`ifdef XilinxUltrascale
|
||||
parameter DEVICE = "ULTRASCALE";
|
||||
`else
|
||||
parameter DEVICE = "7SERIES";
|
||||
`endif
|
||||
parameter DATA_WIDTH = valueOf(data_width);
|
||||
parameter FIFO_SIZE = fifo_size;
|
||||
`ifndef SIMULATION
|
||||
parameter FIRST_WORD_FALL_THROUGH = "TRUE"; // not supported by xsim
|
||||
`else
|
||||
parameter FIRST_WORD_FALL_THROUGH = True;
|
||||
`endif
|
||||
default_clock wrClock(WRCLK) = wrClock;
|
||||
no_reset;
|
||||
// RST is asynchronous
|
||||
input_reset wrReset(RST) clocked_by(no_clock) = fifoReset;
|
||||
input_clock rdClock(RDCLK) = rdClock;
|
||||
method EMPTY empty() clocked_by (rdClock) reset_by (wrReset);
|
||||
method FULL full() clocked_by (wrClock) reset_by (wrReset);
|
||||
method din(DI) enable ((*inhigh*)EN_di) clocked_by (wrClock) reset_by (wrReset);
|
||||
method wren(WREN) enable ((*inhigh*)EN_wren) clocked_by (wrClock) reset_by (wrReset);
|
||||
method DO dout() clocked_by (rdClock) reset_by (wrReset);
|
||||
method rden(RDEN) enable ((*inhigh*)EN_rden) clocked_by (rdClock) reset_by (wrReset);
|
||||
// wrcount and rdcount ports are needed for xsim
|
||||
method WRCOUNT wrcount() clocked_by (wrClock) reset_by (wrReset);
|
||||
method RDCOUNT rdcount() clocked_by (rdClock) reset_by (wrReset);
|
||||
schedule (empty, full, dout, din, wren, rden, wrcount, rdcount) CF (empty, full, dout, din, wren, rden, wrcount, rdcount);
|
||||
endmodule
|
||||
|
||||
module mkDualClockBramFIFOF#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFOF#(t))
|
||||
provisos (Bits#(t,sizet),
|
||||
Add#(1,a__,sizet));
|
||||
String fifo_size = "18Kb";
|
||||
Vector#(TDiv#(sizet,36),X7FifoSyncMacro#(36)) fifos <- replicateM(vmkBramFifo(fifo_size, srcClock, srcReset, dstClock, dstReset));
|
||||
Wire#(Bit#(1)) rdenWire <- mkDWire(0, clocked_by dstClock, reset_by dstReset);
|
||||
Wire#(Bit#(1)) wrenWire <- mkDWire(0, clocked_by srcClock, reset_by srcReset);
|
||||
Vector#(TDiv#(sizet,36),Wire#(Bit#(36))) dinWires <- replicateM(mkDWire(0, clocked_by srcClock, reset_by srcReset));
|
||||
|
||||
for (Integer i = 0; i < valueOf(TDiv#(sizet,36)); i = i+1) begin
|
||||
Reg#(Bit#(9)) rdcount <- mkReg(0, clocked_by dstClock, reset_by dstReset);
|
||||
Reg#(Bit#(9)) wrcount <- mkReg(0, clocked_by srcClock, reset_by srcReset);
|
||||
rule rdenRule;
|
||||
fifos[i].rden(rdenWire);
|
||||
endrule
|
||||
rule wrenRule;
|
||||
fifos[i].wren(wrenWire);
|
||||
endrule
|
||||
rule inputs;
|
||||
fifos[i].din(dinWires[i]);
|
||||
endrule
|
||||
rule countrds;
|
||||
rdcount <= fifos[i].rdcount();
|
||||
endrule
|
||||
rule countwrs;
|
||||
wrcount <= fifos[i].wrcount();
|
||||
endrule
|
||||
end
|
||||
|
||||
function Bool fifoNotEmpty(X7FifoSyncMacro#(36) f); return f.empty == 0; endfunction
|
||||
function Bool fifoNotFull(X7FifoSyncMacro#(36) f); return f.full == 0; endfunction
|
||||
|
||||
method t first() if (all(fifoNotEmpty, fifos));
|
||||
function Bit#(36) fifoFirst(Integer i); return fifos[i].dout(); endfunction
|
||||
Vector#(TDiv#(sizet,36), Bit#(36)) v = genWith(fifoFirst);
|
||||
return unpack(truncateNP(pack(v)));
|
||||
endmethod
|
||||
method Action deq() if (all(fifoNotEmpty, fifos));
|
||||
rdenWire <= 1;
|
||||
endmethod
|
||||
method notEmpty = all(fifoNotEmpty, fifos);
|
||||
method Action enq(t v) if (all(fifoNotFull, fifos));
|
||||
Vector#(TDiv#(sizet,36), Bit#(36)) vs = unpack(extendNP(pack(v)));
|
||||
Vector#(TDiv#(sizet,36), Integer) indices = genVector();
|
||||
function Action fifoEnq(Integer i); action dinWires[i] <= vs[i]; endaction endfunction
|
||||
mapM_(fifoEnq, indices);
|
||||
wrenWire <= 1;
|
||||
endmethod
|
||||
method notFull = all(fifoNotEmpty, fifos);
|
||||
endmodule
|
||||
|
||||
module mkDualClockBramFIFO#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFO#(t))
|
||||
provisos (Bits#(t,sizet),
|
||||
Add#(1,a__,sizet));
|
||||
|
||||
let syncFifo <- mkDualClockBramFIFOF(srcClock, srcReset, dstClock, dstReset);
|
||||
method enq = syncFifo.enq;
|
||||
method deq = syncFifo.deq;
|
||||
method first = syncFifo.first;
|
||||
endmodule
|
||||
|
||||
`else // compatibility mode
|
||||
module mkDualClockBramFIFOF#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFOF#(t))
|
||||
provisos (Bits#(t,sizet),
|
||||
Add#(1,a__,sizet));
|
||||
let syncFifo <- mkSyncFIFO(512, srcClock, srcReset, dstClock);
|
||||
method enq = syncFifo.enq;
|
||||
method deq = syncFifo.deq;
|
||||
method first = syncFifo.first;
|
||||
method notFull = syncFifo.notFull;
|
||||
method notEmpty = syncFifo.notEmpty;
|
||||
endmodule
|
||||
module mkDualClockBramFIFO#(Clock srcClock, Reset srcReset, Clock dstClock, Reset dstReset)(FIFO#(t))
|
||||
provisos (Bits#(t,sizet),
|
||||
Add#(1,a__,sizet));
|
||||
|
||||
let syncFifo <- mkSyncFIFO(512, srcClock, srcReset, dstClock);
|
||||
method enq = syncFifo.enq;
|
||||
method deq = syncFifo.deq;
|
||||
method first = syncFifo.first;
|
||||
endmodule
|
||||
`endif
|
||||
130
src_Core/RISCY_OOO/connectal/bsv/ConnectalClocks.bsv
Normal file
130
src_Core/RISCY_OOO/connectal/bsv/ConnectalClocks.bsv
Normal file
@@ -0,0 +1,130 @@
|
||||
// Copyright (c) 2014 Quanta Research Cambridge, Inc.
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Clocks::*;
|
||||
|
||||
`include "ConnectalProjectConfig.bsv"
|
||||
|
||||
`ifdef PcieClockPeriod
|
||||
Real pcieClockPeriod = `PcieClockPeriod;
|
||||
`endif
|
||||
|
||||
Real mainClockPeriod = `MainClockPeriod;
|
||||
Real derivedClockPeriod =`DerivedClockPeriod;
|
||||
|
||||
(* always_ready, always_enabled *)
|
||||
interface B2C;
|
||||
interface Clock c;
|
||||
interface Reset r;
|
||||
method Action inputclock(Bit#(1) v);
|
||||
method Action inputreset(Bit#(1) v);
|
||||
endinterface
|
||||
import "BVI" CONNECTNET2 =
|
||||
module mkB2C(B2C);
|
||||
default_clock no_clock;
|
||||
default_reset no_reset;
|
||||
output_clock c(OUT1);
|
||||
output_reset r(OUT2);
|
||||
method inputclock(IN1) enable((*inhigh*) en_inputclock) clocked_by(c);
|
||||
method inputreset(IN2) enable((*inhigh*) en_inputreset) clocked_by(c);
|
||||
schedule ( inputclock, inputreset) CF ( inputclock, inputreset);
|
||||
endmodule
|
||||
|
||||
(* always_ready, always_enabled *)
|
||||
interface B2C1;
|
||||
interface Clock c;
|
||||
method Action inputclock(Bit#(1) v);
|
||||
endinterface
|
||||
import "BVI" CONNECTNET =
|
||||
module mkB2C1(B2C1);
|
||||
default_clock clk();
|
||||
default_reset rst();
|
||||
output_clock c(OUT);
|
||||
method inputclock(IN) enable((*inhigh*) en_inputclock);
|
||||
schedule ( inputclock) CF ( inputclock);
|
||||
endmodule
|
||||
|
||||
(* always_ready, always_enabled *)
|
||||
interface C2B;
|
||||
method Bit#(1) o();
|
||||
endinterface
|
||||
import "BVI" CONNECTNET =
|
||||
module mkC2B#(Clock c)(C2B);
|
||||
default_clock clk();
|
||||
default_reset no_reset;
|
||||
//default_reset rst();
|
||||
input_clock ck(IN) = c;
|
||||
method OUT o();
|
||||
schedule ( o) CF ( o);
|
||||
endmodule
|
||||
|
||||
(* always_ready, always_enabled *)
|
||||
interface B2R;
|
||||
interface Reset r;
|
||||
method Action inputreset(Bit#(1) v);
|
||||
endinterface
|
||||
import "BVI" CONNECTNET =
|
||||
module mkB2R(B2R);
|
||||
default_clock clk();
|
||||
default_reset rst();
|
||||
output_reset r(OUT);
|
||||
method inputreset(IN) enable((*inhigh*) en_inputclock);
|
||||
schedule ( inputreset) CF ( inputreset);
|
||||
endmodule
|
||||
|
||||
(* always_ready, always_enabled *)
|
||||
interface R2B;
|
||||
method Bit#(1) o();
|
||||
endinterface
|
||||
import "BVI" CONNECTNET =
|
||||
module mkR2B#(Reset r)(C2B);
|
||||
default_clock no_clock;
|
||||
default_reset no_reset;
|
||||
//default_reset rst();
|
||||
input_reset rst(IN) = r;
|
||||
method OUT o();
|
||||
schedule ( o) CF ( o);
|
||||
endmodule
|
||||
|
||||
interface PositiveReset;
|
||||
interface Reset positiveReset;
|
||||
endinterface
|
||||
|
||||
import "BVI" PositiveReset =
|
||||
module mkPositiveReset#(Integer resetDelay, Reset reset, Clock clock)(PositiveReset);
|
||||
parameter RSTDELAY = resetDelay;
|
||||
default_clock clock(CLK) = clock;
|
||||
default_reset reset(IN_RST) clocked_by (no_clock) = reset;
|
||||
output_reset positiveReset(OUT_RST);
|
||||
endmodule
|
||||
|
||||
interface FpgaReset;
|
||||
interface Reset fpgaReset;
|
||||
endinterface
|
||||
|
||||
import "BVI" FpgaReset =
|
||||
module exposeFpgaReset#(Integer resetDelay, Clock clock)(FpgaReset);
|
||||
parameter RSTDELAY = resetDelay;
|
||||
default_clock clock(CLK) = clock;
|
||||
no_reset;
|
||||
output_reset fpgaReset(OUT_RST);
|
||||
endmodule
|
||||
42
src_Core/RISCY_OOO/connectal/lib/bsv/Arith.bsv
Normal file
42
src_Core/RISCY_OOO/connectal/lib/bsv/Arith.bsv
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
// Copyright (c) 2014 Quanta Research Cambridge, Inc.
|
||||
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
|
||||
function Bool booland(Bool x1, Bool x2); return x1 && x2; endfunction
|
||||
function Bool boolor(Bool x1, Bool x2); return x1 || x2; endfunction
|
||||
|
||||
function Bool eq(a x1, a x2) provisos (Eq#(a)); return x1 == x2; endfunction
|
||||
|
||||
function a add(a x1, a x2) provisos (Arith#(a)); return x1 + x2; endfunction
|
||||
function a mul(a x1, a x2) provisos (Arith#(a)); return x1 * x2; endfunction
|
||||
function Bit#(b) rshift(Bit#(b) x1, Integer i); return x1 >> i; endfunction
|
||||
function Vector#(n, a) vadd(Vector#(n, a) x1, Vector#(n, a) x2) provisos (Arith#(a));
|
||||
return map(uncurry(add), zip(x1, x2));
|
||||
endfunction
|
||||
function Vector#(n, a) vmul(Vector#(n, a) x1, Vector#(n, a) x2) provisos (Arith#(a));
|
||||
return map(uncurry(mul), zip(x1, x2));
|
||||
endfunction
|
||||
function Vector#(n, Bit#(b)) vrshift(Vector#(n, Bit#(b)) x1, Integer i);
|
||||
return map(flip(rshift)(i), x1);
|
||||
endfunction
|
||||
@@ -0,0 +1,15 @@
|
||||
`define ConnectalVersion 15.11.1
|
||||
`define NumberOfMasters 1
|
||||
`define PinType Empty
|
||||
`define PinTypeInclude Misc
|
||||
`define NumberOfUserTiles 1
|
||||
`define SlaveDataBusWidth 32
|
||||
`define SlaveControlAddrWidth 5
|
||||
`define BurstLenSize 10
|
||||
`define project_dir $(DTOP)
|
||||
`define MainClockPeriod 20
|
||||
`define DerivedClockPeriod 10.000000
|
||||
`define BsimHostInterface
|
||||
`define PhysAddrWidth 40
|
||||
`define SIMULATION
|
||||
`define BOARD_bluesim
|
||||
76
src_Core/RISCY_OOO/fpgautils/lib/DramCommon.bsv
Normal file
76
src_Core/RISCY_OOO/fpgautils/lib/DramCommon.bsv
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Assert::*;
|
||||
|
||||
// User interface:
|
||||
// all DRAM use 64B data block
|
||||
// present user with 64-bit address space, but addr should be in terms of 64B
|
||||
typedef 512 DramUserDataSz;
|
||||
typedef TDiv#(DramUserDataSz, 8) DramUserBESz;
|
||||
typedef 58 DramUserAddrSz;
|
||||
|
||||
typedef Bit#(DramUserDataSz) DramUserData;
|
||||
typedef Bit#(DramUserBESz) DramUserBE;
|
||||
typedef Bit#(DramUserAddrSz) DramUserAddr;
|
||||
|
||||
typedef struct { // read/write req
|
||||
DramUserAddr addr;
|
||||
DramUserData data;
|
||||
DramUserBE wrBE; // all 0 means read,
|
||||
// otherwise wrBE[i]=1 means to write byte i
|
||||
} DramUserReq deriving(Bits, Eq, FShow);
|
||||
|
||||
interface DramUser#(
|
||||
// maximum number of in-flight requests (not always used)
|
||||
numeric type maxReadNum,
|
||||
numeric type maxWriteNum,
|
||||
// simulation delay (fully pipelined)
|
||||
numeric type simDelay,
|
||||
// error of dram controller
|
||||
// XXX we should not check address overflow because of wrong-path loads
|
||||
type errT
|
||||
);
|
||||
method Action req(DramUserReq r);
|
||||
method ActionValue#(DramUserData) rdResp; // only read has resp
|
||||
method ActionValue#(errT) err;
|
||||
endinterface
|
||||
|
||||
// Full interface
|
||||
interface DramFull#(
|
||||
numeric type maxReadNum,
|
||||
numeric type maxWriteNum,
|
||||
numeric type simDelay,
|
||||
type errT,
|
||||
// pin type
|
||||
type pinT
|
||||
);
|
||||
interface DramUser#(maxReadNum, maxWriteNum, simDelay, errT) user;
|
||||
interface pinT pins;
|
||||
endinterface
|
||||
|
||||
`ifdef BSIM
|
||||
function Action doAssert(Bool b, String s) = action if(!b) $fdisplay(stderr, "\n%m: ASSERT FAIL!!"); dynamicAssert(b, s); endaction;
|
||||
`else
|
||||
function Action doAssert(Bool b, String s) = noAction;
|
||||
`endif
|
||||
38
src_Core/RISCY_OOO/fpgautils/lib/ResetGuard.bsv
Normal file
38
src_Core/RISCY_OOO/fpgautils/lib/ResetGuard.bsv
Normal file
@@ -0,0 +1,38 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Clocks::*;
|
||||
|
||||
interface ResetGuard;
|
||||
method Bool isReady;
|
||||
endinterface
|
||||
|
||||
import "BVI" reset_guard =
|
||||
module mkResetGuard(ResetGuard);
|
||||
default_clock clk(CLK);
|
||||
default_reset rst(RST);
|
||||
|
||||
method IS_READY isReady();
|
||||
|
||||
schedule (isReady) CF (isReady);
|
||||
endmodule
|
||||
114
src_Core/RISCY_OOO/fpgautils/lib/SyncFifo.bsv
Normal file
114
src_Core/RISCY_OOO/fpgautils/lib/SyncFifo.bsv
Normal file
@@ -0,0 +1,114 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Clocks::*;
|
||||
import FIFOF::*;
|
||||
import Assert::*;
|
||||
import ConnectalBramFifo::*;
|
||||
import BRAMFIFO::*;
|
||||
import XilinxSyncFifo::*;
|
||||
import ResetGuard::*;
|
||||
|
||||
export mkSyncFifo;
|
||||
export mkSyncBramFifo;
|
||||
|
||||
// sync fifo type selec macros: USE_CONNECTAL_BRAM_SYNC_FIFO, USE_BSV_BRAM_SYNC_FIFO, USE_XILINX_SYNC_FIFO
|
||||
|
||||
// we also guard the sync fifo from enq/deq before the reset
|
||||
|
||||
module mkSyncFifo#(
|
||||
Integer depth, Clock srcClk, Reset srcRst, Clock dstClk, Reset dstRst
|
||||
)(SyncFIFOIfc#(t)) provisos(
|
||||
Bits#(t, tW),
|
||||
NumAlias#(w, TMax#(1, tW)) // some sync fifo doesn't support 0-bit data
|
||||
);
|
||||
`ifdef USE_CONNECTAL_BRAM_SYNC_FIFO
|
||||
staticAssert(depth <= 512, "depth of FIFO18 is 512");
|
||||
FIFOF#(Bit#(w)) q <- mkDualClockBramFIFOF(srcClk, srcRst, dstClk, dstRst);
|
||||
|
||||
`elsif USE_BSV_BRAM_SYNC_FIFO
|
||||
SyncFIFOIfc#(Bit#(w)) q <- mkSyncBRAMFIFO(depth, srcClk, srcRst, dstClk, dstRst);
|
||||
|
||||
`elsif USE_XILINX_SYNC_FIFO
|
||||
staticAssert(depth <= 16, "depth of xilinx lut ram FIFO is 16");
|
||||
SyncFIFOIfc#(Bit#(w)) q <- mkXilinxSyncFifo(srcClk, srcRst, dstClk);
|
||||
|
||||
`else
|
||||
SyncFIFOIfc#(Bit#(w)) q <- mkSyncFIFO(depth, srcClk, srcRst, dstClk);
|
||||
|
||||
`endif
|
||||
|
||||
// guard sync fifo operations
|
||||
ResetGuard srcGuard <- mkResetGuard(clocked_by srcClk, reset_by srcRst);
|
||||
ResetGuard dstGuard <- mkResetGuard(clocked_by dstClk, reset_by dstRst);
|
||||
|
||||
method notFull = q.notFull && srcGuard.isReady;
|
||||
method Action enq(t x) if(srcGuard.isReady);
|
||||
q.enq(zeroExtend(pack(x)));
|
||||
endmethod
|
||||
|
||||
method notEmpty = q.notEmpty && dstGuard.isReady;
|
||||
method t first if(dstGuard.isReady);
|
||||
return unpack(truncate(q.first));
|
||||
endmethod
|
||||
method Action deq if(dstGuard.isReady);
|
||||
q.deq;
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
module mkSyncBramFifo#(
|
||||
Integer depth, Clock srcClk, Reset srcRst, Clock dstClk, Reset dstRst
|
||||
)(SyncFIFOIfc#(t)) provisos(
|
||||
Bits#(t, tW),
|
||||
NumAlias#(w, TMax#(1, tW)) // some sync fifo doesn't support 0-bit data
|
||||
);
|
||||
`ifdef USE_CONNECTAL_BRAM_SYNC_FIFO
|
||||
staticAssert(depth <= 512, "depth of FIFO18 is 512");
|
||||
FIFOF#(Bit#(w)) q <- mkDualClockBramFIFOF(srcClk, srcRst, dstClk, dstRst);
|
||||
|
||||
`elsif USE_XILINX_SYNC_FIFO
|
||||
staticAssert(depth <= 512, "depth of xilinx block ram FIFO is 512");
|
||||
SyncFIFOIfc#(Bit#(w)) q <- mkXilinxSyncBramFifo(srcClk, srcRst, dstClk);
|
||||
|
||||
`else
|
||||
SyncFIFOIfc#(Bit#(w)) q <- mkSyncBRAMFIFO(depth, srcClk, srcRst, dstClk, dstRst);
|
||||
|
||||
`endif
|
||||
|
||||
// guard sync fifo operations
|
||||
ResetGuard srcGuard <- mkResetGuard(clocked_by srcClk, reset_by srcRst);
|
||||
ResetGuard dstGuard <- mkResetGuard(clocked_by dstClk, reset_by dstRst);
|
||||
|
||||
method notFull = q.notFull && srcGuard.isReady;
|
||||
method Action enq(t x) if(srcGuard.isReady);
|
||||
q.enq(zeroExtend(pack(x)));
|
||||
endmethod
|
||||
|
||||
method notEmpty = q.notEmpty && dstGuard.isReady;
|
||||
method t first if(dstGuard.isReady);
|
||||
return unpack(truncate(q.first));
|
||||
endmethod
|
||||
method Action deq if(dstGuard.isReady);
|
||||
q.deq;
|
||||
endmethod
|
||||
endmodule
|
||||
51
src_Core/RISCY_OOO/fpgautils/lib/WaitAutoReset.bsv
Normal file
51
src_Core/RISCY_OOO/fpgautils/lib/WaitAutoReset.bsv
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import ResetGuard::*;
|
||||
|
||||
// some xilinx IP automatically reset
|
||||
// we wait several cycles to make sure the reset completes
|
||||
// we also block signals on the IP before reset of current clock domain completes
|
||||
// because such signals may be garbage
|
||||
|
||||
interface WaitAutoReset#(numeric type logCycles);
|
||||
method Bool isReady;
|
||||
endinterface
|
||||
|
||||
module mkWaitAutoReset(WaitAutoReset#(logCycles));
|
||||
Reg#(Bit#(logCycles)) cnt <- mkReg(0);
|
||||
Reg#(Bool) init <- mkReg(False);
|
||||
|
||||
ResetGuard rg <- mkResetGuard;
|
||||
|
||||
rule doInit(!init);
|
||||
cnt <= cnt + 1;
|
||||
if(cnt == maxBound) begin
|
||||
init <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
method Bool isReady;
|
||||
return init && rg.isReady;
|
||||
endmethod
|
||||
endmodule
|
||||
387
src_Core/RISCY_OOO/fpgautils/lib/XilinxFpu.bsv
Normal file
387
src_Core/RISCY_OOO/fpgautils/lib/XilinxFpu.bsv
Normal file
@@ -0,0 +1,387 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import DefaultValue::*;
|
||||
import GetPut::*;
|
||||
import ClientServer::*;
|
||||
import WaitAutoReset::*;
|
||||
import FloatingPoint::*;
|
||||
import FIFO::*;
|
||||
|
||||
export mkXilinxFpFmaIP;
|
||||
export mkXilinxFpDivIP;
|
||||
export mkXilinxFpSqrtIP;
|
||||
export mkXilinxFpFmaSim;
|
||||
export mkXilinxFpDivSim;
|
||||
export mkXilinxFpSqrtSim;
|
||||
export mkXilinxFpFma;
|
||||
export mkXilinxFpDiv;
|
||||
export mkXilinxFpSqrt;
|
||||
|
||||
typedef FloatingPoint::RoundMode FpuRoundMode;
|
||||
typedef FloatingPoint::Exception FpuException;
|
||||
|
||||
export FpuRoundMode(..);
|
||||
export FpuException(..);
|
||||
|
||||
// import Xilinx IP core
|
||||
|
||||
// xilinx raw FMA IP is a * b + c, later we need to adapt to a + b * c
|
||||
interface FpFmaImport;
|
||||
method Action enqA(Bit#(64) a);
|
||||
method Action enqB(Bit#(64) b);
|
||||
method Action enqC(Bit#(64) c);
|
||||
|
||||
method Action deq;
|
||||
method Bit#(64) resp;
|
||||
method Bit#(3) excep;
|
||||
endinterface
|
||||
|
||||
import "BVI" fp_fma =
|
||||
module mkFpFmaImport(FpFmaImport);
|
||||
default_clock clk(aclk, (*unused*) unused_gate);
|
||||
default_reset no_reset;
|
||||
|
||||
method enqA(s_axis_a_tdata) enable(s_axis_a_tvalid) ready(s_axis_a_tready);
|
||||
method enqB(s_axis_b_tdata) enable(s_axis_b_tvalid) ready(s_axis_b_tready);
|
||||
method enqC(s_axis_c_tdata) enable(s_axis_c_tvalid) ready(s_axis_c_tready);
|
||||
|
||||
method deq() enable(m_axis_result_tready) ready(m_axis_result_tvalid);
|
||||
method m_axis_result_tdata resp() ready(m_axis_result_tvalid);
|
||||
method m_axis_result_tuser excep() ready(m_axis_result_tvalid);
|
||||
|
||||
schedule (enqA, enqB, enqC) CF (deq, resp, excep);
|
||||
|
||||
schedule (enqA) CF (enqB, enqC);
|
||||
schedule (enqB) CF (enqC);
|
||||
schedule (enqA) C (enqA);
|
||||
schedule (enqB) C (enqB);
|
||||
schedule (enqC) C (enqC);
|
||||
|
||||
schedule (deq) C (deq);
|
||||
schedule (resp, excep, deq) CF (resp, excep);
|
||||
endmodule
|
||||
|
||||
interface FpDivImport;
|
||||
method Action enqA(Bit#(64) a);
|
||||
method Action enqB(Bit#(64) b);
|
||||
|
||||
method Action deq;
|
||||
method Bit#(64) resp;
|
||||
method Bit#(4) excep;
|
||||
endinterface
|
||||
|
||||
import "BVI" fp_div =
|
||||
module mkFpDivImport(FpDivImport);
|
||||
default_clock clk(aclk, (*unused*) unused_gate);
|
||||
default_reset no_reset;
|
||||
|
||||
method enqA(s_axis_a_tdata) enable(s_axis_a_tvalid) ready(s_axis_a_tready);
|
||||
method enqB(s_axis_b_tdata) enable(s_axis_b_tvalid) ready(s_axis_b_tready);
|
||||
|
||||
method deq() enable(m_axis_result_tready) ready(m_axis_result_tvalid);
|
||||
method m_axis_result_tdata resp() ready(m_axis_result_tvalid);
|
||||
method m_axis_result_tuser excep() ready(m_axis_result_tvalid);
|
||||
|
||||
schedule (enqA, enqB) CF (deq, resp, excep);
|
||||
|
||||
schedule (enqA) CF (enqB);
|
||||
schedule (enqA) C (enqA);
|
||||
schedule (enqB) C (enqB);
|
||||
|
||||
schedule (deq) C (deq);
|
||||
schedule (resp, excep, deq) CF (resp, excep);
|
||||
endmodule
|
||||
|
||||
interface FpSqrtImport;
|
||||
method Action enqA(Bit#(64) a);
|
||||
|
||||
method Action deq;
|
||||
method Bit#(64) resp;
|
||||
method Bit#(1) excep;
|
||||
endinterface
|
||||
|
||||
import "BVI" fp_sqrt =
|
||||
module mkFpSqrtImport(FpSqrtImport);
|
||||
default_clock clk(aclk, (*unused*) unused_gate);
|
||||
default_reset no_reset;
|
||||
|
||||
method enqA(s_axis_a_tdata) enable(s_axis_a_tvalid) ready(s_axis_a_tready);
|
||||
|
||||
method deq() enable(m_axis_result_tready) ready(m_axis_result_tvalid);
|
||||
method m_axis_result_tdata resp() ready(m_axis_result_tvalid);
|
||||
method m_axis_result_tuser excep() ready(m_axis_result_tvalid);
|
||||
|
||||
schedule (enqA) CF (deq, resp, excep);
|
||||
|
||||
schedule (enqA) C (enqA);
|
||||
|
||||
schedule (deq) C (deq);
|
||||
schedule (resp, excep, deq) CF (resp, excep);
|
||||
endmodule
|
||||
|
||||
// adapt the IP core to Bluespec FPU interface
|
||||
(* synthesize *)
|
||||
module mkXilinxFpFmaIP(Server#(
|
||||
Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode),
|
||||
Tuple2#(Double, FpuException)
|
||||
));
|
||||
WaitAutoReset#(8) init <- mkWaitAutoReset;
|
||||
FpFmaImport fpFma <- mkFpFmaImport;
|
||||
|
||||
// xilinx raw ip is a * b + c
|
||||
// what we need to provide is in1 + in2 * in3
|
||||
// so in1 -> c, in2 -> a, in3 -> b
|
||||
|
||||
interface Put request;
|
||||
method Action put(Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode) req) if(init.isReady);
|
||||
let {maybe_in1, in2, in3, rm} = req;
|
||||
Double in1 = fromMaybe(zero(False), maybe_in1);
|
||||
fpFma.enqA(pack(in2));
|
||||
fpFma.enqB(pack(in3));
|
||||
fpFma.enqC(pack(in1));
|
||||
if(rm != Rnd_Nearest_Even) begin
|
||||
$fdisplay(stderr, "[Xlinx FMA] WARNING: unsupported rounding mode ", fshow(rm));
|
||||
end
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface Get response;
|
||||
method ActionValue#(Tuple2#(Double, FpuException)) get if(init.isReady);
|
||||
fpFma.deq;
|
||||
Double val = unpack(fpFma.resp);
|
||||
Bit#(3) exBits = fpFma.excep;
|
||||
FpuException excep = defaultValue;
|
||||
excep.underflow = exBits[0] == 1;
|
||||
excep.overflow = exBits[1] == 1;
|
||||
excep.invalid_op = exBits[2] == 1;
|
||||
return tuple2(val, excep);
|
||||
endmethod
|
||||
endinterface
|
||||
endmodule
|
||||
|
||||
(* synthesize *)
|
||||
module mkXilinxFpDivIP(Server#(
|
||||
Tuple3#(Double, Double, FpuRoundMode),
|
||||
Tuple2#(Double, FpuException)
|
||||
));
|
||||
WaitAutoReset#(8) init <- mkWaitAutoReset;
|
||||
FpDivImport fpDiv <- mkFpDivImport;
|
||||
|
||||
interface Put request;
|
||||
method Action put(Tuple3#(Double, Double, FpuRoundMode) req) if(init.isReady);
|
||||
let {a, b, rm} = req;
|
||||
fpDiv.enqA(pack(a));
|
||||
fpDiv.enqB(pack(b));
|
||||
if(rm != Rnd_Nearest_Even) begin
|
||||
$fdisplay(stderr, "[Xlinx DIV] WARNING: unsupported rounding mode ", fshow(rm));
|
||||
end
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface Get response;
|
||||
method ActionValue#(Tuple2#(Double, FpuException)) get if(init.isReady);
|
||||
fpDiv.deq;
|
||||
Double val = unpack(fpDiv.resp);
|
||||
Bit#(4) exBits = fpDiv.excep;
|
||||
FpuException excep = defaultValue;
|
||||
excep.underflow = exBits[0] == 1;
|
||||
excep.overflow = exBits[1] == 1;
|
||||
excep.invalid_op = exBits[2] == 1;
|
||||
excep.divide_0 = exBits[3] == 1;
|
||||
return tuple2(val, excep);
|
||||
endmethod
|
||||
endinterface
|
||||
endmodule
|
||||
|
||||
(* synthesize *)
|
||||
module mkXilinxFpSqrtIP(Server#(
|
||||
Tuple2#(Double, FpuRoundMode),
|
||||
Tuple2#(Double, FpuException)
|
||||
));
|
||||
WaitAutoReset#(8) init <- mkWaitAutoReset;
|
||||
FpSqrtImport fpSqrt <- mkFpSqrtImport;
|
||||
|
||||
interface Put request;
|
||||
method Action put(Tuple2#(Double, FpuRoundMode) req) if(init.isReady);
|
||||
let {a, rm} = req;
|
||||
fpSqrt.enqA(pack(a));
|
||||
if(rm != Rnd_Nearest_Even) begin
|
||||
$fdisplay(stderr, "[Xlinx SQRT] WARNING: unsupported rounding mode ", fshow(rm));
|
||||
end
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface Get response;
|
||||
method ActionValue#(Tuple2#(Double, FpuException)) get if(init.isReady);
|
||||
fpSqrt.deq;
|
||||
Double val = unpack(fpSqrt.resp);
|
||||
Bit#(1) exBits = fpSqrt.excep;
|
||||
FpuException excep = defaultValue;
|
||||
excep.invalid_op = exBits[0] == 1;
|
||||
return tuple2(val, excep);
|
||||
endmethod
|
||||
endinterface
|
||||
endmodule
|
||||
|
||||
|
||||
// import simulation
|
||||
interface FpFmaSim;
|
||||
method Bit#(64) fma(Bit#(64) a, Bit#(64) b, Bit#(64) c);
|
||||
endinterface
|
||||
|
||||
import "BVI" fp_fma_sim =
|
||||
module mkFpFmaSim(FpFmaSim);
|
||||
default_clock no_clock;
|
||||
default_reset no_reset;
|
||||
|
||||
method RES fma(A, B, C);
|
||||
endmodule
|
||||
|
||||
interface FpDivSim;
|
||||
method Bit#(64) div(Bit#(64) a, Bit#(64) b);
|
||||
endinterface
|
||||
|
||||
import "BVI" fp_div_sim =
|
||||
module mkFpDivSim(FpDivSim);
|
||||
default_clock no_clock;
|
||||
default_reset no_reset;
|
||||
|
||||
method RES div(A, B);
|
||||
endmodule
|
||||
|
||||
interface FpSqrtSim;
|
||||
method Bit#(64) sqrt(Bit#(64) a);
|
||||
endinterface
|
||||
|
||||
import "BVI" fp_sqrt_sim =
|
||||
module mkFpSqrtSim(FpSqrtSim);
|
||||
default_clock no_clock;
|
||||
default_reset no_reset;
|
||||
|
||||
method RES sqrt(A);
|
||||
endmodule
|
||||
|
||||
// adapt the simulation fp ops to Bluespec FPU interface
|
||||
// Xilinx FPU has 2-element buffers inside
|
||||
(* synthesize *)
|
||||
module mkXilinxFpFmaSim(Server#(
|
||||
Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode),
|
||||
Tuple2#(Double, FpuException)
|
||||
));
|
||||
FpFmaSim fpFma <- mkFpFmaSim;
|
||||
FIFO#(Tuple2#(Double, FpuException)) respQ <- mkFIFO;
|
||||
|
||||
// xilinx raw ip is a * b + c
|
||||
// what we need to provide is in1 + in2 * in3
|
||||
// so in1 -> c, in2 -> a, in3 -> b
|
||||
|
||||
interface Put request;
|
||||
method Action put(Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode) req);
|
||||
let {maybe_in1, in2, in3, rm} = req;
|
||||
Double in1 = fromMaybe(zero(False), maybe_in1);
|
||||
Double val = unpack(fpFma.fma(pack(in2), pack(in3), pack(in1)));
|
||||
respQ.enq(tuple2(val, defaultValue));
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface response = toGet(respQ);
|
||||
endmodule
|
||||
|
||||
(* synthesize *)
|
||||
module mkXilinxFpDivSim(Server#(
|
||||
Tuple3#(Double, Double, FpuRoundMode),
|
||||
Tuple2#(Double, FpuException)
|
||||
));
|
||||
FpDivSim fpDiv <- mkFpDivSim;
|
||||
FIFO#(Tuple2#(Double, FpuException)) respQ <- mkFIFO;
|
||||
|
||||
interface Put request;
|
||||
method Action put(Tuple3#(Double, Double, FpuRoundMode) req);
|
||||
let {a, b, rm} = req;
|
||||
Double val = unpack(fpDiv.div(pack(a), pack(b)));
|
||||
respQ.enq(tuple2(val, defaultValue));
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface response = toGet(respQ);
|
||||
endmodule
|
||||
|
||||
(* synthesize *)
|
||||
module mkXilinxFpSqrtSim(Server#(
|
||||
Tuple2#(Double, FpuRoundMode),
|
||||
Tuple2#(Double, FpuException)
|
||||
));
|
||||
FpSqrtSim fpSqrt <- mkFpSqrtSim;
|
||||
FIFO#(Tuple2#(Double, FpuException)) respQ <- mkFIFO;
|
||||
|
||||
interface Put request;
|
||||
method Action put(Tuple2#(Double, FpuRoundMode) req);
|
||||
let {a, rm} = req;
|
||||
Double val = unpack(fpSqrt.sqrt(pack(a)));
|
||||
respQ.enq(tuple2(val, defaultValue));
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface response = toGet(respQ);
|
||||
endmodule
|
||||
|
||||
// wrap simulation and xilinx ip together
|
||||
(* synthesize *)
|
||||
module mkXilinxFpFma(Server#(
|
||||
Tuple4#(Maybe#(Double), Double, Double, FpuRoundMode),
|
||||
Tuple2#(Double, FpuException)
|
||||
));
|
||||
`ifdef BSIM
|
||||
let m <- mkXilinxFpFmaSim;
|
||||
`else
|
||||
let m <- mkXilinxFpFmaIP;
|
||||
`endif
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
(* synthesize *)
|
||||
module mkXilinxFpDiv(Server#(
|
||||
Tuple3#(Double, Double, FpuRoundMode),
|
||||
Tuple2#(Double, FpuException)
|
||||
));
|
||||
`ifdef BSIM
|
||||
let m <- mkXilinxFpDivSim;
|
||||
`else
|
||||
let m <- mkXilinxFpDivIP;
|
||||
`endif
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
(* synthesize *)
|
||||
module mkXilinxFpSqrt(Server#(
|
||||
Tuple2#(Double, FpuRoundMode),
|
||||
Tuple2#(Double, FpuException)
|
||||
));
|
||||
`ifdef BSIM
|
||||
let m <- mkXilinxFpSqrtSim;
|
||||
`else
|
||||
let m <- mkXilinxFpSqrtIP;
|
||||
`endif
|
||||
return m;
|
||||
endmodule
|
||||
203
src_Core/RISCY_OOO/fpgautils/lib/XilinxIntDiv.bsv
Normal file
203
src_Core/RISCY_OOO/fpgautils/lib/XilinxIntDiv.bsv
Normal file
@@ -0,0 +1,203 @@
|
||||
import FIFOF::*;
|
||||
import FIFO::*;
|
||||
|
||||
import WaitAutoReset::*;
|
||||
|
||||
export XilinxIntDiv(..);
|
||||
export mkXilinxIntDiv;
|
||||
|
||||
// import Xilinx IP core for unsigned division
|
||||
|
||||
// axi tuser: user tag + info needed to handle divide by 0 + sign info
|
||||
typedef struct {
|
||||
Bool divByZero;
|
||||
Bit#(64) divByZeroRem; // remainder in case of divide by zero
|
||||
Bool signedDiv; // signed division (so dividend/divisor has been abs)
|
||||
Bit#(1) quotientSign; // sign bit of quotient (in case of signedDiv)
|
||||
Bit#(1) remainderSign; // sign bit of remainder (in case of signedDiv)
|
||||
Bit#(8) tag;
|
||||
} IntDivUser deriving(Bits, Eq, FShow);
|
||||
|
||||
interface IntDivUnsignedImport;
|
||||
method Action enqDividend(Bit#(64) dividend, IntDivUser user);
|
||||
method Action enqDivisor(Bit#(64) divisor);
|
||||
method Action deqResp;
|
||||
method Bool respValid;
|
||||
method Bit#(128) quotient_remainder;
|
||||
method IntDivUser respUser;
|
||||
endinterface
|
||||
|
||||
import "BVI" int_div_unsigned =
|
||||
module mkIntDivUnsignedImport(IntDivUnsignedImport);
|
||||
default_clock clk(aclk, (*unused*) unused_gate);
|
||||
default_reset no_reset;
|
||||
|
||||
method enqDividend(
|
||||
s_axis_dividend_tdata, s_axis_dividend_tuser
|
||||
) enable(s_axis_dividend_tvalid) ready(s_axis_dividend_tready);
|
||||
|
||||
method enqDivisor(
|
||||
s_axis_divisor_tdata
|
||||
) enable(s_axis_divisor_tvalid) ready(s_axis_divisor_tready);
|
||||
|
||||
method deqResp() enable(m_axis_dout_tready) ready(m_axis_dout_tvalid);
|
||||
method m_axis_dout_tvalid respValid;
|
||||
method m_axis_dout_tdata quotient_remainder ready(m_axis_dout_tvalid);
|
||||
method m_axis_dout_tuser respUser ready(m_axis_dout_tvalid);
|
||||
|
||||
schedule (enqDividend) C (enqDividend);
|
||||
schedule (enqDivisor) C (enqDivisor);
|
||||
schedule (deqResp) C (deqResp);
|
||||
schedule (enqDividend) CF (enqDivisor, deqResp);
|
||||
schedule (enqDivisor) CF (deqResp);
|
||||
schedule (
|
||||
respValid,
|
||||
quotient_remainder,
|
||||
respUser
|
||||
) CF (
|
||||
respValid,
|
||||
quotient_remainder,
|
||||
respUser,
|
||||
enqDividend,
|
||||
enqDivisor,
|
||||
deqResp
|
||||
);
|
||||
endmodule
|
||||
|
||||
// simulation
|
||||
module mkIntDivUnsignedSim(IntDivUnsignedImport);
|
||||
FIFO#(Tuple2#(Bit#(64), IntDivUser)) dividendQ <- mkFIFO;
|
||||
FIFO#(Bit#(64)) divisorQ <- mkFIFO;
|
||||
FIFOF#(Tuple2#(Bit#(128), IntDivUser)) respQ <- mkSizedFIFOF(2);
|
||||
|
||||
rule compute;
|
||||
dividendQ.deq;
|
||||
divisorQ.deq;
|
||||
let {dividend, user} = dividendQ.first;
|
||||
let divisor = divisorQ.first;
|
||||
|
||||
UInt#(64) a = unpack(dividend);
|
||||
UInt#(64) b = unpack(divisor);
|
||||
Bit#(64) q = pack(a / b);
|
||||
Bit#(64) r = pack(a % b);
|
||||
respQ.enq(tuple2({q, r}, user));
|
||||
endrule
|
||||
|
||||
method Action enqDividend(Bit#(64) dividend, IntDivUser user);
|
||||
dividendQ.enq(tuple2(dividend, user));
|
||||
endmethod
|
||||
|
||||
method Action enqDivisor(Bit#(64) divisor);
|
||||
divisorQ.enq(divisor);
|
||||
endmethod
|
||||
|
||||
method Action deqResp;
|
||||
respQ.deq;
|
||||
endmethod
|
||||
|
||||
method respValid = respQ.notEmpty;
|
||||
|
||||
method quotient_remainder = tpl_1(respQ.first);
|
||||
|
||||
method respUser = tpl_2(respQ.first);
|
||||
endmodule
|
||||
|
||||
|
||||
// Wrapper for user (add reset guard, check overflow/divided by 0). We cannot
|
||||
// unify two dividers to one, because divider latency may not be a constant.
|
||||
interface XilinxIntDiv#(type tagT);
|
||||
method Action req(Bit#(64) dividend, Bit#(64) divisor, Bool signedDiv, tagT tag);
|
||||
// response
|
||||
method Action deqResp;
|
||||
method Bool respValid;
|
||||
method Bit#(64) quotient;
|
||||
method Bit#(64) remainder;
|
||||
method tagT respTag;
|
||||
endinterface
|
||||
|
||||
module mkXilinxIntDiv(XilinxIntDiv#(tagT)) provisos (
|
||||
Bits#(tagT, tagSz), Add#(tagSz, a__, 8)
|
||||
);
|
||||
`ifdef BSIM
|
||||
IntDivUnsignedImport divIfc <- mkIntDivUnsignedSim;
|
||||
`else
|
||||
IntDivUnsignedImport divIfc <- mkIntDivUnsignedImport;
|
||||
`endif
|
||||
WaitAutoReset#(4) init <- mkWaitAutoReset;
|
||||
|
||||
method Action req(
|
||||
Bit#(64) dividend, Bit#(64) divisor, Bool signedDiv, tagT tag
|
||||
) if(init.isReady);
|
||||
// compute the input ops to div unsigned IP
|
||||
Bit#(1) dividend_sign = truncateLSB(dividend);
|
||||
Bit#(1) divisor_sign = truncateLSB(divisor);
|
||||
Bit#(64) a = dividend;
|
||||
Bit#(64) b = divisor;
|
||||
if(signedDiv) begin
|
||||
if(dividend_sign == 1) begin
|
||||
a = 0 - dividend;
|
||||
end
|
||||
if(divisor_sign == 1) begin
|
||||
b = 0 - divisor;
|
||||
end
|
||||
end
|
||||
// get the user struct (sign/divide by 0)
|
||||
let user = IntDivUser {
|
||||
divByZero: divisor == 0,
|
||||
divByZeroRem: dividend,
|
||||
signedDiv: signedDiv,
|
||||
// quotient negative when dividend and divisor have different signs
|
||||
quotientSign: dividend_sign ^ divisor_sign,
|
||||
// remainder sign follows that of dividend
|
||||
remainderSign: dividend_sign,
|
||||
tag: zeroExtend(pack(tag))
|
||||
};
|
||||
divIfc.enqDividend(a, user);
|
||||
divIfc.enqDivisor(b);
|
||||
endmethod
|
||||
|
||||
// we also put reset guard on deq port to prevent random signals before
|
||||
// reset from dequing or corrupting axi states
|
||||
method Action deqResp if(init.isReady);
|
||||
divIfc.deqResp;
|
||||
endmethod
|
||||
|
||||
method respValid = divIfc.respValid && init.isReady;
|
||||
|
||||
method Bit#(64) quotient if(init.isReady);
|
||||
let user = divIfc.respUser;
|
||||
Bit#(64) q;
|
||||
if(user.divByZero) begin
|
||||
q = maxBound;
|
||||
end
|
||||
else begin
|
||||
q = truncateLSB(divIfc.quotient_remainder);
|
||||
if(user.signedDiv && user.quotientSign == 1) begin
|
||||
q = 0 - q;
|
||||
end
|
||||
// signed overflow is automatically handled
|
||||
end
|
||||
return q;
|
||||
endmethod
|
||||
|
||||
method Bit#(64) remainder if(init.isReady);
|
||||
let user = divIfc.respUser;
|
||||
Bit#(64) r;
|
||||
if(user.divByZero) begin
|
||||
r = user.divByZeroRem;
|
||||
end
|
||||
else begin
|
||||
r = truncate(divIfc.quotient_remainder);
|
||||
if(user.signedDiv && user.remainderSign == 1) begin
|
||||
r = 0 - r;
|
||||
end
|
||||
// signed overflow is automatically handled
|
||||
end
|
||||
return r;
|
||||
endmethod
|
||||
|
||||
method tagT respTag if(init.isReady);
|
||||
return unpack(truncate(divIfc.respUser.tag));
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
205
src_Core/RISCY_OOO/fpgautils/lib/XilinxIntMul.bsv
Normal file
205
src_Core/RISCY_OOO/fpgautils/lib/XilinxIntMul.bsv
Normal file
@@ -0,0 +1,205 @@
|
||||
import Vector::*;
|
||||
import FIFOF::*;
|
||||
import Assert::*;
|
||||
|
||||
export XilinxIntMulSign(..);
|
||||
export XilinxIntMul(..);
|
||||
export mkXilinxIntMul;
|
||||
|
||||
// Xilinx int multiplier IP is a rigorous pipeline with a fixed latency. There
|
||||
// is no back pressure in the raw IP. We will wrap it with flow control. To do
|
||||
// this, we need to know the pipeline latency from macro
|
||||
// XILINX_INT_MUL_LATENCY.
|
||||
|
||||
typedef `XILINX_INT_MUL_LATENCY IntMulLatency;
|
||||
|
||||
interface IntMulImport;
|
||||
method Action req(Bit#(64) a, Bit#(64) b);
|
||||
method Bit#(128) product;
|
||||
endinterface
|
||||
|
||||
import "BVI" int_mul_signed =
|
||||
module mkIntMulSignedImport(IntMulImport);
|
||||
default_clock clk(CLK, (*unused*) unused_gate);
|
||||
default_reset no_reset;
|
||||
|
||||
method req(A, B) enable((*inhigh*) EN);
|
||||
method P product;
|
||||
|
||||
schedule (req) C (req);
|
||||
schedule (product) CF (req, product);
|
||||
endmodule
|
||||
|
||||
import "BVI" int_mul_unsigned =
|
||||
module mkIntMulUnsignedImport(IntMulImport);
|
||||
default_clock clk(CLK, (*unused*) unused_gate);
|
||||
default_reset no_reset;
|
||||
|
||||
method req(A, B) enable((*inhigh*) EN);
|
||||
method P product;
|
||||
|
||||
schedule (req) C (req);
|
||||
schedule (product) CF (req, product);
|
||||
endmodule
|
||||
|
||||
import "BVI" int_mul_signed_unsigned =
|
||||
module mkIntMulSignedUnsignedImport(IntMulImport);
|
||||
default_clock clk(CLK, (*unused*) unused_gate);
|
||||
default_reset no_reset;
|
||||
|
||||
method req(A, B) enable((*inhigh*) EN);
|
||||
method P product;
|
||||
|
||||
schedule (req) C (req);
|
||||
schedule (product) CF (req, product);
|
||||
endmodule
|
||||
|
||||
// mul req type
|
||||
typedef enum {
|
||||
Signed,
|
||||
Unsigned,
|
||||
SignedUnsigned
|
||||
} XilinxIntMulSign deriving(Bits, Eq, FShow);
|
||||
|
||||
// simulation
|
||||
module mkIntMulSim#(XilinxIntMulSign sign)(IntMulImport);
|
||||
RWire#(Bit#(128)) newReq <- mkRWire;
|
||||
Vector#(
|
||||
IntMulLatency, Reg#(Maybe#(Bit#(128)))
|
||||
) pipe <- replicateM(mkReg(Invalid));
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule canon;
|
||||
for(Integer i = 1; i < valueof(IntMulLatency); i = i+1) begin
|
||||
pipe[i] <= pipe[i - 1];
|
||||
end
|
||||
pipe[0] <= newReq.wget;
|
||||
endrule
|
||||
|
||||
method Action req(Bit#(64) a, Bit#(64) b);
|
||||
Int#(128) op1 = (case(sign)
|
||||
Signed, SignedUnsigned: (unpack(signExtend(a)));
|
||||
default: (unpack(zeroExtend(a)));
|
||||
endcase);
|
||||
Int#(128) op2 = (case(sign)
|
||||
Signed: (unpack(signExtend(b)));
|
||||
default: (unpack(zeroExtend(b)));
|
||||
endcase);
|
||||
Int#(128) prod = op1 * op2;
|
||||
newReq.wset(pack(prod));
|
||||
endmethod
|
||||
|
||||
method Bit#(128) product = fromMaybe(?, pipe[valueof(IntMulLatency) - 1]);
|
||||
endmodule
|
||||
|
||||
// wrap up all mul IPs to have back pressure
|
||||
interface XilinxIntMul#(type tagT);
|
||||
method Action req(Bit#(64) a, Bit#(64) b, XilinxIntMulSign sign, tagT tag);
|
||||
method Action deqResp;
|
||||
method Bool respValid;
|
||||
method Bit#(128) product;
|
||||
method tagT respTag;
|
||||
endinterface
|
||||
|
||||
module mkXilinxIntMul(XilinxIntMul#(tagT)) provisos(
|
||||
Bits#(tagT, tagSz),
|
||||
// credit based flow control types
|
||||
NumAlias#(TAdd#(IntMulLatency, 1), maxCredit),
|
||||
Alias#(Bit#(TLog#(TAdd#(maxCredit, 1))), creditT)
|
||||
);
|
||||
// different multilpliers: WaitAutoReset is not needed, since mul is a
|
||||
// pipeline with fixed latency
|
||||
`ifdef BSIM
|
||||
IntMulImport mulSigned <- mkIntMulSim(Signed);
|
||||
IntMulImport mulUnsigned <- mkIntMulSim(Unsigned);
|
||||
IntMulImport mulSignedUnsigned <- mkIntMulSim(SignedUnsigned);
|
||||
`else
|
||||
IntMulImport mulSigned <- mkIntMulSignedImport;
|
||||
IntMulImport mulUnsigned <- mkIntMulUnsignedImport;
|
||||
IntMulImport mulSignedUnsigned <- mkIntMulSignedUnsignedImport;
|
||||
`endif
|
||||
|
||||
// resp FIFO (unguarded) & flow ctrl credit
|
||||
FIFOF#(
|
||||
Tuple2#(Bit#(128), tagT)
|
||||
) respQ <- mkUGSizedFIFOF(valueof(maxCredit));
|
||||
Reg#(creditT) credit <- mkReg(fromInteger(valueof(maxCredit)));
|
||||
|
||||
// shift regs for sign + tag
|
||||
Vector#(
|
||||
IntMulLatency,
|
||||
Reg#(Maybe#(Tuple2#(XilinxIntMulSign, tagT)))
|
||||
) pipe <- replicateM(mkReg(Invalid));
|
||||
|
||||
// wire to catch input req
|
||||
RWire#(Tuple2#(XilinxIntMulSign, tagT)) newReq <- mkRWire;
|
||||
|
||||
// wire to catch deq
|
||||
PulseWire deqEn <- mkPulseWire;
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule canon;
|
||||
creditT nextCredit = credit;
|
||||
// incr credit if resp FIFO is deq
|
||||
if(deqEn) begin
|
||||
if(nextCredit >= fromInteger(valueof(maxCredit))) begin
|
||||
$fdisplay(stderr, "\n%m: ASSERT FAIL!!");
|
||||
dynamicAssert(False, "credit overflow");
|
||||
end
|
||||
nextCredit = nextCredit + 1;
|
||||
end
|
||||
// enq resp FIFO if something is outputed from mul
|
||||
if (
|
||||
pipe[valueof(IntMulLatency) - 1] matches tagged Valid {.sign, .tag}
|
||||
) begin
|
||||
Bit#(128) prod = (case(sign)
|
||||
Signed: (mulSigned.product);
|
||||
Unsigned: (mulUnsigned.product);
|
||||
SignedUnsigned: (mulSignedUnsigned.product);
|
||||
default: (?);
|
||||
endcase);
|
||||
respQ.enq(tuple2(prod, tag));
|
||||
end
|
||||
// shift pipe regs
|
||||
for(Integer i = 1; i < valueof(IntMulLatency); i = i+1) begin
|
||||
pipe[i] <= pipe[i - 1];
|
||||
end
|
||||
pipe[0] <= newReq.wget;
|
||||
// decr credit if new req is taken
|
||||
if(isValid(newReq.wget)) begin
|
||||
if(nextCredit == 0) begin
|
||||
$fdisplay(stderr, "\n%m: ASSERT FAIL!!");
|
||||
dynamicAssert(False, "credit underflow");
|
||||
end
|
||||
nextCredit = nextCredit - 1;
|
||||
end
|
||||
// update credit
|
||||
credit <= nextCredit;
|
||||
endrule
|
||||
|
||||
method Action req(Bit#(64) a, Bit#(64) b,
|
||||
XilinxIntMulSign sign, tagT tag) if(credit > 0);
|
||||
case(sign)
|
||||
Signed: mulSigned.req(a, b);
|
||||
Unsigned: mulUnsigned.req(a, b);
|
||||
SignedUnsigned: mulSignedUnsigned.req(a, b);
|
||||
endcase
|
||||
newReq.wset(tuple2(sign, tag)); // notify new req
|
||||
endmethod
|
||||
|
||||
method Action deqResp if(respQ.notEmpty);
|
||||
respQ.deq;
|
||||
deqEn.send; // notify deq resp
|
||||
endmethod
|
||||
|
||||
method respValid = respQ.notEmpty;
|
||||
|
||||
method Bit#(128) product if(respQ.notEmpty);
|
||||
return tpl_1(respQ.first);
|
||||
endmethod
|
||||
|
||||
method tagT respTag if(respQ.notEmpty);
|
||||
return tpl_2(respQ.first);
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
181
src_Core/RISCY_OOO/fpgautils/lib/XilinxSyncFifo.bsv
Normal file
181
src_Core/RISCY_OOO/fpgautils/lib/XilinxSyncFifo.bsv
Normal file
@@ -0,0 +1,181 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Vector::*;
|
||||
import Clocks::*;
|
||||
import CBus::*;
|
||||
|
||||
export mkXilinxSyncFifo;
|
||||
export mkXilinxSyncBramFifo;
|
||||
|
||||
// import sync fifos from Xilinx FIFO generator
|
||||
interface SyncFifoImport#(numeric type w);
|
||||
method Bool full;
|
||||
method Action enq(Bit#(w) x);
|
||||
method Bool empty;
|
||||
method Bit#(w) first;
|
||||
method Action deq;
|
||||
endinterface
|
||||
|
||||
import "BVI" sync_fifo_w32_d16 =
|
||||
module mkSyncFifoImport_w32_d16#(Clock srcClk, Clock dstClk)(SyncFifoImport#(32));
|
||||
default_clock no_clock;
|
||||
default_reset no_reset;
|
||||
input_clock (wr_clk) = srcClk;
|
||||
input_clock (rd_clk) = dstClk;
|
||||
|
||||
method full full() clocked_by(srcClk) reset_by(no_reset);
|
||||
method enq(din) enable(wr_en) clocked_by(srcClk) reset_by(no_reset);
|
||||
method empty empty() clocked_by(dstClk) reset_by(no_reset);
|
||||
method dout first() clocked_by(dstClk) reset_by(no_reset);
|
||||
method deq() enable(rd_en) clocked_by(dstClk) reset_by(no_reset);
|
||||
|
||||
schedule (full, enq) CF (empty, first, deq);
|
||||
schedule (full) CF (full, enq);
|
||||
schedule (enq) C (enq);
|
||||
schedule (empty, first) CF (empty, first, deq);
|
||||
schedule (deq) C (deq);
|
||||
endmodule
|
||||
|
||||
import "BVI" sync_bram_fifo_w36_d512 =
|
||||
module mkSyncBramFifoImport_w36_d512#(Clock srcClk, Clock dstClk)(SyncFifoImport#(36));
|
||||
default_clock no_clock;
|
||||
default_reset no_reset;
|
||||
input_clock (wr_clk) = srcClk;
|
||||
input_clock (rd_clk) = dstClk;
|
||||
|
||||
method full full() clocked_by(srcClk) reset_by(no_reset);
|
||||
method enq(din) enable(wr_en) clocked_by(srcClk) reset_by(no_reset);
|
||||
method empty empty() clocked_by(dstClk) reset_by(no_reset);
|
||||
method dout first() clocked_by(dstClk) reset_by(no_reset);
|
||||
method deq() enable(rd_en) clocked_by(dstClk) reset_by(no_reset);
|
||||
|
||||
schedule (full, enq) CF (empty, first, deq);
|
||||
schedule (full) CF (full, enq);
|
||||
schedule (enq) C (enq);
|
||||
schedule (empty, first) CF (empty, first, deq);
|
||||
schedule (deq) C (deq);
|
||||
endmodule
|
||||
|
||||
// wrap up imported BSV or simulation module
|
||||
(* synthesize, no_default_clock, no_default_reset *)
|
||||
module mkSyncFifo_w32_d16#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(Bit#(w))) provisos (NumAlias#(w, 32));
|
||||
`ifdef BSIM
|
||||
SyncFIFOIfc#(Bit#(w)) q <- mkSyncFIFO(16, srcClk, srcRst, dstClk);
|
||||
return q;
|
||||
`else
|
||||
SyncFifoImport#(w) q <- mkSyncFifoImport_w32_d16(srcClk, dstClk);
|
||||
|
||||
method notFull = !q.full;
|
||||
method Action enq(Bit#(w) x) if(!q.full);
|
||||
q.enq(x);
|
||||
endmethod
|
||||
method notEmpty = !q.empty;
|
||||
method Bit#(w) first if(!q.empty);
|
||||
return q.first;
|
||||
endmethod
|
||||
method Action deq if(!q.empty);
|
||||
q.deq;
|
||||
endmethod
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
(* synthesize, no_default_clock, no_default_reset *)
|
||||
module mkSyncBramFifo_w36_d512#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(Bit#(w))) provisos (NumAlias#(w, 36));
|
||||
`ifdef BSIM
|
||||
SyncFIFOIfc#(Bit#(w)) q <- mkSyncFIFO(512, srcClk, srcRst, dstClk);
|
||||
return q;
|
||||
`else
|
||||
SyncFifoImport#(w) q <- mkSyncBramFifoImport_w36_d512(srcClk, dstClk);
|
||||
|
||||
method notFull = !q.full;
|
||||
method Action enq(Bit#(w) x) if(!q.full);
|
||||
q.enq(x);
|
||||
endmethod
|
||||
method notEmpty = !q.empty;
|
||||
method Bit#(w) first if(!q.empty);
|
||||
return q.first;
|
||||
endmethod
|
||||
method Action deq if(!q.empty);
|
||||
q.deq;
|
||||
endmethod
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
// generate parameterized-width fifo from fixed-with fifo
|
||||
module mkGenXilinxSyncFifo#(
|
||||
function module#(SyncFIFOIfc#(Bit#(fifoW))) mkfifo(Clock srcClk, Reset srcRst, Clock dstClk),
|
||||
Clock srcClk, Reset srcRst, Clock dstClk
|
||||
)(SyncFIFOIfc#(t)) provisos(
|
||||
Bits#(t, dataW),
|
||||
Add#(1, a__, dataW),
|
||||
NumAlias#(fifoNum, TDiv#(dataW, fifoW))
|
||||
);
|
||||
Vector#(fifoNum, SyncFIFOIfc#(Bit#(fifoW))) fifos <- replicateM(mkfifo(srcClk, srcRst, dstClk));
|
||||
|
||||
function Bool getNotFull(SyncFIFOIfc#(Bit#(fifoW)) ifc) = ifc.notFull;
|
||||
Bool isNotFull = all(getNotFull, fifos);
|
||||
|
||||
function Bool getNotEmpty(SyncFIFOIfc#(Bit#(fifoW)) ifc) = ifc.notEmpty;
|
||||
Bool isNotEmpty = all(getNotEmpty, fifos);
|
||||
|
||||
method notFull = isNotFull;
|
||||
|
||||
method Action enq(t x);
|
||||
Vector#(fifoNum, Bit#(fifoW)) data = unpack(zeroExtendNP(pack(x)));
|
||||
for(Integer i = 0; i < valueof(fifoNum); i = i+1) begin
|
||||
fifos[i].enq(data[i]);
|
||||
end
|
||||
endmethod
|
||||
|
||||
method notEmpty = isNotEmpty;
|
||||
|
||||
method t first;
|
||||
Vector#(fifoNum, Bit#(fifoW)) data = ?;
|
||||
for(Integer i = 0; i < valueof(fifoNum); i = i+1) begin
|
||||
data[i] = fifos[i].first;
|
||||
end
|
||||
return unpack(truncateNP(pack(data)));
|
||||
endmethod
|
||||
|
||||
method Action deq;
|
||||
for(Integer i = 0; i < valueof(fifoNum); i = i+1) begin
|
||||
fifos[i].deq;
|
||||
end
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
module mkXilinxSyncFifo#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(t)) provisos(
|
||||
Bits#(t, dataW), Add#(1, a__, dataW)
|
||||
);
|
||||
SyncFIFOIfc#(t) m <- mkGenXilinxSyncFifo(mkSyncFifo_w32_d16, srcClk, srcRst, dstClk);
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
module mkXilinxSyncBramFifo#(Clock srcClk, Reset srcRst, Clock dstClk)(SyncFIFOIfc#(t)) provisos(
|
||||
Bits#(t, dataW), Add#(1, a__, dataW)
|
||||
);
|
||||
SyncFIFOIfc#(t) m <- mkGenXilinxSyncFifo(mkSyncBramFifo_w36_d512, srcClk, srcRst, dstClk);
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
8
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_div_sim.v
Normal file
8
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_div_sim.v
Normal file
@@ -0,0 +1,8 @@
|
||||
|
||||
module fp_div_sim(
|
||||
input [63:0] A,
|
||||
input [63:0] B,
|
||||
output [63:0] RES
|
||||
);
|
||||
assign RES = $realtobits($bitstoreal(A) / $bitstoreal(B));
|
||||
endmodule
|
||||
9
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_fma_sim.v
Normal file
9
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_fma_sim.v
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
module fp_fma_sim(
|
||||
input [63:0] A,
|
||||
input [63:0] B,
|
||||
input [63:0] C,
|
||||
output [63:0] RES
|
||||
);
|
||||
assign RES = $realtobits($bitstoreal(A) * $bitstoreal(B) + $bitstoreal(C));
|
||||
endmodule
|
||||
7
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_sqrt_sim.v
Normal file
7
src_Core/RISCY_OOO/fpgautils/xilinx/fpu/fp_sqrt_sim.v
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
module fp_sqrt_sim(
|
||||
input [63:0] A,
|
||||
output [63:0] RES
|
||||
);
|
||||
assign RES = $realtobits($sqrt($bitstoreal(A)));
|
||||
endmodule
|
||||
69
src_Core/RISCY_OOO/fpgautils/xilinx/reset_regs/reset_guard.v
Normal file
69
src_Core/RISCY_OOO/fpgautils/xilinx/reset_regs/reset_guard.v
Normal file
@@ -0,0 +1,69 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// This module outputs a 1-bit signal
|
||||
// The signal is initially 0 after programming the FPGA
|
||||
// XXX: everything should be inited to 0 after programming
|
||||
|
||||
// When a reset arrives, the module starts counting
|
||||
// In N cycles after the reset, the signal becomes 1
|
||||
// This signal can be used as a guard for sync fifo operations
|
||||
|
||||
`ifdef BSV_POSITIVE_RESET
|
||||
`define BSV_RESET_VALUE 1'b1
|
||||
`define BSV_RESET_EDGE posedge
|
||||
`else
|
||||
`define BSV_RESET_VALUE 1'b0
|
||||
`define BSV_RESET_EDGE negedge
|
||||
`endif
|
||||
|
||||
module reset_guard(
|
||||
input CLK,
|
||||
input RST,
|
||||
output IS_READY
|
||||
);
|
||||
reg ready = 0;
|
||||
reg rst_done = 0;
|
||||
|
||||
always@(posedge CLK) begin
|
||||
if(RST == `BSV_RESET_VALUE) begin
|
||||
ready <= 0;
|
||||
rst_done <= 1;
|
||||
// synopsys translate_off
|
||||
if(!rst_done) begin
|
||||
$display("[reset_guard] %t %m reset happen", $time);
|
||||
end
|
||||
// synopsys translate_on
|
||||
end
|
||||
else if(rst_done) begin
|
||||
ready <= 1;
|
||||
// synopsys translate_off
|
||||
if(!ready) begin
|
||||
$display("[reset_guard] %t %m guard ready", $time);
|
||||
end
|
||||
// synopsys translate_on
|
||||
end
|
||||
end
|
||||
|
||||
assign IS_READY = ready;
|
||||
endmodule
|
||||
382
src_Core/RISCY_OOO/procs/RV64G_OOO/AluExePipeline.bsv
Normal file
382
src_Core/RISCY_OOO/procs/RV64G_OOO/AluExePipeline.bsv
Normal file
@@ -0,0 +1,382 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import Vector::*;
|
||||
import FIFO::*;
|
||||
import GetPut::*;
|
||||
import BuildVector::*;
|
||||
import Cntrs::*;
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import SynthParam::*;
|
||||
import Exec::*;
|
||||
import Performance::*;
|
||||
import BrPred::*;
|
||||
import DirPredictor::*;
|
||||
import ReservationStationEhr::*;
|
||||
import ReservationStationAlu::*;
|
||||
import ReorderBuffer::*;
|
||||
import SpecFifo::*;
|
||||
import HasSpecBits::*;
|
||||
import Bypass::*;
|
||||
|
||||
// ALU pipeline has 4 stages
|
||||
// dispatch -> reg read -> exe -> finish (write reg)
|
||||
// bypass is sent out from the end of exe stage
|
||||
// and reg read stage will recv bypass
|
||||
|
||||
typedef struct {
|
||||
// inst info
|
||||
DecodedInst dInst;
|
||||
PhyRegs regs;
|
||||
InstTag tag;
|
||||
DirPredTrainInfo dpTrain;
|
||||
// specualtion
|
||||
Maybe#(SpecTag) spec_tag;
|
||||
} AluDispatchToRegRead deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
// inst info
|
||||
DecodedInst dInst;
|
||||
Maybe#(PhyDst) dst;
|
||||
InstTag tag;
|
||||
DirPredTrainInfo dpTrain;
|
||||
// src reg vals & pc & ppc
|
||||
Data rVal1;
|
||||
Data rVal2;
|
||||
Addr pc;
|
||||
Addr ppc;
|
||||
// specualtion
|
||||
Maybe#(SpecTag) spec_tag;
|
||||
} AluRegReadToExe deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
// inst info
|
||||
IType iType;
|
||||
Maybe#(PhyDst) dst;
|
||||
InstTag tag;
|
||||
DirPredTrainInfo dpTrain;
|
||||
// result
|
||||
Data data; // alu compute result
|
||||
Maybe#(Data) csrData; // data to write CSR file
|
||||
ControlFlow controlFlow;
|
||||
// speculation
|
||||
Maybe#(SpecTag) spec_tag;
|
||||
} AluExeToFinish deriving(Bits, Eq, FShow);
|
||||
|
||||
// XXX currently ALU/Br should not have any exception, so we don't have cause feild above
|
||||
// TODO FIXME In future, if branch target is unaligned to 4 bytes, we may have exception
|
||||
// and probably JR/JAL should NOT write dst reg when exception happens
|
||||
// However, currently JR/JAL renaming is included in the previous checkpoint
|
||||
// so we need to explicitly check exception and don't write reg
|
||||
|
||||
// synthesized pipeline fifos
|
||||
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, AluDispatchToRegRead) AluDispToRegFifo;
|
||||
(* synthesize *)
|
||||
module mkAluDispToRegFifo(AluDispToRegFifo);
|
||||
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
// this one must be 1 elem FIFO, otherwise we cannot get correct bypass
|
||||
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, AluRegReadToExe) AluRegToExeFifo;
|
||||
(* synthesize *)
|
||||
module mkAluRegToExeFifo(AluRegToExeFifo);
|
||||
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
// this one must be 1 elem FIFO, otherwise we cannot get correct bypass
|
||||
typedef SpecFifo_SB_deq_enq_SB_deq_wrong_C_enq#(1, AluExeToFinish) AluExeToFinFifo;
|
||||
(* synthesize *)
|
||||
module mkAluExeToFinFifo(AluExeToFinFifo);
|
||||
let m <- mkSpecFifo_SB_deq_enq_SB_deq_wrong_C_enq(False);
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr nextPc;
|
||||
IType iType;
|
||||
Bool taken;
|
||||
DirPredTrainInfo dpTrain;
|
||||
Bool mispred;
|
||||
} FetchTrainBP deriving(Bits, Eq, FShow);
|
||||
|
||||
interface AluExeInput;
|
||||
// conservative scoreboard check in reg read stage
|
||||
method RegsReady sbCons_lazyLookup(PhyRegs r);
|
||||
// Phys reg file
|
||||
method Data rf_rd1(PhyRIndx rindx);
|
||||
method Data rf_rd2(PhyRIndx rindx);
|
||||
// CSR file
|
||||
method Data csrf_rd(CSR csr);
|
||||
// ROB
|
||||
method Addr rob_getPC(InstTag t);
|
||||
method Addr rob_getPredPC(InstTag t);
|
||||
method Action rob_setExecuted(InstTag t, Maybe#(Data) csrData, ControlFlow cf);
|
||||
// Fetch stage
|
||||
method Action fetch_train_predictors(FetchTrainBP train);
|
||||
|
||||
// global broadcast methods
|
||||
// set aggressive sb & wake up inst in RS
|
||||
method Action setRegReadyAggr(PhyRIndx dst);
|
||||
// send bypass from exe and finish stage
|
||||
interface Vector#(2, SendBypass) sendBypass;
|
||||
// write reg file & set conservative sb
|
||||
method Action writeRegFile(PhyRIndx dst, Data data);
|
||||
// redirect
|
||||
method Action redirect(Addr new_pc, SpecTag spec_tag, InstTag inst_tag);
|
||||
// spec update
|
||||
method Action correctSpec(SpecTag t);
|
||||
|
||||
// performance
|
||||
method Bool doStats;
|
||||
endinterface
|
||||
|
||||
interface AluExePipeline;
|
||||
// recv bypass from exe and finish stages of each ALU pipeline
|
||||
interface Vector#(TMul#(2, AluExeNum), RecvBypass) recvBypass;
|
||||
interface ReservationStationAlu rsAluIfc;
|
||||
interface SpeculationUpdate specUpdate;
|
||||
method Data getPerf(ExeStagePerfType t);
|
||||
endinterface
|
||||
|
||||
module mkAluExePipeline#(AluExeInput inIfc)(AluExePipeline);
|
||||
Bool verbose = True;
|
||||
|
||||
// alu reservation station
|
||||
ReservationStationAlu rsAlu <- mkReservationStationAlu;
|
||||
// pipeline fifos
|
||||
let dispToRegQ <- mkAluDispToRegFifo;
|
||||
let regToExeQ <- mkAluRegToExeFifo;
|
||||
let exeToFinQ <- mkAluExeToFinFifo;
|
||||
// wire to recv bypass
|
||||
Vector#(TMul#(2, AluExeNum), RWire#(Tuple2#(PhyRIndx, Data))) bypassWire <- replicateM(mkRWire);
|
||||
// index to send bypass, ordering doesn't matter
|
||||
Integer exeSendBypassPort = 0;
|
||||
Integer finishSendBypassPort = 1;
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
// performance counters
|
||||
Count#(Data) exeRedirectBrCnt <- mkCount(0);
|
||||
Count#(Data) exeRedirectJrCnt <- mkCount(0);
|
||||
Count#(Data) exeRedirectOtherCnt <- mkCount(0);
|
||||
`endif
|
||||
|
||||
rule doDispatchAlu;
|
||||
rsAlu.doDispatch;
|
||||
let x = rsAlu.dispatchData;
|
||||
if(verbose) $display("[doDispatchAlu] ", fshow(x));
|
||||
|
||||
// set reg ready aggressively
|
||||
if(x.regs.dst matches tagged Valid .dst) begin
|
||||
inIfc.setRegReadyAggr(dst.indx);
|
||||
end
|
||||
|
||||
// go to next stage
|
||||
dispToRegQ.enq(ToSpecFifo {
|
||||
data: AluDispatchToRegRead {
|
||||
dInst: x.data.dInst,
|
||||
regs: x.regs,
|
||||
tag: x.tag,
|
||||
dpTrain: x.data.dpTrain,
|
||||
spec_tag: x.spec_tag
|
||||
},
|
||||
spec_bits: x.spec_bits
|
||||
});
|
||||
endrule
|
||||
|
||||
rule doRegReadAlu;
|
||||
dispToRegQ.deq;
|
||||
let dispToReg = dispToRegQ.first;
|
||||
let x = dispToReg.data;
|
||||
if(verbose) $display("[doRegReadAlu] ", fshow(dispToReg));
|
||||
|
||||
// check conservative scoreboard
|
||||
let regsReady = inIfc.sbCons_lazyLookup(x.regs);
|
||||
|
||||
// get rVal1 (check bypass)
|
||||
Data rVal1 = ?;
|
||||
if(x.dInst.csr matches tagged Valid .csr) begin
|
||||
rVal1 = inIfc.csrf_rd(csr);
|
||||
end
|
||||
else if(x.regs.src1 matches tagged Valid .src1) begin
|
||||
rVal1 <- readRFBypass(src1, regsReady.src1, inIfc.rf_rd1(src1), bypassWire);
|
||||
end
|
||||
|
||||
// get rVal2 (check bypass)
|
||||
Data rVal2 = ?;
|
||||
if(x.regs.src2 matches tagged Valid .src2) begin
|
||||
rVal2 <- readRFBypass(src2, regsReady.src2, inIfc.rf_rd2(src2), bypassWire);
|
||||
end
|
||||
|
||||
// get PC and PPC
|
||||
let pc = inIfc.rob_getPC(x.tag);
|
||||
let ppc = inIfc.rob_getPredPC(x.tag);
|
||||
|
||||
// go to next stage
|
||||
regToExeQ.enq(ToSpecFifo {
|
||||
data: AluRegReadToExe {
|
||||
dInst: x.dInst,
|
||||
dst: x.regs.dst,
|
||||
tag: x.tag,
|
||||
dpTrain: x.dpTrain,
|
||||
rVal1: rVal1,
|
||||
rVal2: rVal2,
|
||||
pc: pc,
|
||||
ppc: ppc,
|
||||
spec_tag: x.spec_tag
|
||||
},
|
||||
spec_bits: dispToReg.spec_bits
|
||||
});
|
||||
endrule
|
||||
|
||||
rule doExeAlu;
|
||||
regToExeQ.deq;
|
||||
let regToExe = regToExeQ.first;
|
||||
let x = regToExe.data;
|
||||
if(verbose) $display("[doExeAlu] ", fshow(regToExe));
|
||||
|
||||
// execution
|
||||
ExecResult exec_result = basicExec(x.dInst, x.rVal1, x.rVal2, x.pc, x.ppc);
|
||||
|
||||
// when inst needs to store csrData in ROB, it must have iType = Csr, cannot mispredict
|
||||
if(isValid(x.dInst.csr)) begin
|
||||
doAssert(x.dInst.iType == Csr, "Only Csr inst needs to update csrData in ROB");
|
||||
doAssert(!exec_result.controlFlow.mispredict, "Csr inst cannot mispredict");
|
||||
doAssert(exec_result.controlFlow.nextPc == x.ppc && x.ppc == x.pc + 4, "Csr inst ppc = pc+4");
|
||||
end
|
||||
|
||||
// send bypass
|
||||
if(x.dst matches tagged Valid .dst) begin
|
||||
inIfc.sendBypass[exeSendBypassPort].send(dst.indx, exec_result.data);
|
||||
end
|
||||
|
||||
// go to next stage
|
||||
exeToFinQ.enq(ToSpecFifo {
|
||||
data: AluExeToFinish {
|
||||
iType: x.dInst.iType,
|
||||
dst: x.dst,
|
||||
tag: x.tag,
|
||||
dpTrain: x.dpTrain,
|
||||
data: exec_result.data,
|
||||
csrData: isValid(x.dInst.csr) ? Valid (exec_result.csrData) : Invalid,
|
||||
controlFlow: exec_result.controlFlow,
|
||||
spec_tag: x.spec_tag
|
||||
},
|
||||
spec_bits: regToExe.spec_bits
|
||||
});
|
||||
endrule
|
||||
|
||||
rule doFinishAlu;
|
||||
exeToFinQ.deq;
|
||||
let exeToFin = exeToFinQ.first;
|
||||
let x = exeToFin.data;
|
||||
if(verbose) $display("[doFinishAlu] ", fshow(exeToFin));
|
||||
|
||||
// send bypass & write reg file
|
||||
if(x.dst matches tagged Valid .dst) begin
|
||||
inIfc.sendBypass[finishSendBypassPort].send(dst.indx, x.data);
|
||||
inIfc.writeRegFile(dst.indx, x.data);
|
||||
end
|
||||
|
||||
// update the instruction in the reorder buffer.
|
||||
inIfc.rob_setExecuted(
|
||||
x.tag,
|
||||
x.csrData,
|
||||
x.controlFlow
|
||||
);
|
||||
|
||||
// handle spec tags for branch predictions
|
||||
(* split *)
|
||||
if (x.controlFlow.mispredict) (* nosplit *) begin
|
||||
// wrong branch predictin, we must have spec tag
|
||||
doAssert(isValid(x.spec_tag), "mispredicted branch must have spec tag");
|
||||
inIfc.redirect(x.controlFlow.nextPc, validValue(x.spec_tag), x.tag);
|
||||
// must be a branch, train branch predictor
|
||||
doAssert(x.iType == Jr || x.iType == Br, "only jr and br can mispredict");
|
||||
inIfc.fetch_train_predictors(FetchTrainBP {
|
||||
pc: x.controlFlow.pc,
|
||||
nextPc: x.controlFlow.nextPc,
|
||||
iType: x.iType,
|
||||
taken: x.controlFlow.taken,
|
||||
dpTrain: x.dpTrain,
|
||||
mispred: True
|
||||
});
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter
|
||||
if(inIfc.doStats) begin
|
||||
case(x.iType)
|
||||
Br: exeRedirectBrCnt.incr(1);
|
||||
Jr: exeRedirectJrCnt.incr(1);
|
||||
default: exeRedirectOtherCnt.incr(1);
|
||||
endcase
|
||||
end
|
||||
`endif
|
||||
end
|
||||
else (* nosplit *) begin
|
||||
// release spec tag
|
||||
if (x.spec_tag matches tagged Valid .valid_spec_tag) begin
|
||||
inIfc.correctSpec(valid_spec_tag);
|
||||
end
|
||||
// train branch predictor if needed
|
||||
// since we can only do 1 training in a cycle, split the rule
|
||||
// XXX not training JAL, reduce chance of conflicts
|
||||
if(x.iType == Jr || x.iType == Br) begin
|
||||
inIfc.fetch_train_predictors(FetchTrainBP {
|
||||
pc: x.controlFlow.pc,
|
||||
nextPc: x.controlFlow.nextPc,
|
||||
iType: x.iType,
|
||||
taken: x.controlFlow.taken,
|
||||
dpTrain: x.dpTrain,
|
||||
mispred: False
|
||||
});
|
||||
end
|
||||
end
|
||||
endrule
|
||||
|
||||
interface recvBypass = map(getRecvBypassIfc, bypassWire);
|
||||
|
||||
interface rsAluIfc = rsAlu;
|
||||
|
||||
interface specUpdate = joinSpeculationUpdate(vec(
|
||||
rsAlu.specUpdate,
|
||||
dispToRegQ.specUpdate,
|
||||
regToExeQ.specUpdate,
|
||||
exeToFinQ.specUpdate
|
||||
));
|
||||
|
||||
method Data getPerf(ExeStagePerfType t);
|
||||
return (case(t)
|
||||
`ifdef PERF_COUNT
|
||||
ExeRedirectBr: exeRedirectBrCnt;
|
||||
ExeRedirectJr: exeRedirectJrCnt;
|
||||
ExeRedirectOther: exeRedirectOtherCnt;
|
||||
`endif
|
||||
default: 0;
|
||||
endcase);
|
||||
endmethod
|
||||
endmodule
|
||||
|
||||
756
src_Core/RISCY_OOO/procs/RV64G_OOO/CommitStage.bsv
Normal file
756
src_Core/RISCY_OOO/procs/RV64G_OOO/CommitStage.bsv
Normal file
@@ -0,0 +1,756 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import Cntrs::*;
|
||||
import ConfigReg::*;
|
||||
import FIFO::*;
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import CCTypes::*;
|
||||
import Performance::*;
|
||||
import ReorderBuffer::*;
|
||||
import ReorderBufferSynth::*;
|
||||
import RenamingTable::*;
|
||||
import CsrFile::*;
|
||||
import StoreBuffer::*;
|
||||
import VerificationPacket::*;
|
||||
import RenameDebugIF::*;
|
||||
|
||||
typedef struct {
|
||||
// info about the inst blocking at ROB head
|
||||
Addr pc;
|
||||
IType iType;
|
||||
Maybe#(Trap) trap;
|
||||
RobInstState state;
|
||||
Bool claimedPhyReg;
|
||||
Bool ldKilled;
|
||||
Bool memAccessAtCommit;
|
||||
Bool lsqAtCommitNotified;
|
||||
Bool nonMMIOStDone;
|
||||
Bool epochIncremented;
|
||||
SpecBits specBits;
|
||||
// info about LSQ/TLB
|
||||
Bool stbEmpty;
|
||||
Bool stqEmpty;
|
||||
Bool tlbNoPendingReq;
|
||||
// CSR info: previlige mode
|
||||
Bit#(2) prv;
|
||||
// inst count
|
||||
Data instCount;
|
||||
} CommitStuck deriving(Bits, Eq, FShow);
|
||||
|
||||
interface CommitInput;
|
||||
// func units
|
||||
interface ReorderBufferSynth robIfc;
|
||||
interface RegRenamingTable rtIfc;
|
||||
interface CsrFile csrfIfc;
|
||||
// no stores
|
||||
method Bool stbEmpty;
|
||||
method Bool stqEmpty;
|
||||
// notify LSQ that inst has reached commit
|
||||
interface Vector#(SupSize, Put#(LdStQTag)) lsqSetAtCommit;
|
||||
// TLB has stopped processing now
|
||||
method Bool tlbNoPendingReq;
|
||||
// set flags
|
||||
method Action setFlushTlbs;
|
||||
method Action setUpdateVMInfo;
|
||||
method Action setFlushReservation;
|
||||
method Action setFlushBrPred; // security
|
||||
method Action setFlushCaches; // security
|
||||
method Action setReconcileI; // recocile I$
|
||||
method Action setReconcileD; // recocile D$
|
||||
// redirect
|
||||
method Action killAll;
|
||||
method Action redirectPc(Addr trap_pc);
|
||||
method Action setFetchWaitRedirect;
|
||||
method Action incrementEpoch;
|
||||
// record if we commit a CSR inst or interrupt
|
||||
method Action commitCsrInstOrInterrupt;
|
||||
// performance
|
||||
method Bool doStats;
|
||||
// deadlock check
|
||||
method Bool checkDeadlock;
|
||||
endinterface
|
||||
|
||||
typedef struct {
|
||||
RenameError err;
|
||||
Addr pc;
|
||||
IType iType;
|
||||
Maybe#(Trap) trap;
|
||||
SpecBits specBits;
|
||||
} RenameErrInfo deriving(Bits, Eq, FShow);
|
||||
|
||||
interface CommitStage;
|
||||
// performance
|
||||
method Data getPerf(ComStagePerfType t);
|
||||
// deadlock check
|
||||
interface Get#(CommitStuck) commitInstStuck;
|
||||
interface Get#(CommitStuck) commitUserInstStuck;
|
||||
// rename debug
|
||||
method Action startRenameDebug;
|
||||
interface Get#(RenameErrInfo) renameErr;
|
||||
endinterface
|
||||
|
||||
// we apply actions the end of commit rule
|
||||
// use struct to record actions to be done
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr addr;
|
||||
Trap trap;
|
||||
} CommitTrap deriving(Bits, Eq, FShow);
|
||||
|
||||
module mkCommitStage#(CommitInput inIfc)(CommitStage);
|
||||
Bool verbose = True;
|
||||
|
||||
// func units
|
||||
ReorderBufferSynth rob = inIfc.robIfc;
|
||||
RegRenamingTable regRenamingTable = inIfc.rtIfc;
|
||||
CsrFile csrf = inIfc.csrfIfc;
|
||||
|
||||
// FIXME FIXME FIXME wires to set atCommit in LSQ: avoid scheduling cycle.
|
||||
// Using wire should be fine, because LSQ does not need to see atCommit
|
||||
// signal immediately. XXX The concern is about killAll which checks
|
||||
// atCommit in LSQ, but we never call killAll and setAtCommit in the same
|
||||
// cycle.
|
||||
Vector#(SupSize, RWire#(LdStQTag)) setLSQAtCommit <- replicateM(mkRWire);
|
||||
|
||||
for(Integer i = 0; i< valueof(SupSize); i = i+1) begin
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule doSetLSQAtCommit(setLSQAtCommit[i].wget matches tagged Valid .tag);
|
||||
inIfc.lsqSetAtCommit[i].put(tag);
|
||||
endrule
|
||||
end
|
||||
|
||||
// commit stage performance counters
|
||||
`ifdef PERF_COUNT
|
||||
// inst
|
||||
Count#(Data) instCnt <- mkCount(0);
|
||||
Count#(Data) userInstCnt <- mkCount(0);
|
||||
Count#(Data) supComUserCnt <- mkCount(0);
|
||||
// branch/jump inst
|
||||
Count#(Data) comBrCnt <- mkCount(0);
|
||||
Count#(Data) comJmpCnt <- mkCount(0);
|
||||
Count#(Data) comJrCnt <- mkCount(0);
|
||||
// mem inst
|
||||
Count#(Data) comLdCnt <- mkCount(0);
|
||||
Count#(Data) comStCnt <- mkCount(0);
|
||||
Count#(Data) comLrCnt <- mkCount(0);
|
||||
Count#(Data) comScCnt <- mkCount(0);
|
||||
Count#(Data) comAmoCnt <- mkCount(0);
|
||||
// load mispeculation
|
||||
Count#(Data) comLdKillByLdCnt <- mkCount(0);
|
||||
Count#(Data) comLdKillByStCnt <- mkCount(0);
|
||||
Count#(Data) comLdKillByCacheCnt <- mkCount(0);
|
||||
// exception/sys inst related
|
||||
Count#(Data) comSysCnt <- mkCount(0);
|
||||
Count#(Data) excepCnt <- mkCount(0);
|
||||
Count#(Data) interruptCnt <- mkCount(0);
|
||||
// flush tlb
|
||||
Count#(Data) flushTlbCnt <- mkCount(0);
|
||||
// flush security
|
||||
Count#(Data) flushSecurityCnt <- mkCount(0);
|
||||
Count#(Data) flushBPCnt <- mkCount(0);
|
||||
Count#(Data) flushCacheCnt <- mkCount(0);
|
||||
`endif
|
||||
|
||||
// deadlock check
|
||||
`ifdef CHECK_DEADLOCK
|
||||
// timer to check deadlock
|
||||
Reg#(DeadlockTimer) commitInstTimer <- mkReg(0);
|
||||
Reg#(DeadlockTimer) commitUserInstTimer <- mkReg(0);
|
||||
// FIFOs to output deadlock info
|
||||
FIFO#(CommitStuck) commitInstStuckQ <- mkFIFO1;
|
||||
FIFO#(CommitStuck) commitUserInstStuckQ <- mkFIFO1;
|
||||
// wires to indicate that deadlock is reported, so reset timers
|
||||
PulseWire commitInstStuckSent <- mkPulseWire;
|
||||
PulseWire commitUserInstStuckSent <- mkPulseWire;
|
||||
// wires to reset timers since processor is making progress
|
||||
PulseWire commitInst <- mkPulseWire;
|
||||
PulseWire commitUserInst <- mkPulseWire;
|
||||
|
||||
function CommitStuck commitStuck;
|
||||
let x = rob.deqPort[0].deq_data;
|
||||
return CommitStuck {
|
||||
pc: x.pc,
|
||||
iType: x.iType,
|
||||
trap: x.trap,
|
||||
state: x.rob_inst_state,
|
||||
claimedPhyReg: x.claimed_phy_reg,
|
||||
ldKilled: isValid(x.ldKilled),
|
||||
memAccessAtCommit: x.memAccessAtCommit,
|
||||
lsqAtCommitNotified: x.lsqAtCommitNotified,
|
||||
nonMMIOStDone: x.nonMMIOStDone,
|
||||
epochIncremented: x.epochIncremented,
|
||||
specBits: x.spec_bits,
|
||||
stbEmpty: inIfc.stbEmpty,
|
||||
stqEmpty: inIfc.stqEmpty,
|
||||
tlbNoPendingReq: inIfc.tlbNoPendingReq,
|
||||
prv: csrf.decodeInfo.prv,
|
||||
instCount: csrf.rd(CSRinstret)
|
||||
};
|
||||
endfunction
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock_commitInst(inIfc.checkDeadlock && commitInstTimer == maxBound);
|
||||
commitInstStuckQ.enq(commitStuck);
|
||||
commitInstStuckSent.send;
|
||||
endrule
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock_commitUserInst(inIfc.checkDeadlock && commitUserInstTimer == maxBound);
|
||||
commitUserInstStuckQ.enq(commitStuck);
|
||||
commitUserInstStuckSent.send;
|
||||
endrule
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incrDeadlockTimer(inIfc.checkDeadlock);
|
||||
function DeadlockTimer getNextTimer(DeadlockTimer t);
|
||||
return t == maxBound ? maxBound : t + 1;
|
||||
endfunction
|
||||
commitInstTimer <= (commitInst || commitInstStuckSent) ? 0 : getNextTimer(commitInstTimer);
|
||||
commitUserInstTimer <= (commitUserInst || commitUserInstStuckSent) ? 0 : getNextTimer(commitUserInstTimer);
|
||||
endrule
|
||||
`endif
|
||||
|
||||
`ifdef RENAME_DEBUG
|
||||
// rename debug
|
||||
Reg#(Bool) renameDebugStarted <- mkConfigReg(False);
|
||||
Reg#(Maybe#(RenameErrInfo)) renameErrInfo <- mkConfigReg(Invalid);
|
||||
Bool canSetRenameErr = renameDebugStarted && renameErrInfo == Invalid; // only set err info once
|
||||
// only send 1 error msg
|
||||
FIFO#(RenameErrInfo) renameErrQ <- mkFIFO1;
|
||||
|
||||
rule sendRenameErr(renameDebugStarted &&& renameErrInfo matches tagged Valid .info);
|
||||
renameErrQ.enq(info);
|
||||
renameDebugStarted <= False;
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// we commit trap in two cycles: first cycle deq ROB and flush; second
|
||||
// cycle handles trap, redirect and handles system consistency
|
||||
Reg#(Maybe#(CommitTrap)) commitTrap <- mkReg(Invalid); // saves new pc here
|
||||
|
||||
// maintain system consistency when system state (CSR) changes or for security
|
||||
function Action makeSystemConsistent(Bool flushTlb,
|
||||
Bool flushSecurity,
|
||||
Bool reconcileI);
|
||||
action
|
||||
`ifndef SECURITY
|
||||
flushSecurity = False;
|
||||
`endif
|
||||
|
||||
`ifndef DISABLE_SECURE_FLUSH_TLB
|
||||
if(flushTlb || flushSecurity) begin
|
||||
`else
|
||||
if(flushTlb) begin
|
||||
`endif
|
||||
inIfc.setFlushTlbs;
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
flushTlbCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
end
|
||||
// notify TLB to keep update of CSR changes
|
||||
inIfc.setUpdateVMInfo;
|
||||
// always wait store buffer and SQ to be empty
|
||||
when(inIfc.stbEmpty && inIfc.stqEmpty, noAction);
|
||||
// We wait TLB to finish all requests and become sync with memory.
|
||||
// Notice that currently TLB is read only, so TLB is always in sync
|
||||
// with memory (i.e., there is no write to commit to memory). Since all
|
||||
// insts have been killed, nothing can be issued to D TLB at this time.
|
||||
// Since fetch stage is set to wait for redirect, fetch1 stage is
|
||||
// stalled, and nothing can be issued to I TLB at this time.
|
||||
// Therefore, we just need to make sure that I and D TLBs are not
|
||||
// handling any miss req. Besides, when I and D TLBs do not have any
|
||||
// miss req, L2 TLB must be idling.
|
||||
when(inIfc.tlbNoPendingReq, noAction);
|
||||
// yield load reservation in cache
|
||||
inIfc.setFlushReservation;
|
||||
|
||||
// flush for security, we can delay the stall for fetch-empty and
|
||||
// wrong-path-load-empty until we really do the flush. This delay is
|
||||
// valid because these wrong path inst/req will not interfere with
|
||||
// whatever CSR changes we are making now.
|
||||
if(flushSecurity) begin
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
flushSecurityCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifndef DISABLE_SECURE_FLUSH_BP
|
||||
inIfc.setFlushBrPred;
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
flushBPCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
`endif
|
||||
|
||||
`ifndef DISABLE_SECURE_FLUSH_CACHE
|
||||
inIfc.setFlushCaches;
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
flushCacheCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
`endif
|
||||
end
|
||||
|
||||
`ifdef SELF_INV_CACHE
|
||||
// reconcile I$
|
||||
if(reconcileI) begin
|
||||
inIfc.setReconcileI;
|
||||
end
|
||||
`ifdef SYSTEM_SELF_INV_L1D
|
||||
// FIXME is this reconcile of D$ necessary?
|
||||
inIfc.setReconcileD;
|
||||
`endif
|
||||
`endif
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// TODO Currently we don't check spec bits == 0 when we commit an
|
||||
// instruction. This is because killings of wrong path instructions are
|
||||
// done in a single cycle. However, when we make killings distributed or
|
||||
// pipelined, then we need to check spec bits at commit port.
|
||||
|
||||
rule doCommitTrap_flush(
|
||||
!isValid(commitTrap) &&&
|
||||
rob.deqPort[0].deq_data.trap matches tagged Valid .trap
|
||||
);
|
||||
rob.deqPort[0].deq;
|
||||
let x = rob.deqPort[0].deq_data;
|
||||
if(verbose) $display("[doCommitTrap] ", fshow(x));
|
||||
|
||||
// record trap info
|
||||
Addr vaddr = ?;
|
||||
if(x.ppc_vaddr_csrData matches tagged VAddr .va) begin
|
||||
vaddr = va;
|
||||
end
|
||||
commitTrap <= Valid (CommitTrap {
|
||||
trap: trap,
|
||||
pc: x.pc,
|
||||
addr: vaddr
|
||||
});
|
||||
|
||||
// flush everything. Only increment epoch and stall fetch when we haven
|
||||
// not done it yet (we may have already done them at rename stage)
|
||||
inIfc.killAll;
|
||||
if(!x.epochIncremented) begin
|
||||
inIfc.incrementEpoch;
|
||||
inIfc.setFetchWaitRedirect;
|
||||
end
|
||||
|
||||
// faulting mem inst may have claimed phy reg, we should not commit it;
|
||||
// instead, we kill the renaming by calling killAll
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter
|
||||
if(inIfc.doStats) begin
|
||||
if(trap matches tagged Exception .e) begin
|
||||
excepCnt.incr(1);
|
||||
end
|
||||
else begin
|
||||
interruptCnt.incr(1);
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
// checks
|
||||
doAssert(x.rob_inst_state == Executed, "must be executed");
|
||||
doAssert(x.spec_bits == 0, "cannot have spec bits");
|
||||
endrule
|
||||
|
||||
rule doCommitTrap_handle(commitTrap matches tagged Valid .trap);
|
||||
// reset commitTrap
|
||||
commitTrap <= Invalid;
|
||||
|
||||
// notify commit of interrupt (so MMIO pRq may be handled)
|
||||
if(trap.trap matches tagged Interrupt .inter) begin
|
||||
inIfc.commitCsrInstOrInterrupt;
|
||||
end
|
||||
|
||||
// trap handling & redirect
|
||||
let new_pc <- csrf.trap(trap.trap, trap.pc, trap.addr);
|
||||
inIfc.redirectPc(new_pc);
|
||||
|
||||
// system consistency
|
||||
// TODO spike flushes TLB here, but perhaps it is because spike's TLB
|
||||
// does not include prv info, and it has to flush when prv changes.
|
||||
// XXX As approximation, Trap may cause context switch, so flush for
|
||||
// security
|
||||
makeSystemConsistent(False, True, False);
|
||||
endrule
|
||||
|
||||
// commit misspeculated load
|
||||
rule doCommitKilledLd(
|
||||
!isValid(commitTrap) &&&
|
||||
!isValid(rob.deqPort[0].deq_data.trap) &&&
|
||||
rob.deqPort[0].deq_data.ldKilled matches tagged Valid .killBy
|
||||
);
|
||||
rob.deqPort[0].deq;
|
||||
let x = rob.deqPort[0].deq_data;
|
||||
if(verbose) $display("[doCommitKilledLd] ", fshow(x));
|
||||
|
||||
// kill everything, redirect, and increment epoch
|
||||
inIfc.killAll;
|
||||
inIfc.redirectPc(x.pc);
|
||||
inIfc.incrementEpoch;
|
||||
|
||||
// the killed Ld should have claimed phy reg, we should not commit it;
|
||||
// instead, we have kill the renaming by calling killAll
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
case(killBy)
|
||||
Ld: comLdKillByLdCnt.incr(1);
|
||||
St: comLdKillByStCnt.incr(1);
|
||||
Cache: comLdKillByCacheCnt.incr(1);
|
||||
endcase
|
||||
end
|
||||
`endif
|
||||
|
||||
// checks
|
||||
doAssert(!x.epochIncremented, "cannot increment epoch before");
|
||||
doAssert(x.rob_inst_state == Executed, "must be executed");
|
||||
doAssert(x.spec_bits == 0, "cannot have spec bits");
|
||||
endrule
|
||||
|
||||
// commit system inst
|
||||
rule doCommitSystemInst(
|
||||
!isValid(commitTrap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.trap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
|
||||
rob.deqPort[0].deq_data.rob_inst_state == Executed &&
|
||||
isSystem(rob.deqPort[0].deq_data.iType)
|
||||
);
|
||||
rob.deqPort[0].deq;
|
||||
let x = rob.deqPort[0].deq_data;
|
||||
if(verbose) $display("[doCommitSystemInst] ", fshow(x));
|
||||
|
||||
// we claim a phy reg for every inst, so commit its renaming
|
||||
regRenamingTable.commit[0].commit;
|
||||
|
||||
Bool write_satp = False; // flush tlb when satp csr is modified
|
||||
Bool flush_security = False; // flush for security when the flush csr is written
|
||||
if(x.iType == Csr) begin
|
||||
// notify commit of CSR (so MMIO pRq may be handled)
|
||||
inIfc.commitCsrInstOrInterrupt;
|
||||
// write CSR
|
||||
let csr_idx = validValue(x.csr);
|
||||
Data csr_data = ?;
|
||||
if(x.ppc_vaddr_csrData matches tagged CSRData .d) begin
|
||||
csr_data = d;
|
||||
end
|
||||
else begin
|
||||
doAssert(False, "must have csr data");
|
||||
end
|
||||
csrf.csrInstWr(csr_idx, csr_data);
|
||||
// check if satp is modified or not
|
||||
write_satp = csr_idx == CSRsatp;
|
||||
`ifdef SECURITY
|
||||
flush_security = csr_idx == CSRmflush;
|
||||
`endif
|
||||
end
|
||||
|
||||
// redirect (Sret and Mret redirect pc is got from CSRF)
|
||||
Addr next_pc = x.ppc_vaddr_csrData matches tagged PPC .ppc ? ppc : (x.pc + 4);
|
||||
doAssert(next_pc == x.pc + 4, "ppc must be pc + 4");
|
||||
if(x.iType == Sret) begin
|
||||
next_pc <- csrf.sret;
|
||||
end
|
||||
else if(x.iType == Mret) begin
|
||||
next_pc <- csrf.mret;
|
||||
end
|
||||
inIfc.redirectPc(next_pc);
|
||||
|
||||
// rename stage only sends out system inst when ROB is empty, so no
|
||||
// need to flush ROB again
|
||||
|
||||
// system consistency
|
||||
// flush TLB for SFence.VMA and when SATP CSR is modified
|
||||
// XXX as approximation, sret/mret may mean context switch, so flush
|
||||
// for security
|
||||
makeSystemConsistent(
|
||||
x.iType == SFence || write_satp, // TODO flush TLB when change sanctum regs?
|
||||
flush_security || x.iType == Sret || x.iType == Mret,
|
||||
x.iType == FenceI // reconcile I$ for fence.i
|
||||
);
|
||||
|
||||
// incr inst cnt
|
||||
csrf.incInstret(1);
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
comSysCnt.incr(1);
|
||||
// inst count stats
|
||||
instCnt.incr(1);
|
||||
if(csrf.decodeInfo.prv == 0) begin
|
||||
userInstCnt.incr(1);
|
||||
end
|
||||
end
|
||||
`endif
|
||||
`ifdef CHECK_DEADLOCK
|
||||
commitInst.send;
|
||||
if(csrf.decodeInfo.prv == 0) begin
|
||||
commitUserInst.send;
|
||||
end
|
||||
`endif
|
||||
|
||||
// checks
|
||||
doAssert(x.epochIncremented, "must have already incremented epoch");
|
||||
doAssert((x.iType == Csr) == isValid(x.csr), "only CSR has valid csr idx");
|
||||
doAssert(x.fflags == 0 && !x.will_dirty_fpu_state, "cannot dirty FPU");
|
||||
doAssert(x.spec_bits == 0, "cannot have spec bits");
|
||||
doAssert(x.claimed_phy_reg, "must have claimed phy reg");
|
||||
`ifdef RENAME_DEBUG
|
||||
if(!x.claimed_phy_reg && canSetRenameErr) begin
|
||||
renameErrInfo <= Valid (RenameErrInfo {
|
||||
err: NonTrapCommitLackClaim,
|
||||
pc: x.pc,
|
||||
iType: x.iType,
|
||||
trap: x.trap,
|
||||
specBits: x.spec_bits
|
||||
});
|
||||
end
|
||||
`endif
|
||||
endrule
|
||||
|
||||
// Lr/Sc/Amo/MMIO cannot proceed to executed until we notify LSQ that it
|
||||
// has reached the commit stage
|
||||
rule notifyLSQCommit(
|
||||
!isValid(commitTrap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.trap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
|
||||
rob.deqPort[0].deq_data.rob_inst_state != Executed &&
|
||||
rob.deqPort[0].deq_data.memAccessAtCommit &&
|
||||
!rob.deqPort[0].deq_data.lsqAtCommitNotified
|
||||
);
|
||||
let x = rob.deqPort[0].deq_data;
|
||||
let inst_tag = rob.deqPort[0].getDeqInstTag;
|
||||
if(verbose) $display("[notifyLSQCommit] ", fshow(x), "; ", fshow(inst_tag));
|
||||
|
||||
// notify LSQ, and record in ROB that notification is done
|
||||
setLSQAtCommit[0].wset(x.lsqTag);
|
||||
rob.setLSQAtCommitNotified(inst_tag);
|
||||
endrule
|
||||
|
||||
// commit normal: fire when at least one commit can be done
|
||||
rule doCommitNormalInst(
|
||||
!isValid(commitTrap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.trap) &&
|
||||
!isValid(rob.deqPort[0].deq_data.ldKilled) &&
|
||||
rob.deqPort[0].deq_data.rob_inst_state == Executed &&
|
||||
!isSystem(rob.deqPort[0].deq_data.iType)
|
||||
);
|
||||
// stop superscalar commit after we
|
||||
// 1. see a trap or system inst or killed Ld
|
||||
// 2. inst is not ready to commit
|
||||
Bool stop = False;
|
||||
|
||||
// We merge writes on FPU csr and apply writes at the end of the rule
|
||||
Bit#(5) fflags = 0;
|
||||
Bool will_dirty_fpu_state = False;
|
||||
// rename error
|
||||
Maybe#(RenameErrInfo) renameError = Invalid;
|
||||
// incr committed inst cnt at the end of rule
|
||||
SupCnt comInstCnt = 0;
|
||||
SupCnt comUserInstCnt = 0;
|
||||
`ifdef PERF_COUNT
|
||||
// incr some performance counter at the end of rule
|
||||
SupCnt brCnt = 0;
|
||||
SupCnt jmpCnt = 0;
|
||||
SupCnt jrCnt = 0;
|
||||
SupCnt ldCnt = 0;
|
||||
SupCnt stCnt = 0;
|
||||
SupCnt lrCnt = 0;
|
||||
SupCnt scCnt = 0;
|
||||
SupCnt amoCnt = 0;
|
||||
`endif
|
||||
|
||||
// compute what actions to take
|
||||
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
|
||||
if(!stop && rob.deqPort[i].canDeq) begin
|
||||
let x = rob.deqPort[i].deq_data;
|
||||
let inst_tag = rob.deqPort[i].getDeqInstTag;
|
||||
|
||||
// check can be committed or not
|
||||
if(x.rob_inst_state != Executed || isValid(x.ldKilled) || isValid(x.trap) || isSystem(x.iType)) begin
|
||||
// inst not ready for commit, or system inst, or trap, or killed, stop here
|
||||
stop = True;
|
||||
end
|
||||
else begin
|
||||
if (verbose) $display("[doCommitNormalInst - %d] ", i, fshow(inst_tag), " ; ", fshow(x));
|
||||
|
||||
// inst can be committed, deq it
|
||||
rob.deqPort[i].deq;
|
||||
|
||||
// every inst here should have been renamed, commit renaming
|
||||
regRenamingTable.commit[i].commit;
|
||||
doAssert(x.claimed_phy_reg, "should have renamed");
|
||||
|
||||
`ifdef RENAME_DEBUG
|
||||
// send debug msg for rename error
|
||||
if(!x.claimed_phy_reg && !isValid(renameError)) begin
|
||||
renameError = Valid (RenameErrInfo {
|
||||
err: NonTrapCommitLackClaim,
|
||||
pc: x.pc,
|
||||
iType: x.iType,
|
||||
trap: x.trap,
|
||||
specBits: x.spec_bits
|
||||
});
|
||||
end
|
||||
`endif
|
||||
|
||||
// cumulate writes to FPU csr
|
||||
fflags = fflags | x.fflags;
|
||||
will_dirty_fpu_state = will_dirty_fpu_state || x.will_dirty_fpu_state;
|
||||
|
||||
// for non-mmio st, notify SQ that store is committed
|
||||
if(x.nonMMIOStDone) begin
|
||||
setLSQAtCommit[i].wset(x.lsqTag);
|
||||
end
|
||||
|
||||
// inst commit counter
|
||||
comInstCnt = comInstCnt + 1;
|
||||
if(csrf.decodeInfo.prv == 0) begin
|
||||
comUserInstCnt = comUserInstCnt + 1; // user space inst
|
||||
end
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter
|
||||
case(x.iType)
|
||||
Br: brCnt = brCnt + 1;
|
||||
J : jmpCnt = jmpCnt + 1;
|
||||
Jr: jrCnt = jrCnt + 1;
|
||||
Ld: ldCnt = ldCnt + 1;
|
||||
St: stCnt = stCnt + 1;
|
||||
Lr: lrCnt = lrCnt + 1;
|
||||
Sc: scCnt = scCnt + 1;
|
||||
Amo: amoCnt = amoCnt + 1;
|
||||
endcase
|
||||
`endif
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// write FPU csr
|
||||
if(csrf.fpuInstNeedWr(fflags, will_dirty_fpu_state)) begin
|
||||
csrf.fpuInstWr(fflags);
|
||||
end
|
||||
|
||||
// incr inst cnt
|
||||
csrf.incInstret(comInstCnt);
|
||||
|
||||
`ifdef RENAME_DEBUG
|
||||
// set rename error
|
||||
if(canSetRenameErr && isValid(renameError)) begin
|
||||
renameErrInfo <= renameError;
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
commitInst.send; // ROB head is removed
|
||||
if(comUserInstCnt > 0) begin
|
||||
commitUserInst.send;
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter
|
||||
if(inIfc.doStats) begin
|
||||
// branch stats
|
||||
comBrCnt.incr(zeroExtend(brCnt));
|
||||
comJmpCnt.incr(zeroExtend(jmpCnt));
|
||||
comJrCnt.incr(zeroExtend(jrCnt));
|
||||
// mem stats
|
||||
comLdCnt.incr(zeroExtend(ldCnt));
|
||||
comStCnt.incr(zeroExtend(stCnt));
|
||||
comLrCnt.incr(zeroExtend(lrCnt));
|
||||
comScCnt.incr(zeroExtend(scCnt));
|
||||
comAmoCnt.incr(zeroExtend(amoCnt));
|
||||
// inst count stats
|
||||
instCnt.incr(zeroExtend(comInstCnt));
|
||||
userInstCnt.incr(zeroExtend(comUserInstCnt));
|
||||
if(comUserInstCnt > 1) begin
|
||||
supComUserCnt.incr(1);
|
||||
end
|
||||
end
|
||||
`endif
|
||||
endrule
|
||||
|
||||
|
||||
method Data getPerf(ComStagePerfType t);
|
||||
return (case(t)
|
||||
`ifdef PERF_COUNT
|
||||
InstCnt: instCnt;
|
||||
UserInstCnt: userInstCnt;
|
||||
SupComUserCnt: supComUserCnt;
|
||||
ComBrCnt: comBrCnt;
|
||||
ComJmpCnt: comJmpCnt;
|
||||
ComJrCnt: comJrCnt;
|
||||
ComLdCnt: comLdCnt;
|
||||
ComStCnt: comStCnt;
|
||||
ComLrCnt: comLrCnt;
|
||||
ComScCnt: comScCnt;
|
||||
ComAmoCnt: comAmoCnt;
|
||||
ComLdKillByLd: comLdKillByLdCnt;
|
||||
ComLdKillBySt: comLdKillByStCnt;
|
||||
ComLdKillByCache: comLdKillByCacheCnt;
|
||||
ComSysCnt: comSysCnt;
|
||||
ExcepCnt: excepCnt;
|
||||
InterruptCnt: interruptCnt;
|
||||
FlushTlbCnt: flushTlbCnt;
|
||||
FlushSecurityCnt: flushSecurityCnt;
|
||||
FlushBPCnt: flushBPCnt;
|
||||
FlushCacheCnt: flushCacheCnt;
|
||||
`endif
|
||||
default: 0;
|
||||
endcase);
|
||||
endmethod
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface commitInstStuck = toGet(commitInstStuckQ);
|
||||
interface commitUserInstStuck = toGet(commitUserInstStuckQ);
|
||||
`else
|
||||
interface commitInstStuck = nullGet;
|
||||
interface commitUserInstStuck = nullGet;
|
||||
`endif
|
||||
|
||||
`ifdef RENAME_DEBUG
|
||||
method Action startRenameDebug if(!renameDebugStarted);
|
||||
renameDebugStarted <= True;
|
||||
endmethod
|
||||
interface renameErr = toGet(renameErrQ);
|
||||
`else
|
||||
method Action startRenameDebug;
|
||||
noAction;
|
||||
endmethod
|
||||
interface renameErr = nullGet;
|
||||
`endif
|
||||
endmodule
|
||||
641
src_Core/RISCY_OOO/procs/RV64G_OOO/FetchStage.bsv
Normal file
641
src_Core/RISCY_OOO/procs/RV64G_OOO/FetchStage.bsv
Normal file
@@ -0,0 +1,641 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
|
||||
import BrPred::*;
|
||||
import DirPredictor::*;
|
||||
import Btb::*;
|
||||
import ClientServer::*;
|
||||
import Connectable::*;
|
||||
import Decode::*;
|
||||
import Ehr::*;
|
||||
import Fifo::*;
|
||||
import GetPut::*;
|
||||
import MemoryTypes::*;
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import CCTypes::*;
|
||||
import Ras::*;
|
||||
import EpochManager::*;
|
||||
import Performance::*;
|
||||
import Vector::*;
|
||||
import Assert::*;
|
||||
import Cntrs::*;
|
||||
import ConfigReg::*;
|
||||
import TlbTypes::*;
|
||||
import ITlb::*;
|
||||
import CCTypes::*;
|
||||
import L1CoCache::*;
|
||||
import MMIOInst::*;
|
||||
|
||||
interface FetchStage;
|
||||
// pipeline
|
||||
interface Vector#(SupSize, SupFifoDeq#(FromFetchStage)) pipelines;
|
||||
|
||||
// tlb and mem connections
|
||||
interface ITlb iTlbIfc;
|
||||
interface ICoCache iMemIfc;
|
||||
interface MMIOInstToCore mmioIfc;
|
||||
|
||||
// starting and stopping
|
||||
method Action start(Addr pc);
|
||||
method Action stop();
|
||||
|
||||
// redirection methods
|
||||
method Action setWaitRedirect;
|
||||
method Action redirect(Addr pc);
|
||||
method Action done_flushing();
|
||||
method Action train_predictors(
|
||||
Addr pc, Addr next_pc, IType iType, Bool taken,
|
||||
DirPredTrainInfo dpTrain, Bool mispred
|
||||
);
|
||||
|
||||
// security
|
||||
method Bool emptyForFlush;
|
||||
method Action flush_predictors;
|
||||
method Bool flush_predictors_done;
|
||||
|
||||
// debug
|
||||
method FetchDebugState getFetchState;
|
||||
|
||||
// performance
|
||||
interface Perf#(DecStagePerfType) perf;
|
||||
endinterface
|
||||
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Epoch mainEp;
|
||||
Bool waitForRedirect;
|
||||
Bool waitForFlush;
|
||||
} FetchDebugState deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr pred_next_pc;
|
||||
Bool decode_epoch;
|
||||
Epoch main_epoch;
|
||||
} Fetch1ToFetch2 deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr phys_pc;
|
||||
Addr pred_next_pc;
|
||||
Maybe#(Exception) cause;
|
||||
Bool access_mmio; // inst fetch from MMIO
|
||||
Bool decode_epoch;
|
||||
Epoch main_epoch;
|
||||
} Fetch2ToFetch3 deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr ppc;
|
||||
Bool decode_epoch;
|
||||
Epoch main_epoch;
|
||||
Instruction inst;
|
||||
Maybe#(Exception) cause;
|
||||
} Fetch3ToDecode deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr ppc;
|
||||
Epoch main_epoch;
|
||||
DirPredTrainInfo dpTrain;
|
||||
Instruction inst;
|
||||
DecodedInst dInst;
|
||||
ArchRegs regs;
|
||||
Maybe#(Exception) cause;
|
||||
} FromFetchStage deriving (Bits, Eq, FShow);
|
||||
|
||||
// train next addr pred (BTB)
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr nextPc;
|
||||
} TrainNAP deriving(Bits, Eq, FShow);
|
||||
|
||||
(* synthesize *)
|
||||
module mkFetchStage(FetchStage);
|
||||
// rule ordering: Fetch1 (BTB+TLB) < Fetch3 (decode & dir pred) < redirect method
|
||||
// Fetch1 < Fetch3 to avoid bypassing path on PC and epochs
|
||||
|
||||
let verbose = True;
|
||||
|
||||
// Basic State Elements
|
||||
Reg#(Bool) started <- mkReg(False);
|
||||
|
||||
// Stall fetch when trap happens or system inst is renamed
|
||||
// All inst younger than the trap/system inst will be killed
|
||||
// Since CSR may be modified, sending wrong path request to TLB may cause problem
|
||||
// So we stall until the next redirection happens
|
||||
// The next redirect is either by the trap/system inst or an older one
|
||||
Reg#(Bool) waitForRedirect <- mkReg(False);
|
||||
// We don't want setWaitForRedirect method and redirect method to happen together
|
||||
// make them conflict
|
||||
RWire#(void) setWaitRedirect_redirect_conflict <- mkRWire;
|
||||
|
||||
// Stall fetch during the flush triggered by the procesing trap/system inst in commit stage
|
||||
// We stall until the flush is done
|
||||
Reg#(Bool) waitForFlush <- mkReg(False);
|
||||
|
||||
Ehr#(3, Addr) pc_reg <- mkEhr(0);
|
||||
Integer pc_fetch1_port = 0;
|
||||
Integer pc_decode_port = 1;
|
||||
Integer pc_redirect_port = 2;
|
||||
|
||||
// Epochs
|
||||
Reg#(Bool) decode_epoch <- mkReg(False);
|
||||
Reg#(Epoch) f_main_epoch <- mkReg(0); // fetch estimate of main epoch
|
||||
|
||||
// Pipeline Stage FIFOs
|
||||
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch1ToFetch2)) f12f2 <- mkCFFifo;
|
||||
Fifo#(4, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f22f3 <- mkCFFifo; // FIFO should match I$ latency
|
||||
Fifo#(2, Tuple2#(Bit#(TLog#(SupSize)),Fetch2ToFetch3)) f32d <- mkCFFifo;
|
||||
Fifo#(2, Vector#(SupSize,Maybe#(Instruction))) instdata <- mkPipelineFifo();
|
||||
SupFifo#(SupSize, 2, FromFetchStage) out_fifo <- mkSupFifo;
|
||||
// Can the fifo size be smaller?
|
||||
|
||||
// Branch Predictors
|
||||
NextAddrPred nextAddrPred <- mkBtb;
|
||||
let dirPred <- mkDirPredictor;
|
||||
ReturnAddrStack ras <- mkRas;
|
||||
// Wire to train next addr pred (NAP)
|
||||
RWire#(TrainNAP) napTrainByExe <- mkRWire;
|
||||
RWire#(TrainNAP) napTrainByDec <- mkRWire;
|
||||
Fifo#(1, TrainNAP) napTrainByDecQ <- mkPipelineFifo; // cut off critical path
|
||||
|
||||
// TLB and Cache connections
|
||||
ITlb iTlb <- mkITlb;
|
||||
ICoCache iMem <- mkICoCache;
|
||||
MMIOInst mmio <- mkMMIOInst;
|
||||
Server#(Addr, TlbResp) tlb_server = iTlb.to_proc;
|
||||
Server#(Addr, Vector#(SupSize, Maybe#(Instruction))) mem_server = iMem.to_proc;
|
||||
|
||||
// performance counters
|
||||
Fifo#(1, DecStagePerfType) perfReqQ <- mkCFFifo; // perf req FIFO
|
||||
`ifdef PERF_COUNT
|
||||
Reg#(Bool) doStats <- mkConfigReg(False);
|
||||
// decode stage redirect
|
||||
Count#(Data) decRedirectBrCnt <- mkCount(0);
|
||||
Count#(Data) decRedirectJmpCnt <- mkCount(0);
|
||||
Count#(Data) decRedirectJrCnt <- mkCount(0);
|
||||
Count#(Data) decRedirectOtherCnt <- mkCount(0);
|
||||
// perf resp FIFO
|
||||
Fifo#(1, PerfResp#(DecStagePerfType)) perfRespQ <- mkCFFifo;
|
||||
|
||||
rule doPerfReq;
|
||||
let t <- toGet(perfReqQ).get;
|
||||
Data d = (case(t)
|
||||
DecRedirectBr: decRedirectBrCnt;
|
||||
DecRedirectJmp: decRedirectJmpCnt;
|
||||
DecRedirectJr: decRedirectJrCnt;
|
||||
DecRedirectOther: decRedirectOtherCnt;
|
||||
default: 0;
|
||||
endcase);
|
||||
perfRespQ.enq(PerfResp {
|
||||
pType: t,
|
||||
data: d
|
||||
});
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// We don't send req to TLB when waiting for redirect or TLB flush. Since
|
||||
// there is no FIFO between doFetch1 and TLB, when OOO commit stage wait
|
||||
// TLB idle to change VM CSR / signal flush TLB, there is no wrong path
|
||||
// request afterwards to race with the system code that manage paget table.
|
||||
rule doFetch1(started && !waitForRedirect && !waitForFlush);
|
||||
let pc = pc_reg[pc_fetch1_port];
|
||||
|
||||
// Chain of prediction for the next instructions
|
||||
// We need a BTB with a register file with enough ports!
|
||||
// Instead of cascading predictions, we can always feed pc+4*i into
|
||||
// predictor, because we will break superscaler fetch if nextpc != pc+4
|
||||
Vector#(SupSize, Addr) pred_future_pc;
|
||||
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
|
||||
pred_future_pc[i] = nextAddrPred.predPc(pc + fromInteger(4 * i));
|
||||
end
|
||||
|
||||
// Next pc is the first nextPc that breaks the chain of pc+4 or
|
||||
// that is at the end of a cacheline.
|
||||
Vector#(SupSize,Integer) indexes = genVector;
|
||||
function Bool findNextPc(Addr pc, Integer i);
|
||||
Bool notLastInst = getLineInstOffset(pc + fromInteger(4*i)) != maxBound;
|
||||
Bool noJump = pred_future_pc[i] == pc + fromInteger(4*(i+1));
|
||||
return (!(notLastInst && noJump));
|
||||
endfunction
|
||||
Integer posLastSup = fromMaybe(valueof(SupSize) - 1, find(findNextPc(pc), indexes));
|
||||
Addr pred_next_pc = pred_future_pc[posLastSup];
|
||||
pc_reg[pc_fetch1_port] <= pred_next_pc;
|
||||
|
||||
// Send TLB request
|
||||
tlb_server.request.put(pc);
|
||||
|
||||
let out = Fetch1ToFetch2 {
|
||||
pc: pc,
|
||||
pred_next_pc: pred_next_pc,
|
||||
decode_epoch: decode_epoch,
|
||||
main_epoch: f_main_epoch};
|
||||
f12f2.enq(tuple2(fromInteger(posLastSup),out));
|
||||
if (verbose) $display("Fetch1: ", fshow(out));
|
||||
endrule
|
||||
|
||||
rule doFetch2;
|
||||
let {nbSup,in} = f12f2.first;
|
||||
f12f2.deq;
|
||||
|
||||
// Get TLB response
|
||||
match {.phys_pc, .cause} <- tlb_server.response.get;
|
||||
|
||||
// Access main mem or boot rom
|
||||
Bool access_mmio = False;
|
||||
if (!isValid(cause)) begin
|
||||
case(mmio.getFetchTarget(phys_pc))
|
||||
MainMem: begin
|
||||
// Send ICache request
|
||||
mem_server.request.put(phys_pc);
|
||||
end
|
||||
BootRom: begin
|
||||
// Send MMIO req. Luckily boot rom is also aligned with
|
||||
// cache line size, so all nbSup+1 insts can be fetched
|
||||
// from boot rom. It won't happen that insts fetched from
|
||||
// boot rom is less than requested.
|
||||
mmio.bootRomReq(phys_pc, nbSup);
|
||||
access_mmio = True;
|
||||
end
|
||||
default: begin
|
||||
// Access fault
|
||||
cause = Valid (InstAccessFault);
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
let out = Fetch2ToFetch3 {
|
||||
pc: in.pc,
|
||||
phys_pc: phys_pc,
|
||||
pred_next_pc: in.pred_next_pc,
|
||||
cause: cause,
|
||||
access_mmio: access_mmio,
|
||||
decode_epoch: in.decode_epoch,
|
||||
main_epoch: in.main_epoch };
|
||||
f22f3.enq(tuple2(nbSup,out));
|
||||
if (verbose) $display("Fetch2: ", fshow(out));
|
||||
endrule
|
||||
|
||||
// Break out of i$
|
||||
rule doFetch3;
|
||||
let {nbSup, fetch3In} = f22f3.first;
|
||||
f22f3.deq();
|
||||
if (verbose) $display("Fetch3 %d",fetch3In.pc);
|
||||
|
||||
// Get ICache/MMIO response if no exception
|
||||
// In case of exception, we still need to process at least inst_data[0]
|
||||
// (it will be turned to an exception later), so inst_data[0] must be
|
||||
// valid.
|
||||
Vector#(SupSize,Maybe#(Instruction)) inst_d = replicate(tagged Valid (0));
|
||||
if(!isValid(fetch3In.cause)) begin
|
||||
if(fetch3In.access_mmio) begin
|
||||
if(verbose) $display("get answer from MMIO %d", fetch3In.pc);
|
||||
inst_d <- mmio.bootRomResp;
|
||||
end
|
||||
else begin
|
||||
if(verbose) $display("get answer from memory %d", fetch3In.pc);
|
||||
inst_d <- mem_server.response.get;
|
||||
end
|
||||
end
|
||||
if(verbose) $display("epoch instr: %d, epoch main : %d", fetch3In.main_epoch, f_main_epoch);
|
||||
instdata.enq(inst_d);
|
||||
f32d.enq(f22f3.first);
|
||||
endrule
|
||||
|
||||
rule doDecode;
|
||||
let {nbSup, fetch3In} = f32d.first;
|
||||
f32d.deq();
|
||||
let inst_data = instdata.first();
|
||||
instdata.deq();
|
||||
// The main_epoch check is required to make sure this stage doesn't
|
||||
// redirect the PC if a later stage already redirected the PC.
|
||||
if (fetch3In.main_epoch == f_main_epoch) begin
|
||||
Bool decode_epoch_local = decode_epoch; // next value for decode epoch
|
||||
Maybe#(Addr) redirectPc = Invalid; // next pc redirect by branch predictor
|
||||
Maybe#(TrainNAP) trainNAP = Invalid; // training data sent to next addr pred
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: inst being redirect by decode stage
|
||||
// Note that only 1 redirection may happen in a cycle
|
||||
Maybe#(IType) redirectInst = Invalid;
|
||||
`endif
|
||||
|
||||
for (Integer i = 0; i < valueof(SupSize); i=i+1) begin
|
||||
if (inst_data[i] != tagged Invalid && fromInteger(i) <= nbSup) begin
|
||||
// get the input to decode
|
||||
let in = Fetch3ToDecode {
|
||||
pc: fetch3In.pc+fromInteger(4*i),
|
||||
// last inst, next pc may not be pc+4
|
||||
ppc: fromInteger(i) == nbSup ? fetch3In.pred_next_pc :
|
||||
fetch3In.pc+fromInteger(4*(i+1)),
|
||||
decode_epoch: fetch3In.decode_epoch,
|
||||
main_epoch: fetch3In.main_epoch,
|
||||
inst: fromMaybe(?,inst_data[i]),
|
||||
cause: fetch3In.cause
|
||||
};
|
||||
let cause = in.cause;
|
||||
if (verbose) $display("Decode %d\n",i);
|
||||
|
||||
// do decode and branch prediction
|
||||
// Drop here if does not match the decode_epoch.
|
||||
if (in.decode_epoch == decode_epoch_local) begin
|
||||
doAssert(in.main_epoch == f_main_epoch, "main epoch must match");
|
||||
|
||||
let decode_result = decode(in.inst);
|
||||
|
||||
// update cause if there was not an early detected exception
|
||||
if (!isValid(cause)) begin
|
||||
cause = decode_result.illegalInst ? tagged Valid IllegalInst : tagged Invalid;
|
||||
end
|
||||
|
||||
let dInst = decode_result.dInst;
|
||||
let regs = decode_result.regs;
|
||||
DirPredTrainInfo dp_train = ?; // dir pred training bookkeeping
|
||||
|
||||
// update predicted next pc
|
||||
if (!isValid(cause)) begin
|
||||
// direction predict
|
||||
Bool pred_taken = False;
|
||||
if(dInst.iType == Br) begin
|
||||
let pred_res <- dirPred.pred[i].pred(in.pc);
|
||||
pred_taken = pred_res.taken;
|
||||
dp_train = pred_res.train;
|
||||
end
|
||||
Maybe#(Addr) nextPc = decodeBrPred(in.pc, dInst, pred_taken);
|
||||
|
||||
// return address stack link reg is x1 or x5
|
||||
function Bool linkedR(Maybe#(ArchRIndx) register);
|
||||
Bool res = False;
|
||||
if (register matches tagged Valid .r &&& (r == tagged Gpr 1 || r == tagged Gpr 5)) begin
|
||||
res = True;
|
||||
end
|
||||
return res;
|
||||
endfunction
|
||||
Bool dst_link = linkedR(regs.dst);
|
||||
Bool src1_link = linkedR(regs.src1);
|
||||
Addr push_addr = in.pc + 4;
|
||||
Addr pop_addr = ras.ras[i].first;
|
||||
if (dInst.iType == J && dst_link) begin
|
||||
// rs1 is invalid, i.e., not link: push
|
||||
ras.ras[i].popPush(False, Valid (push_addr));
|
||||
end
|
||||
else if (dInst.iType == Jr) begin // jalr
|
||||
if (!dst_link && src1_link) begin
|
||||
// rd is link while rs1 is not: pop
|
||||
nextPc = Valid (pop_addr);
|
||||
ras.ras[i].popPush(True, Invalid);
|
||||
end
|
||||
else if (!src1_link && dst_link) begin
|
||||
// rs1 is not link while rd is link: push
|
||||
ras.ras[i].popPush(False, Valid (push_addr));
|
||||
end
|
||||
else if (dst_link && src1_link) begin
|
||||
// both rd and rs1 are links
|
||||
if (regs.src1 != regs.dst) begin
|
||||
// not same reg: first pop, then push
|
||||
nextPc = Valid (pop_addr);
|
||||
ras.ras[i].popPush(True, Valid (push_addr));
|
||||
end
|
||||
else begin
|
||||
// same reg: push
|
||||
ras.ras[i].popPush(False, Valid (push_addr));
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if(verbose) begin
|
||||
$display("Branch prediction: ", fshow(dInst.iType), " ; ", fshow(in.pc), " ; ",
|
||||
fshow(in.ppc), " ; ", fshow(pred_taken), " ; ", fshow(nextPc));
|
||||
end
|
||||
|
||||
// check previous mispred
|
||||
if (nextPc matches tagged Valid .decode_pred_next_pc &&& decode_pred_next_pc != in.ppc) begin
|
||||
if (verbose) $display("ppc and decodeppc : %h %h", in.ppc, decode_pred_next_pc);
|
||||
decode_epoch_local = !decode_epoch_local;
|
||||
redirectPc = Valid (decode_pred_next_pc); // record redirect next pc
|
||||
in.ppc = decode_pred_next_pc;
|
||||
// train next addr pred when mispredict
|
||||
trainNAP = Valid (TrainNAP {pc: in.pc, nextPc: decode_pred_next_pc});
|
||||
`ifdef PERF_COUNT
|
||||
// performance stats: record decode redirect
|
||||
doAssert(redirectInst == Invalid, "at most 1 decode redirect per cycle");
|
||||
redirectInst = Valid (dInst.iType);
|
||||
`endif
|
||||
end
|
||||
end
|
||||
let out = FromFetchStage{pc: in.pc,
|
||||
ppc: in.ppc,
|
||||
main_epoch: in.main_epoch,
|
||||
dpTrain: dp_train,
|
||||
inst: in.inst,
|
||||
dInst: dInst,
|
||||
regs: decode_result.regs,
|
||||
cause: cause };
|
||||
out_fifo.enqS[i].enq(out);
|
||||
if (verbose) $display("Decode: ", fshow(out));
|
||||
end
|
||||
else begin
|
||||
if (verbose) $display("Drop decoded within a superscalar");
|
||||
// just drop wrong path instructions
|
||||
end
|
||||
end
|
||||
else if (inst_data[i] == tagged Invalid && fromInteger(i) <= nbSup) begin
|
||||
// inst num is less than expected; this should not happen
|
||||
// because both I$ and boot rom are aligned to cache line
|
||||
// size.
|
||||
doAssert(False, "Fetched insts not enough");
|
||||
end
|
||||
end
|
||||
|
||||
// update PC and epoch
|
||||
if(redirectPc matches tagged Valid .nextPc) begin
|
||||
pc_reg[pc_decode_port] <= nextPc;
|
||||
end
|
||||
decode_epoch <= decode_epoch_local;
|
||||
// send training data for next addr pred
|
||||
if (trainNAP matches tagged Valid .x) begin
|
||||
napTrainByDecQ.enq(x);
|
||||
end
|
||||
`ifdef PERF_COUNT
|
||||
// performance counter: check whether redirect happens
|
||||
if(redirectInst matches tagged Valid .iType &&& doStats) begin
|
||||
case(iType)
|
||||
Br: decRedirectBrCnt.incr(1);
|
||||
J : decRedirectJmpCnt.incr(1);
|
||||
Jr: decRedirectJrCnt.incr(1);
|
||||
default: decRedirectOtherCnt.incr(1);
|
||||
endcase
|
||||
end
|
||||
`endif
|
||||
end
|
||||
else begin
|
||||
if (verbose) $display("drop in fetch3decode");
|
||||
end
|
||||
endrule
|
||||
|
||||
// train next addr pred: we use a wire to catch outputs of napTrainByDecQ.
|
||||
// This prevents napTrainByDecQ from clogging doDecode rule when
|
||||
// superscalar size is large
|
||||
(* fire_when_enabled *)
|
||||
rule setTrainNAPByDec;
|
||||
napTrainByDecQ.deq;
|
||||
napTrainByDec.wset(napTrainByDecQ.first);
|
||||
endrule
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule doTrainNAP(isValid(napTrainByDec.wget) || isValid(napTrainByExe.wget));
|
||||
// Give priority to train from exe. This is because exe has train data
|
||||
// only when misprediction happens, i.e., train by dec is already at
|
||||
// wrong path.
|
||||
TrainNAP train = fromMaybe(validValue(napTrainByDec.wget), napTrainByExe.wget);
|
||||
nextAddrPred.update(train.pc, train.nextPc, train.nextPc != train.pc + 4);
|
||||
endrule
|
||||
|
||||
// Security: we can flush when front end is empty, i.e.
|
||||
// (1) Fetch1 is stalled for waiting flush
|
||||
// (2) all internal FIFOs are empty (the output sup fifo needs not to be
|
||||
// empty, but why leave this security hole)
|
||||
Bool empty_for_flush = waitForFlush &&
|
||||
!f12f2.notEmpty && !f22f3.notEmpty &&
|
||||
!f32d.notEmpty && out_fifo.internalEmpty;
|
||||
|
||||
interface Vector pipelines = out_fifo.deqS;
|
||||
interface iTlbIfc = iTlb;
|
||||
interface iMemIfc = iMem;
|
||||
interface mmioIfc = mmio.toCore;
|
||||
|
||||
method Action start(Addr start_pc);
|
||||
pc_reg[0] <= start_pc;
|
||||
started <= True;
|
||||
waitForRedirect <= False;
|
||||
waitForFlush <= False;
|
||||
endmethod
|
||||
method Action stop();
|
||||
started <= False;
|
||||
endmethod
|
||||
|
||||
method Action setWaitRedirect;
|
||||
waitForRedirect <= True;
|
||||
setWaitRedirect_redirect_conflict.wset(?); // conflict with redirect
|
||||
endmethod
|
||||
method Action redirect(Addr new_pc);
|
||||
if (verbose) $display("Redirect: newpc %h, old f_main_epoch %d, new f_main_epoch %d",new_pc,f_main_epoch,f_main_epoch+1);
|
||||
pc_reg[pc_redirect_port] <= new_pc;
|
||||
f_main_epoch <= (f_main_epoch == fromInteger(valueOf(NumEpochs)-1)) ? 0 : f_main_epoch + 1;
|
||||
// redirect comes, stop stalling for redirect
|
||||
waitForRedirect <= False;
|
||||
setWaitRedirect_redirect_conflict.wset(?); // conflict with setWaitForRedirect
|
||||
// this redirect may be caused by a trap/system inst in commit stage
|
||||
// we conservatively set wait for flush TODO make this an input parameter
|
||||
waitForFlush <= True;
|
||||
endmethod
|
||||
method Action done_flushing() if (waitForFlush);
|
||||
// signal that the pipeline can resume fetching
|
||||
waitForFlush <= False;
|
||||
// XXX The guard prevents the readyToFetch rule in Core.bsv from firing every cycle
|
||||
// The guard also makes this method sequence before (restricted) redirect method
|
||||
// So the effect of setting waitForFlush in redirect method will not be overwritten
|
||||
// Then we don't need to make two methods conflict
|
||||
// It's fine for the effect of this method to be overwritten, because it fires very often
|
||||
endmethod
|
||||
|
||||
method Action train_predictors(
|
||||
Addr pc, Addr next_pc, IType iType, Bool taken,
|
||||
DirPredTrainInfo dpTrain, Bool mispred
|
||||
);
|
||||
//if (iType == J || (iType == Br && next_pc < pc)) begin
|
||||
// // Only train the next address predictor for jumps and backward branches
|
||||
// // next_pc != pc + 4 is a substitute for taken
|
||||
// nextAddrPred.update(pc, next_pc, taken);
|
||||
//end
|
||||
if (iType == Br) begin
|
||||
// Train the direction predictor for all branches
|
||||
dirPred.update(pc, taken, dpTrain, mispred);
|
||||
end
|
||||
// train next addr pred when mispred
|
||||
if(mispred) begin
|
||||
napTrainByExe.wset(TrainNAP {pc: pc, nextPc: next_pc});
|
||||
end
|
||||
endmethod
|
||||
|
||||
// security
|
||||
method Bool emptyForFlush;
|
||||
return empty_for_flush;
|
||||
endmethod
|
||||
|
||||
method Action flush_predictors;
|
||||
nextAddrPred.flush;
|
||||
dirPred.flush;
|
||||
ras.flush;
|
||||
endmethod
|
||||
|
||||
method Bool flush_predictors_done;
|
||||
return nextAddrPred.flush_done && dirPred.flush_done && ras.flush_done;
|
||||
endmethod
|
||||
|
||||
method FetchDebugState getFetchState;
|
||||
return FetchDebugState {
|
||||
pc: pc_reg[0],
|
||||
waitForRedirect: waitForRedirect,
|
||||
waitForFlush: waitForFlush,
|
||||
mainEp: f_main_epoch
|
||||
};
|
||||
endmethod
|
||||
|
||||
interface Perf perf;
|
||||
method Action setStatus(Bool stats);
|
||||
`ifdef PERF_COUNT
|
||||
doStats <= stats;
|
||||
`else
|
||||
noAction;
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method Action req(DecStagePerfType r);
|
||||
perfReqQ.enq(r);
|
||||
endmethod
|
||||
|
||||
method ActionValue#(PerfResp#(DecStagePerfType)) resp;
|
||||
`ifdef PERF_COUNT
|
||||
perfRespQ.deq;
|
||||
return perfRespQ.first;
|
||||
`else
|
||||
perfReqQ.deq;
|
||||
return PerfResp {
|
||||
pType: perfReqQ.first,
|
||||
data: 0
|
||||
};
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
method Bool respValid = perfRespQ.notEmpty;
|
||||
`else
|
||||
method Bool respValid = perfReqQ.notEmpty;
|
||||
`endif
|
||||
endinterface
|
||||
endmodule
|
||||
|
||||
325
src_Core/RISCY_OOO/procs/RV64G_OOO/FpuMulDivExePipeline.bsv
Normal file
325
src_Core/RISCY_OOO/procs/RV64G_OOO/FpuMulDivExePipeline.bsv
Normal file
@@ -0,0 +1,325 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import DefaultValue::*;
|
||||
import Cntrs::*;
|
||||
import Vector::*;
|
||||
import BuildVector::*;
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import SynthParam::*;
|
||||
import Exec::*;
|
||||
import Performance::*;
|
||||
import ReservationStationEhr::*;
|
||||
import ReservationStationFpuMulDiv::*;
|
||||
import ReorderBuffer::*;
|
||||
import HasSpecBits::*;
|
||||
import SpecFifo::*;
|
||||
import MulDiv::*;
|
||||
import Fpu::*;
|
||||
import Bypass::*;
|
||||
|
||||
typedef struct {
|
||||
// inst info
|
||||
ExecFunc execFunc;
|
||||
PhyRegs regs;
|
||||
InstTag tag;
|
||||
// FpuMulDiv must not have valid spec tag
|
||||
} FpuMulDivDispatchToRegRead deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
// inst info
|
||||
ExecFunc execFunc;
|
||||
Maybe#(PhyDst) dst;
|
||||
InstTag tag;
|
||||
// src reg vals
|
||||
Data rVal1;
|
||||
Data rVal2;
|
||||
Data rVal3;
|
||||
} FpuMulDivRegReadToExe deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
// inst info
|
||||
ExecFunc execFunc;
|
||||
Maybe#(PhyDst) dst;
|
||||
InstTag tag;
|
||||
} FpuMulDivExeToFinish deriving(Bits, Eq, FShow);
|
||||
|
||||
// synthesized pipeline fifos
|
||||
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, FpuMulDivDispatchToRegRead) FpuMulDivDispToRegFifo;
|
||||
(* synthesize *)
|
||||
module mkFpuMulDivDispToRegFifo(FpuMulDivDispToRegFifo);
|
||||
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
typedef SpecFifo_SB_deq_enq_C_deq_enq#(1, FpuMulDivRegReadToExe) FpuMulDivRegToExeFifo;
|
||||
(* synthesize *)
|
||||
module mkFpuMulDivRegToExeFifo(FpuMulDivRegToExeFifo);
|
||||
let m <- mkSpecFifo_SB_deq_enq_C_deq_enq(False);
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
interface FpuMulDivExeInput;
|
||||
// conservative scoreboard check in reg read stage
|
||||
method RegsReady sbCons_lazyLookup(PhyRegs r);
|
||||
// Phys reg file
|
||||
method Data rf_rd1(PhyRIndx rindx);
|
||||
method Data rf_rd2(PhyRIndx rindx);
|
||||
method Data rf_rd3(PhyRIndx rindx);
|
||||
// CSR file
|
||||
method Data csrf_rd(CSR csr);
|
||||
// ROB
|
||||
method Action rob_setExecuted(InstTag t, Bit#(5) fflags);
|
||||
|
||||
// global broadcast methods
|
||||
// write reg file & set both conservative and aggressive sb & wake up inst
|
||||
method Action writeRegFile(PhyRIndx dst, Data data);
|
||||
// spec update
|
||||
method Action conflictWrongSpec;
|
||||
// performance
|
||||
method Bool doStats;
|
||||
endinterface
|
||||
|
||||
interface FpuMulDivExePipeline;
|
||||
// recv bypass from the ALU exe and finish stages
|
||||
interface Vector#(TMul#(2, AluExeNum), RecvBypass) recvBypass;
|
||||
interface ReservationStationFpuMulDiv rsFpuMulDivIfc;
|
||||
interface SpeculationUpdate specUpdate;
|
||||
// performance
|
||||
method Data getPerf(ExeStagePerfType t);
|
||||
endinterface
|
||||
|
||||
module mkFpuMulDivExePipeline#(FpuMulDivExeInput inIfc)(FpuMulDivExePipeline);
|
||||
Bool verbose = True;
|
||||
|
||||
// fpu mul div reservation station
|
||||
ReservationStationFpuMulDiv rsFpuMulDiv <- mkReservationStationFpuMulDiv;
|
||||
|
||||
// pipeline fifos
|
||||
let dispToRegQ <- mkFpuMulDivDispToRegFifo;
|
||||
let regToExeQ <- mkFpuMulDivRegToExeFifo;
|
||||
|
||||
// wire to recv bypass
|
||||
Vector#(TMul#(2, AluExeNum), RWire#(Tuple2#(PhyRIndx, Data))) bypassWire <- replicateM(mkRWire);
|
||||
|
||||
// mul div fpu func units
|
||||
MulDivExec mulDivExec <- mkMulDivExec;
|
||||
FpuExec fpuExec <- mkFpuExecPipeline;
|
||||
|
||||
// fpu/mul/div performance counters
|
||||
`ifdef PERF_COUNT
|
||||
Count#(Data) exeIntMulCnt <- mkCount(0);
|
||||
Count#(Data) exeIntDivCnt <- mkCount(0);
|
||||
Count#(Data) exeFpFmaCnt <- mkCount(0);
|
||||
Count#(Data) exeFpDivCnt <- mkCount(0);
|
||||
Count#(Data) exeFpSqrtCnt <- mkCount(0);
|
||||
`endif
|
||||
|
||||
rule doDispatchFpuMulDiv;
|
||||
rsFpuMulDiv.doDispatch;
|
||||
let x = rsFpuMulDiv.dispatchData;
|
||||
if(verbose) $display("[doDispatchFpuMulDiv] ", fshow(x));
|
||||
|
||||
// FPU MUL DIV never have exception or misprecition, so no spec tag
|
||||
doAssert(!isValid(x.spec_tag), "FpuMulDiv should not carry any spec tag");
|
||||
|
||||
// go to next stage
|
||||
dispToRegQ.enq(ToSpecFifo {
|
||||
data: FpuMulDivDispatchToRegRead {
|
||||
execFunc: x.data.execFunc,
|
||||
regs: x.regs,
|
||||
tag: x.tag
|
||||
},
|
||||
spec_bits: x.spec_bits
|
||||
});
|
||||
endrule
|
||||
|
||||
rule doRegReadFpuMulDiv;
|
||||
dispToRegQ.deq;
|
||||
let dispToReg = dispToRegQ.first;
|
||||
let x = dispToReg.data;
|
||||
if(verbose) $display("[doRegReadFpuMulDiv] ", fshow(dispToReg));
|
||||
|
||||
// check conservative scoreboard
|
||||
let regsReady = inIfc.sbCons_lazyLookup(x.regs);
|
||||
|
||||
// get rVal1 (check bypass)
|
||||
Data rVal1 = ?;
|
||||
if(x.regs.src1 matches tagged Valid .src1) begin
|
||||
rVal1 <- readRFBypass(src1, regsReady.src1, inIfc.rf_rd1(src1), bypassWire);
|
||||
end
|
||||
|
||||
// get rVal2 (check bypass)
|
||||
Data rVal2 = ?;
|
||||
if(x.regs.src2 matches tagged Valid .src2) begin
|
||||
rVal2 <- readRFBypass(src2, regsReady.src2, inIfc.rf_rd2(src2), bypassWire);
|
||||
end
|
||||
|
||||
// get rVal3 (check bypass)
|
||||
Data rVal3 = ?;
|
||||
if(x.regs.src3 matches tagged Valid .src3) begin
|
||||
rVal3 <- readRFBypass(src3, regsReady.src3, inIfc.rf_rd3(src3), bypassWire);
|
||||
end
|
||||
|
||||
// go to next stage
|
||||
regToExeQ.enq(ToSpecFifo {
|
||||
data: FpuMulDivRegReadToExe {
|
||||
execFunc: x.execFunc,
|
||||
dst: x.regs.dst,
|
||||
tag: x.tag,
|
||||
rVal1: rVal1,
|
||||
rVal2: rVal2,
|
||||
rVal3: rVal3
|
||||
},
|
||||
spec_bits: dispToReg.spec_bits
|
||||
});
|
||||
endrule
|
||||
|
||||
rule doExeFpuMulDiv;
|
||||
regToExeQ.deq;
|
||||
let regToExe = regToExeQ.first;
|
||||
let x = regToExe.data;
|
||||
let spec_bits = regToExe.spec_bits;
|
||||
if(verbose) $display("[doExeFpuMulDiv] ", fshow(regToExe));
|
||||
|
||||
// send to exe unit
|
||||
Data rVal1 = x.rVal1;
|
||||
Data rVal2 = x.rVal2;
|
||||
Data rVal3 = x.rVal3;
|
||||
case (x.execFunc) matches
|
||||
tagged Fpu .fpu_inst: begin
|
||||
fpuExec.exec(fpu_inst, rVal1, rVal2, rVal3, x.dst, x.tag, spec_bits);
|
||||
end
|
||||
tagged MulDiv .muldiv_inst: begin
|
||||
mulDivExec.exec(muldiv_inst, rVal1, rVal2, x.dst, x.tag, spec_bits);
|
||||
end
|
||||
default: begin
|
||||
doAssert(False, "unknown execFunc for doExeFpuMulDiv");
|
||||
end
|
||||
endcase
|
||||
endrule
|
||||
|
||||
function Action doFinish(Maybe#(PhyDst) dst, InstTag tag, Data data, Bit#(5) fflags);
|
||||
action
|
||||
// write to register file
|
||||
if(dst matches tagged Valid .valid_dst) begin
|
||||
inIfc.writeRegFile(valid_dst.indx, data);
|
||||
end
|
||||
// update the instruction in the reorder buffer.
|
||||
inIfc.rob_setExecuted(tag, fflags);
|
||||
// since FPU op has no spec tag, this doFinish rule is ordered before
|
||||
// other rules that calls incorrectSpec, and BSV compiler creates
|
||||
// cycles in scheduling. We manually creates a conflict between this
|
||||
// rule and incorrectSpec to break the cycle
|
||||
//inIfc.conflictWrongSpec;
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
rule doFinishFpSimple;
|
||||
FpuResp resp <- fpuExec.simpleResp;
|
||||
if(verbose) $display("[doFinishFpSimple] ", fshow(resp));
|
||||
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
|
||||
endrule
|
||||
|
||||
rule doFinishFpFma;
|
||||
FpuResp resp <- fpuExec.fmaResp;
|
||||
if(verbose) $display("[doFinishFpFma] ", fshow(resp));
|
||||
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
exeFpFmaCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
endrule
|
||||
|
||||
rule doFinishFpDiv;
|
||||
FpuResp resp <- fpuExec.divResp;
|
||||
if(verbose) $display("[doFinishFpDiv] ", fshow(resp));
|
||||
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
exeFpDivCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
endrule
|
||||
|
||||
rule doFinishFpSqrt;
|
||||
FpuResp resp <- fpuExec.sqrtResp;
|
||||
if(verbose) $display("[doFinishFpSqrt] ", fshow(resp));
|
||||
doFinish(resp.dst, resp.tag, resp.res.data, resp.res.fflags);
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
exeFpSqrtCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
endrule
|
||||
|
||||
rule doFinishIntMul;
|
||||
MulDivResp resp <- mulDivExec.mulResp;
|
||||
if(verbose) $display("[doFinishIntMul] ", fshow(resp));
|
||||
doFinish(resp.dst, resp.tag, resp.data, 0);
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
exeIntMulCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
endrule
|
||||
|
||||
rule doFinishIntDiv;
|
||||
MulDivResp resp <- mulDivExec.divResp;
|
||||
if(verbose) $display("[doFinishIntDiv] ", fshow(resp));
|
||||
doFinish(resp.dst, resp.tag, resp.data, 0);
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
exeIntDivCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
endrule
|
||||
|
||||
interface recvBypass = map(getRecvBypassIfc, bypassWire);
|
||||
|
||||
interface rsFpuMulDivIfc = rsFpuMulDiv;
|
||||
|
||||
interface specUpdate = joinSpeculationUpdate(vec(
|
||||
rsFpuMulDiv.specUpdate,
|
||||
dispToRegQ.specUpdate,
|
||||
regToExeQ.specUpdate,
|
||||
fpuExec.specUpdate,
|
||||
mulDivExec.specUpdate
|
||||
));
|
||||
|
||||
method Data getPerf(ExeStagePerfType t);
|
||||
return (case(t)
|
||||
`ifdef PERF_COUNT
|
||||
ExeIntMulCnt: exeIntMulCnt;
|
||||
ExeIntDivCnt: exeIntDivCnt;
|
||||
ExeFpFmaCnt: exeFpFmaCnt;
|
||||
ExeFpDivCnt: exeFpDivCnt;
|
||||
ExeFpSqrtCnt: exeFpSqrtCnt;
|
||||
`endif
|
||||
default: 0;
|
||||
endcase);
|
||||
endmethod
|
||||
endmodule
|
||||
1289
src_Core/RISCY_OOO/procs/RV64G_OOO/MemExePipeline.bsv
Normal file
1289
src_Core/RISCY_OOO/procs/RV64G_OOO/MemExePipeline.bsv
Normal file
File diff suppressed because it is too large
Load Diff
326
src_Core/RISCY_OOO/procs/RV64G_OOO/ProcConfig.bsv
Normal file
326
src_Core/RISCY_OOO/procs/RV64G_OOO/ProcConfig.bsv
Normal file
@@ -0,0 +1,326 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
// Core size and cache size are controlled by macros CORE_XXX and CACHE_XXX. In
|
||||
// MICRO 2018 paper, configuration BASE-T is defining CORE_SMALL and
|
||||
// CACHE_LARGE, and configuration BASE-R is defining CORE_MEDIUM and
|
||||
// CACHE_LARGE. Due to resource restrictions, 4-core multiprocessor on AWS is
|
||||
// defining CORE_TINY and CACHE_MC.
|
||||
|
||||
//
|
||||
// ==== common parameters ====
|
||||
//
|
||||
|
||||
`define rv64 True
|
||||
`define m True
|
||||
`define a True
|
||||
`define f True
|
||||
`define d True
|
||||
|
||||
//`define NUM_CORES 1 // defined in make file
|
||||
|
||||
//`define PERF_COUNT // defined in makefile
|
||||
|
||||
`define REUSE_FMA // use FMA for add and mul
|
||||
|
||||
`define LOG_BOOT_ROM_BYTES 12 // 4KB boot rom
|
||||
|
||||
// tournament predictor, other options are: BHT, TOUR, GSELECT, GSHARE. NOTE
|
||||
// that the predictors are of different size.
|
||||
`define DIR_PRED_TOUR
|
||||
|
||||
`define LOG_DEADLOCK_CYCLES 26 // 64M cycles for deadlock detection
|
||||
|
||||
// Be lazy in reservation station wake and phy reg file, and enqs. LSQ is by
|
||||
// default lazy. 1-elem spec FIFOs (pipeline stage regs) are by default not
|
||||
// lazy.
|
||||
`define LAZY_RS_RF True
|
||||
`define RS_LAZY_ENQ True
|
||||
`define ROB_LAZY_ENQ True
|
||||
|
||||
`define L1_TLB_SIZE 32 // L1 fully assoc TLB size
|
||||
|
||||
`define L2_TLB_HUGE_SIZE 8 // L2 2MB/1GB TLB size
|
||||
`define LOG_L2_TLB_4KB_SIZE 10 // L2 4KB TLB log size (1024 entries)
|
||||
`define LOG_L2_TLB_4KB_WAYS 2 // L2 4KB TLB log ways (4 ways)
|
||||
|
||||
// FMA bookkeeping FIFO: add 1 to allow simultaneous enq/deq
|
||||
`define BOOKKEEPING_FP_FMA_SIZE TAdd#(`XILINX_FP_FMA_LATENCY, 1)
|
||||
// INT MUL bookkeeping FIFO: add 1 to allow simultaneous enq/deq, another 1
|
||||
// because of internal flow control in MUL unit
|
||||
`define BOOKKEEPING_INT_MUL_SIZE TAdd#(`XILINX_INT_MUL_LATENCY, 2)
|
||||
|
||||
// non-blocking DTLB
|
||||
`define DTLB_REQ_NUM 4
|
||||
// non-blocking L2 TLB
|
||||
`define L2TLB_REQ_NUM 2
|
||||
|
||||
`define DRAM_MAX_READS TExp#(`LOG_LLC_WAYS) // max reads in DRAM, match LLC ways
|
||||
`define DRAM_MAX_WRITES 16 // write buffer size in AWS DRAM controller
|
||||
`define DRAM_MAX_REQS 24
|
||||
`define DRAM_LATENCY 120 // model a constant dram latency
|
||||
|
||||
`ifdef SECURITY
|
||||
`define LOG_DRAM_REGION_NUM 6 // 64 DRAM regions
|
||||
`define LOG_DRAM_REGION_SIZE 25 // 32MB for each DRAM region
|
||||
`endif
|
||||
|
||||
//
|
||||
// ==== CACHE SIZE ====
|
||||
//
|
||||
|
||||
`ifdef CACHE_SMALL
|
||||
|
||||
// L1
|
||||
`define LOG_L1_LINES 8 // 16KB
|
||||
`define LOG_L1_WAYS 3 // 8 ways
|
||||
|
||||
// LLC
|
||||
`define LOG_LLC_LINES 12 // 256KB
|
||||
`define LOG_LLC_WAYS 4 // 16 ways
|
||||
|
||||
`endif
|
||||
|
||||
`ifdef CACHE_LARGE
|
||||
|
||||
// L1
|
||||
`define LOG_L1_LINES 9 // 32KB
|
||||
`define LOG_L1_WAYS 3 // 8 ways
|
||||
|
||||
// LLC
|
||||
`define LOG_LLC_LINES 14 // 1MB
|
||||
`define LOG_LLC_WAYS 4 // 16 ways
|
||||
|
||||
`endif
|
||||
|
||||
`ifdef CACHE_MC_1MB
|
||||
|
||||
// L1
|
||||
`define LOG_L1_LINES 9 // 32KB
|
||||
`define LOG_L1_WAYS 2 // 4 ways
|
||||
|
||||
// LLC
|
||||
`define LOG_LLC_LINES 14 // 1MB
|
||||
`define LOG_LLC_WAYS 4 // 16 ways
|
||||
|
||||
`endif
|
||||
|
||||
`ifdef CACHE_MC_2MB
|
||||
|
||||
// L1
|
||||
`define LOG_L1_LINES 9 // 32KB
|
||||
`define LOG_L1_WAYS 2 // 4 ways
|
||||
|
||||
// LLC
|
||||
`define LOG_LLC_LINES 15 // 2MB
|
||||
`define LOG_LLC_WAYS 4 // 16 ways
|
||||
|
||||
`endif
|
||||
|
||||
//
|
||||
// ==== CORE SIZE ====
|
||||
//
|
||||
|
||||
`ifdef CORE_TINY
|
||||
|
||||
// superscalar
|
||||
`define sizeSup 2
|
||||
|
||||
// ROB
|
||||
`define ROB_SIZE 48
|
||||
|
||||
// speculation
|
||||
`define NUM_EPOCHS 8
|
||||
`define NUM_SPEC_TAGS 8
|
||||
|
||||
// Smaller L1 TLB
|
||||
`undef TLB_SIZE
|
||||
`define TLB_SIZE 16
|
||||
|
||||
// LSQ
|
||||
`define LDQ_SIZE 18
|
||||
`define STQ_SIZE 10
|
||||
`define SB_SIZE 2
|
||||
|
||||
// reservation station sizes
|
||||
`define RS_ALU_SIZE 10
|
||||
`define RS_MEM_SIZE 10
|
||||
`define RS_FPUMULDIV_SIZE 10
|
||||
|
||||
`endif
|
||||
|
||||
`ifdef CORE_SMALL
|
||||
|
||||
// superscalar
|
||||
`define sizeSup 2
|
||||
|
||||
// ROB
|
||||
`define ROB_SIZE 64
|
||||
|
||||
// speculation
|
||||
`define NUM_EPOCHS 12
|
||||
`define NUM_SPEC_TAGS 12
|
||||
|
||||
// LSQ
|
||||
`define LDQ_SIZE 24
|
||||
`define STQ_SIZE 14
|
||||
`define SB_SIZE 4
|
||||
|
||||
// reservation station sizes
|
||||
`define RS_ALU_SIZE 16
|
||||
`define RS_MEM_SIZE 16
|
||||
`define RS_FPUMULDIV_SIZE 16
|
||||
|
||||
`endif
|
||||
|
||||
`ifdef CORE_MEDIUM
|
||||
|
||||
// superscalar
|
||||
`define sizeSup 2
|
||||
|
||||
// ROB
|
||||
`define ROB_SIZE 80
|
||||
|
||||
// speculation
|
||||
`define NUM_EPOCHS 12
|
||||
`define NUM_SPEC_TAGS 12
|
||||
|
||||
// LSQ
|
||||
`define LDQ_SIZE 24
|
||||
`define STQ_SIZE 14
|
||||
`define SB_SIZE 4
|
||||
|
||||
// reservation station sizes
|
||||
`define RS_ALU_SIZE 16
|
||||
`define RS_MEM_SIZE 16
|
||||
`define RS_FPUMULDIV_SIZE 16
|
||||
|
||||
`endif
|
||||
|
||||
`ifdef CORE_SMALL_WIDE
|
||||
|
||||
// superscalar
|
||||
`define sizeSup 4
|
||||
|
||||
// ROB
|
||||
`define ROB_SIZE 64
|
||||
|
||||
// speculation
|
||||
`define NUM_EPOCHS 16
|
||||
`define NUM_SPEC_TAGS 16
|
||||
|
||||
// LSQ
|
||||
`define LDQ_SIZE 24
|
||||
`define STQ_SIZE 14
|
||||
`define SB_SIZE 4
|
||||
|
||||
// reservation station sizes
|
||||
`define RS_ALU_SIZE 8
|
||||
`define RS_MEM_SIZE 8
|
||||
`define RS_FPUMULDIV_SIZE 16
|
||||
|
||||
`endif
|
||||
|
||||
`ifdef CORE_BOOM
|
||||
// we extend SMALL to match BOOM's ROB and memory latency, we also increase
|
||||
// spec tags because of increased ROB size
|
||||
|
||||
// superscalar
|
||||
`define sizeSup 2
|
||||
|
||||
// ROB
|
||||
`define ROB_SIZE 80
|
||||
|
||||
// speculation
|
||||
`define NUM_EPOCHS 12
|
||||
`define NUM_SPEC_TAGS 12
|
||||
|
||||
// LSQ
|
||||
`define LDQ_SIZE 24
|
||||
`define STQ_SIZE 14
|
||||
`define SB_SIZE 4
|
||||
|
||||
// reservation station sizes
|
||||
`define RS_ALU_SIZE 16
|
||||
`define RS_MEM_SIZE 16
|
||||
`define RS_FPUMULDIV_SIZE 16
|
||||
|
||||
// change memory latency to 80
|
||||
`undef DRAM_LATENCY
|
||||
`define DRAM_LATENCY 80
|
||||
|
||||
`endif
|
||||
|
||||
`ifdef CORE_LARGE
|
||||
|
||||
// superscalar
|
||||
`define sizeSup 2
|
||||
|
||||
// ROB
|
||||
`define ROB_SIZE 128
|
||||
|
||||
// speculation
|
||||
`define NUM_EPOCHS 16
|
||||
`define NUM_SPEC_TAGS 32
|
||||
|
||||
// LSQ
|
||||
`define LDQ_SIZE 48
|
||||
`define STQ_SIZE 28
|
||||
`define SB_SIZE 4
|
||||
|
||||
// reservation station sizes
|
||||
`define RS_ALU_SIZE 32
|
||||
`define RS_MEM_SIZE 32
|
||||
`define RS_FPUMULDIV_SIZE 32
|
||||
|
||||
`endif
|
||||
|
||||
`ifdef CORE_LARGE_WIDE
|
||||
|
||||
// superscalar
|
||||
`define sizeSup 4
|
||||
|
||||
// ROB
|
||||
`define ROB_SIZE 128
|
||||
|
||||
// speculation
|
||||
`define NUM_EPOCHS 16
|
||||
`define NUM_SPEC_TAGS 32
|
||||
|
||||
// LSQ
|
||||
`define LDQ_SIZE 48
|
||||
`define STQ_SIZE 28
|
||||
`define SB_SIZE 4
|
||||
|
||||
// reservation station sizes
|
||||
`define RS_ALU_SIZE 16
|
||||
`define RS_MEM_SIZE 16
|
||||
`define RS_FPUMULDIV_SIZE 32
|
||||
|
||||
`endif
|
||||
|
||||
//
|
||||
// ==== derived parameters ====
|
||||
//
|
||||
|
||||
|
||||
34
src_Core/RISCY_OOO/procs/RV64G_OOO/RFileSynth.bsv
Normal file
34
src_Core/RISCY_OOO/procs/RV64G_OOO/RFileSynth.bsv
Normal file
@@ -0,0 +1,34 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import PhysRFile::*;
|
||||
import SynthParam::*;
|
||||
|
||||
typedef RFile#(RFileWrPortNum, RFileRdPortNum) RFileSynth;
|
||||
|
||||
(* synthesize *)
|
||||
module mkRFileSynth(RFileSynth);
|
||||
let m <- mkRFile(`LAZY_RS_RF);
|
||||
return m;
|
||||
endmodule
|
||||
963
src_Core/RISCY_OOO/procs/RV64G_OOO/RenameStage.bsv
Normal file
963
src_Core/RISCY_OOO/procs/RV64G_OOO/RenameStage.bsv
Normal file
@@ -0,0 +1,963 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import Vector::*;
|
||||
import GetPut::*;
|
||||
import Cntrs::*;
|
||||
import Fifo::*;
|
||||
import FIFO::*;
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import CCTypes::*;
|
||||
import SynthParam::*;
|
||||
import Performance::*;
|
||||
import Exec::*;
|
||||
import FetchStage::*;
|
||||
import RenamingTable::*;
|
||||
import ReorderBuffer::*;
|
||||
import ReorderBufferSynth::*;
|
||||
import Scoreboard::*;
|
||||
import ScoreboardSynth::*;
|
||||
import CsrFile::*;
|
||||
import SpecTagManager::*;
|
||||
import EpochManager::*;
|
||||
import ReservationStationEhr::*;
|
||||
import ReservationStationAlu::*;
|
||||
import ReservationStationMem::*;
|
||||
import ReservationStationFpuMulDiv::*;
|
||||
import SplitLSQ::*;
|
||||
|
||||
typedef struct {
|
||||
FetchDebugState fetch;
|
||||
EpochDebugState epoch;
|
||||
} RenameStuck deriving(Bits, Eq, FShow);
|
||||
|
||||
interface RenameInput;
|
||||
// func units
|
||||
interface FetchStage fetchIfc; // just for debug
|
||||
interface ReorderBufferSynth robIfc;
|
||||
interface RegRenamingTable rtIfc;
|
||||
interface ScoreboardCons sbConsIfc;
|
||||
interface ScoreboardAggr sbAggrIfc;
|
||||
interface CsrFile csrfIfc;
|
||||
interface EpochManager emIfc;
|
||||
interface SpecTagManager smIfc;
|
||||
interface Vector#(AluExeNum, ReservationStationAlu) rsAluIfc;
|
||||
interface Vector#(FpuMulDivExeNum, ReservationStationFpuMulDiv) rsFpuMulDivIfc;
|
||||
interface ReservationStationMem rsMemIfc;
|
||||
interface SplitLSQ lsqIfc;
|
||||
// pending MMIO req from platform
|
||||
method Bool pendingMMIOPRq;
|
||||
// record that a CSR inst or interrupt is sent to ROB
|
||||
method Action issueCsrInstOrInterrupt;
|
||||
// deadlock check
|
||||
method Bool checkDeadlock;
|
||||
// performance
|
||||
method Bool doStats;
|
||||
endinterface
|
||||
|
||||
interface RenameStage;
|
||||
// performance count
|
||||
method Data getPerf(ExeStagePerfType t);
|
||||
// deadlock check
|
||||
interface Get#(RenameStuck) renameInstStuck;
|
||||
interface Get#(RenameStuck) renameCorrectPathStuck;
|
||||
endinterface
|
||||
|
||||
module mkRenameStage#(RenameInput inIfc)(RenameStage);
|
||||
Bool verbose = True;
|
||||
|
||||
// func units
|
||||
FetchStage fetchStage = inIfc.fetchIfc;
|
||||
ReorderBufferSynth rob = inIfc.robIfc;
|
||||
RegRenamingTable regRenamingTable = inIfc.rtIfc;
|
||||
ScoreboardCons sbCons = inIfc.sbConsIfc;
|
||||
ScoreboardAggr sbAggr = inIfc.sbAggrIfc;
|
||||
CsrFile csrf = inIfc.csrfIfc;
|
||||
EpochManager epochManager = inIfc.emIfc;
|
||||
SpecTagManager specTagManager = inIfc.smIfc;
|
||||
Vector#(AluExeNum, ReservationStationAlu) reservationStationAlu = inIfc.rsAluIfc;
|
||||
Vector#(FpuMulDivExeNum, ReservationStationFpuMulDiv) reservationStationFpuMulDiv = inIfc.rsFpuMulDivIfc;
|
||||
ReservationStationMem reservationStationMem = inIfc.rsMemIfc;
|
||||
SplitLSQ lsq = inIfc.lsqIfc;
|
||||
|
||||
// performance counter
|
||||
`ifdef PERF_COUNT
|
||||
Count#(Data) supRenameCnt <- mkCount(0);
|
||||
`ifdef SECURITY
|
||||
Count#(Data) specNoneCycles <- mkCount(0);
|
||||
Count#(Data) specNonMemCycles <- mkCount(0);
|
||||
`endif
|
||||
`endif
|
||||
|
||||
// deadlock check
|
||||
`ifdef CHECK_DEADLOCK
|
||||
// timer to check deadlock
|
||||
Reg#(DeadlockTimer) renameInstTimer <- mkReg(0);
|
||||
Reg#(DeadlockTimer) renameCorrectPathTimer <- mkReg(0);
|
||||
// FIFOs to output deadlock info
|
||||
FIFO#(RenameStuck) renameInstStuckQ <- mkFIFO1;
|
||||
FIFO#(RenameStuck) renameCorrectPathStuckQ <- mkFIFO1;
|
||||
// wires to indicate that deadlock is reported, so reset timers
|
||||
PulseWire renameInstStuckSent <- mkPulseWire;
|
||||
PulseWire renameCorrectPathStuckSent <- mkPulseWire;
|
||||
// wires to reset timers since processor is making progress
|
||||
PulseWire renameWrongPath <- mkPulseWire;
|
||||
PulseWire renameCorrectPath <- mkPulseWire;
|
||||
|
||||
let renameStuck = RenameStuck {
|
||||
fetch: fetchStage.getFetchState,
|
||||
epoch: epochManager.getEpochState
|
||||
};
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock_renameInst(inIfc.checkDeadlock && renameInstTimer == maxBound);
|
||||
renameInstStuckQ.enq(renameStuck);
|
||||
renameInstStuckSent.send;
|
||||
endrule
|
||||
|
||||
(* fire_when_enabled *)
|
||||
rule checkDeadlock_renameCorrecPath(inIfc.checkDeadlock && renameCorrectPathTimer == maxBound);
|
||||
renameCorrectPathStuckQ.enq(renameStuck);
|
||||
renameCorrectPathStuckSent.send;
|
||||
endrule
|
||||
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule incrDeadlockTimer(inIfc.checkDeadlock);
|
||||
function DeadlockTimer getNextTimer(DeadlockTimer t);
|
||||
return t == maxBound ? maxBound : t + 1;
|
||||
endfunction
|
||||
renameInstTimer <= (renameCorrectPath || renameWrongPath || renameInstStuckSent) ? 0 : getNextTimer(renameInstTimer);
|
||||
renameCorrectPathTimer <= (renameCorrectPath || renameCorrectPathStuckSent) ? 0 : getNextTimer(renameCorrectPathTimer);
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// kill wrong path inst
|
||||
// XXX we have to make this a separate rule instead of merging it with rename correct path
|
||||
// This is because the rename correct path rule is conflict with other rules that redirect
|
||||
// If wrong path inst keeps coming in, the rename rule may only kill wrong path, but blocks the redirect rule
|
||||
rule doRenaming_wrongPath(
|
||||
!epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // first wrong path, so at least kill one
|
||||
);
|
||||
// we stop when we see a correct path inst
|
||||
Bool stop = False;
|
||||
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
|
||||
if(!stop && fetchStage.pipelines[i].canDeq) begin
|
||||
let x = fetchStage.pipelines[i].first;
|
||||
if(epochManager.checkEpoch[i].check(x.main_epoch)) begin
|
||||
// correct path; stop killing
|
||||
stop = True;
|
||||
end
|
||||
else begin
|
||||
// wrong path, kill it & update prev epoch
|
||||
fetchStage.pipelines[i].deq;
|
||||
epochManager.updatePrevEpoch[i].update(x.main_epoch);
|
||||
if(verbose) $display("[doRenaming - %d] wrong path: pc = %16x", i, x.pc);
|
||||
end
|
||||
end
|
||||
end
|
||||
`ifdef CHECK_DEADLOCK
|
||||
renameWrongPath.send;
|
||||
`endif
|
||||
endrule
|
||||
|
||||
// check for exceptions and interrupts
|
||||
function Maybe#(Trap) getTrap(FromFetchStage x);
|
||||
Maybe#(Trap) trap = tagged Invalid;
|
||||
let csr_state = csrf.decodeInfo;
|
||||
let pending_interrupt = csrf.pending_interrupt;
|
||||
let new_exception = checkForException(x.dInst, x.regs, csr_state);
|
||||
if (isValid(x.cause)) begin
|
||||
// previously found exception
|
||||
trap = tagged Valid (tagged Exception fromMaybe(?, x.cause));
|
||||
end else if (isValid(pending_interrupt)) begin
|
||||
// pending interrupt
|
||||
trap = tagged Valid (tagged Interrupt fromMaybe(?, pending_interrupt));
|
||||
end else if (isValid(new_exception)) begin
|
||||
// newly found exception
|
||||
trap = tagged Valid (tagged Exception fromMaybe(?, new_exception));
|
||||
end
|
||||
return trap;
|
||||
endfunction
|
||||
|
||||
// trap for first inst to rename
|
||||
Maybe#(Trap) firstTrap = getTrap(fetchStage.pipelines[0].first);
|
||||
|
||||
// XXX Stall renaming till ROB is empty if we need to replay this inst (i.e. system inst)
|
||||
// This is a rough fix for a bug with the FPU CSR registers.
|
||||
// i.e. stall when doReplay(inst) is true
|
||||
|
||||
function Action incrEpochStallFetch;
|
||||
action
|
||||
epochManager.incrementEpoch;
|
||||
// stall fetch until redirect
|
||||
fetchStage.setWaitRedirect;
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// rename single trap
|
||||
rule doRenaming_Trap(
|
||||
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
|
||||
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
|
||||
&& isValid(firstTrap) // take trap
|
||||
&& rob.isEmpty // stall for ROB empty
|
||||
);
|
||||
fetchStage.pipelines[0].deq;
|
||||
let x = fetchStage.pipelines[0].first;
|
||||
let pc = x.pc;
|
||||
let ppc = x.ppc;
|
||||
let main_epoch = x.main_epoch;
|
||||
let dpTrain = x.dpTrain;
|
||||
let inst = x.inst;
|
||||
let dInst = x.dInst;
|
||||
let arch_regs = x.regs;
|
||||
let cause = x.cause;
|
||||
if(verbose) $display("[doRenaming] trap: ", fshow(x));
|
||||
|
||||
// update prev epoch
|
||||
epochManager.updatePrevEpoch[0].update(main_epoch);
|
||||
// Flip epoch without redirecting
|
||||
// This avoids doing incorrect work
|
||||
incrEpochStallFetch;
|
||||
// just place it in the reorder buffer
|
||||
let y = ToReorderBuffer{pc: pc,
|
||||
iType: dInst.iType,
|
||||
csr: dInst.csr,
|
||||
claimed_phy_reg: False, // no renaming is done
|
||||
trap: firstTrap,
|
||||
// default values of FullResult
|
||||
ppc_vaddr_csrData: PPC (ppc), // default use PPC
|
||||
fflags: 0,
|
||||
////////
|
||||
will_dirty_fpu_state: False,
|
||||
rob_inst_state: Executed,
|
||||
lsqTag: ?,
|
||||
ldKilled: Invalid,
|
||||
memAccessAtCommit: False,
|
||||
lsqAtCommitNotified: False,
|
||||
nonMMIOStDone: False,
|
||||
epochIncremented: True, // we have incremented epoch
|
||||
spec_bits: specTagManager.currentSpecBits
|
||||
};
|
||||
rob.enqPort[0].enq(y);
|
||||
// record if we issue an interrupt
|
||||
if(firstTrap matches tagged Valid (tagged Interrupt .i)) begin
|
||||
inIfc.issueCsrInstOrInterrupt;
|
||||
end
|
||||
`ifdef CHECK_DEADLOCK
|
||||
renameCorrectPath.send;
|
||||
`endif
|
||||
endrule
|
||||
|
||||
// print rename info
|
||||
function Action printRename(Integer i,
|
||||
RegsReady regs_ready_cons,
|
||||
RegsReady regs_ready_aggr,
|
||||
ArchRegs arch_regs,
|
||||
PhyRegs phy_regs);
|
||||
action
|
||||
$display(" [doRenaming - %d] regs_ready: cons ", i, fshow(regs_ready_cons), " ; aggr ", fshow(regs_ready_aggr));
|
||||
if (arch_regs.src1 matches tagged Valid .valid_src) begin
|
||||
if (phy_regs.src1 matches tagged Valid .valid_src_renamed) begin
|
||||
$display(" [SRC RENAMING] ", fshow(valid_src), " -> ", fshow(valid_src_renamed));
|
||||
end else begin
|
||||
$display(" [SRC RENAMING] ERROR: ", fshow(valid_src), " -> INVALID");
|
||||
doAssert(False, "rename src1 invalid");
|
||||
end
|
||||
end
|
||||
if (arch_regs.src2 matches tagged Valid .valid_src) begin
|
||||
if (phy_regs.src2 matches tagged Valid .valid_src_renamed) begin
|
||||
$fdisplay(stdout, " [SRC RENAMING] ", fshow(valid_src), " -> ", fshow(valid_src_renamed));
|
||||
end else begin
|
||||
$fdisplay(stdout, " [SRC RENAMING] ERROR: ", fshow(valid_src), " -> INVALID");
|
||||
doAssert(False, "rename src2 invalid");
|
||||
end
|
||||
end
|
||||
if (arch_regs.src3 matches tagged Valid .valid_src) begin
|
||||
if (phy_regs.src3 matches tagged Valid .valid_src_renamed) begin
|
||||
$fdisplay(stdout, " [SRC RENAMING] ", fshow(valid_src), " -> ", fshow(valid_src_renamed));
|
||||
end else begin
|
||||
$fdisplay(stdout, " [SRC RENAMING] ERROR: ", fshow(valid_src), " -> INVALID");
|
||||
doAssert(False, "rename src3 invalid");
|
||||
end
|
||||
end
|
||||
if (arch_regs.dst matches tagged Valid .valid_dst) begin
|
||||
if (phy_regs.dst matches tagged Valid .valid_dst_renamed) begin
|
||||
$fdisplay(stdout, " [DST RENAMING] ", fshow(valid_dst), " => ", fshow(valid_dst_renamed));
|
||||
end else begin
|
||||
$fdisplay(stdout, " [DST RENAMING] ERROR: ", fshow(valid_dst), " -> INVALID");
|
||||
doAssert(False, "rename dst invalid");
|
||||
end
|
||||
end
|
||||
endaction
|
||||
endfunction
|
||||
|
||||
// check for system inst that needs to replay
|
||||
Bool firstReplay = doReplay(fetchStage.pipelines[0].first.dInst.iType);
|
||||
|
||||
// System inst is renamed only when ROB is empty
|
||||
rule doRenaming_SystemInst(
|
||||
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
|
||||
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
|
||||
&& !isValid(firstTrap) // not trap
|
||||
&& firstReplay // system inst needs replay
|
||||
&& rob.isEmpty // stall for ROB empty
|
||||
);
|
||||
fetchStage.pipelines[0].deq;
|
||||
let x = fetchStage.pipelines[0].first;
|
||||
let pc = x.pc;
|
||||
let ppc = x.ppc;
|
||||
let main_epoch = x.main_epoch;
|
||||
let dpTrain = x.dpTrain;
|
||||
let inst = x.inst;
|
||||
let dInst = x.dInst;
|
||||
let arch_regs = x.regs;
|
||||
let cause = x.cause;
|
||||
if(verbose) $display("[doRenaming] system inst: ", fshow(x));
|
||||
|
||||
// update prev epoch
|
||||
epochManager.updatePrevEpoch[0].update(main_epoch);
|
||||
// Flip epoch without redirecting. This avoids doing incorrect work
|
||||
incrEpochStallFetch;
|
||||
|
||||
// get spec bits (should be 0), and no need to checkout spec tag
|
||||
let spec_bits = specTagManager.currentSpecBits;
|
||||
doAssert(spec_bits == 0, "cannot have spec bits");
|
||||
|
||||
// do renaming (renaming is non-speculative)
|
||||
let rename_result = regRenamingTable.rename[0].getRename(arch_regs);
|
||||
let phy_regs = rename_result.phy_regs;
|
||||
regRenamingTable.rename[0].claimRename(arch_regs, spec_bits);
|
||||
|
||||
// scoreboard lookup
|
||||
let regs_ready_cons = sbCons.eagerLookup[0].get(phy_regs);
|
||||
let regs_ready_aggr = sbAggr.eagerLookup[0].get(phy_regs);
|
||||
sbCons.setBusy[0].set(phy_regs.dst);
|
||||
sbAggr.setBusy[0].set(phy_regs.dst);
|
||||
|
||||
// print rename info
|
||||
if (verbose) begin
|
||||
printRename(0, regs_ready_cons, regs_ready_aggr, arch_regs, phy_regs);
|
||||
end
|
||||
|
||||
// get ROB tag
|
||||
let inst_tag = rob.enqPort[0].getEnqInstTag;
|
||||
|
||||
// CSR inst will be sent to ALU exe pipeline
|
||||
Bool to_exec = False;
|
||||
if (dInst.execFunc matches tagged Alu .alu) begin
|
||||
to_exec = True;
|
||||
doAssert(dInst.iType == Csr, "only CSR inst send to exe");
|
||||
end
|
||||
else begin
|
||||
doAssert(dInst.iType == FenceI ||
|
||||
dInst.iType == SFence ||
|
||||
dInst.iType == Sret ||
|
||||
dInst.iType == Mret,
|
||||
"non-CSR inst not send to exe");
|
||||
doAssert(dInst.execFunc == tagged Other,
|
||||
"non-exe inst exec func is other");
|
||||
end
|
||||
|
||||
// send to ALU reservation station
|
||||
if (to_exec) begin
|
||||
reservationStationAlu[0].enq(ToReservationStation {
|
||||
data: AluRSData {dInst: dInst, dpTrain: dpTrain},
|
||||
regs: phy_regs,
|
||||
tag: inst_tag,
|
||||
spec_bits: spec_bits,
|
||||
spec_tag: Invalid,
|
||||
regs_ready: regs_ready_aggr // alu will recv bypass
|
||||
});
|
||||
end
|
||||
|
||||
// send to ROB
|
||||
Bool will_dirty_fpu_state = False;
|
||||
if (arch_regs.dst matches tagged Valid( tagged Fpu .r )) begin
|
||||
will_dirty_fpu_state = True;
|
||||
doAssert(False, "system inst never touches FP regs");
|
||||
end
|
||||
RobInstState rob_inst_state = to_exec ? NotDone : Executed;
|
||||
let y = ToReorderBuffer{pc: pc,
|
||||
iType: dInst.iType,
|
||||
csr: dInst.csr,
|
||||
claimed_phy_reg: True, // XXX we always claim a free reg in rename
|
||||
trap: Invalid, // no trap
|
||||
// default values of FullResult
|
||||
ppc_vaddr_csrData: PPC (ppc), // default use PPC
|
||||
fflags: 0,
|
||||
////////
|
||||
will_dirty_fpu_state: will_dirty_fpu_state,
|
||||
rob_inst_state: rob_inst_state,
|
||||
lsqTag: ?,
|
||||
ldKilled: Invalid,
|
||||
memAccessAtCommit: False,
|
||||
lsqAtCommitNotified: False,
|
||||
nonMMIOStDone: False,
|
||||
epochIncremented: True, // system inst has incremented epoch
|
||||
spec_bits: spec_bits
|
||||
};
|
||||
rob.enqPort[0].enq(y);
|
||||
|
||||
// record if we issue an CSR inst
|
||||
if(dInst.iType == Csr) begin
|
||||
inIfc.issueCsrInstOrInterrupt;
|
||||
end
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
renameCorrectPath.send;
|
||||
`endif
|
||||
endrule
|
||||
|
||||
`ifdef SECURITY
|
||||
// speculation control:
|
||||
// M mode: turn off speculation for mem inst only
|
||||
// non-M mode: controlled by mspec CSR
|
||||
Bool machineMode = csrf.decodeInfo.prv == prvM;
|
||||
Bool specNone = !machineMode && csrf.rd(CSRmspec) == zeroExtend(mSpecNone);
|
||||
Bool specNonMem = machineMode || csrf.rd(CSRmspec) == zeroExtend(mSpecNonMem);
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
rule incSpecNoneCycles(inIfc.doStats && specNone);
|
||||
specNoneCycles.incr(1);
|
||||
endrule
|
||||
rule incSpecNonMemCycles(inIfc.doStats && specNonMem);
|
||||
specNonMemCycles.incr(1);
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// first inst is mem inst
|
||||
function Bool isMemInst(ExecFunc f);
|
||||
return f matches tagged Mem .m ? True : False;
|
||||
endfunction
|
||||
Bool firstMem = isMemInst(fetchStage.pipelines[0].first.dInst.execFunc);
|
||||
|
||||
// In case speculation is turned off for mem inst only, we rename mem inst
|
||||
// only when ROB is empty
|
||||
rule doRenaming_MemInst(
|
||||
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
|
||||
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
|
||||
&& !isValid(firstTrap) // not trap
|
||||
&& !firstReplay // not system inst
|
||||
// turn off speculation for mem inst only, and first inst is mem
|
||||
&& (specNonMem && firstMem)
|
||||
&& rob.isEmpty // stall for ROB empty to process mem inst
|
||||
);
|
||||
fetchStage.pipelines[0].deq;
|
||||
let x = fetchStage.pipelines[0].first;
|
||||
let pc = x.pc;
|
||||
let ppc = x.ppc;
|
||||
let main_epoch = x.main_epoch;
|
||||
let dpTrain = x.dpTrain;
|
||||
let inst = x.inst;
|
||||
let dInst = x.dInst;
|
||||
let arch_regs = x.regs;
|
||||
let cause = x.cause;
|
||||
if(verbose) $display("[doRenaming] mem inst: ", fshow(x));
|
||||
|
||||
// update prev epoch
|
||||
epochManager.updatePrevEpoch[0].update(main_epoch);
|
||||
|
||||
// get spec bits (should be 0), and no need to checkout spec tag
|
||||
let spec_bits = specTagManager.currentSpecBits;
|
||||
doAssert(spec_bits == 0, "cannot have spec bits");
|
||||
|
||||
// do renaming (renaming is non-speculative)
|
||||
let rename_result = regRenamingTable.rename[0].getRename(arch_regs);
|
||||
let phy_regs = rename_result.phy_regs;
|
||||
regRenamingTable.rename[0].claimRename(arch_regs, spec_bits);
|
||||
|
||||
// scoreboard lookup
|
||||
let regs_ready_cons = sbCons.eagerLookup[0].get(phy_regs);
|
||||
let regs_ready_aggr = sbAggr.eagerLookup[0].get(phy_regs);
|
||||
sbCons.setBusy[0].set(phy_regs.dst);
|
||||
sbAggr.setBusy[0].set(phy_regs.dst);
|
||||
|
||||
// print rename info
|
||||
if (verbose) begin
|
||||
printRename(0, regs_ready_cons, regs_ready_aggr, arch_regs, phy_regs);
|
||||
end
|
||||
|
||||
// get ROB tag
|
||||
let inst_tag = rob.enqPort[0].getEnqInstTag;
|
||||
|
||||
// LSQ tag
|
||||
LdStQTag lsq_tag = ?;
|
||||
|
||||
// send to MEM reservation station
|
||||
if (dInst.execFunc matches tagged Mem .mem_inst) begin
|
||||
Bool isLdQ = isLdQMemFunc(mem_inst.mem_func);
|
||||
Maybe#(LdStQTag) lsqEnqTag = isLdQ ? lsq.enqLdTag : lsq.enqStTag;
|
||||
if (lsqEnqTag matches tagged Valid .lsqTag) begin
|
||||
// can process, send to Mem rs and LSQ
|
||||
lsq_tag = lsqTag; // record LSQ tag
|
||||
if (dInst.iType != Fence) begin // Fence does not go to RS
|
||||
reservationStationMem.enq(ToReservationStation {
|
||||
data: MemRSData {
|
||||
mem_func: mem_inst.mem_func,
|
||||
imm: validValue(dInst.imm),
|
||||
ldstq_tag: lsqTag
|
||||
},
|
||||
regs: phy_regs,
|
||||
tag: inst_tag,
|
||||
spec_bits: spec_bits,
|
||||
spec_tag: Invalid,
|
||||
regs_ready: regs_ready_aggr // mem currently recv bypass
|
||||
});
|
||||
end
|
||||
doAssert(ppc == pc + 4, "Mem next PC is not PC+4");
|
||||
doAssert(!isValid(dInst.csr), "Mem never explicitly read/write CSR");
|
||||
doAssert((dInst.iType != Fence) == isValid(dInst.imm),
|
||||
"Mem (non-Fence) needs imm for virtual addr");
|
||||
// put in ldstq
|
||||
if(isLdQ) begin
|
||||
lsq.enqLd(inst_tag, mem_inst, phy_regs.dst, spec_bits);
|
||||
end
|
||||
else begin
|
||||
lsq.enqSt(inst_tag, mem_inst, phy_regs.dst, spec_bits);
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// cannot process this inst, stall
|
||||
when(False, noAction);
|
||||
end
|
||||
end
|
||||
else begin
|
||||
doAssert(False, "Must be mem inst");
|
||||
end
|
||||
|
||||
// send to ROB
|
||||
Bool will_dirty_fpu_state = False;
|
||||
if (arch_regs.dst matches tagged Valid( tagged Fpu .r )) begin
|
||||
will_dirty_fpu_state = True;
|
||||
end
|
||||
RobInstState rob_inst_state = NotDone; // mem inst always needs execution
|
||||
let y = ToReorderBuffer{pc: pc,
|
||||
iType: dInst.iType,
|
||||
csr: dInst.csr,
|
||||
claimed_phy_reg: True, // XXX we always claim a free reg in rename
|
||||
trap: Invalid, // no trap
|
||||
// default values of FullResult
|
||||
ppc_vaddr_csrData: PPC (ppc), // default use PPC
|
||||
fflags: 0,
|
||||
////////
|
||||
will_dirty_fpu_state: will_dirty_fpu_state,
|
||||
rob_inst_state: rob_inst_state,
|
||||
lsqTag: lsq_tag,
|
||||
ldKilled: Invalid,
|
||||
memAccessAtCommit: False, // set by ROB in case of fence
|
||||
lsqAtCommitNotified: False,
|
||||
nonMMIOStDone: False,
|
||||
epochIncremented: False,
|
||||
spec_bits: spec_bits
|
||||
};
|
||||
rob.enqPort[0].enq(y);
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
renameCorrectPath.send;
|
||||
`endif
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// Count based scheduling in case of $n$ RS for the same inst type. We
|
||||
// assume all such RS are of the same size, and prioritize RS with smaller
|
||||
// valid (occupied) entries.
|
||||
function Maybe#(idxT) scheduleRS(
|
||||
Vector#(n, countT) valid_cnt, Vector#(n, Bool) rdy
|
||||
) provisos(
|
||||
Ord#(countT), Alias#(idxT, Bit#(TLog#(n))), Add#(1, a__, n)
|
||||
);
|
||||
function Bit#(TLog#(n)) getRS(idxT a, idxT b);
|
||||
if(!rdy[a]) begin
|
||||
return b;
|
||||
end
|
||||
else if(!rdy[b]) begin
|
||||
return a;
|
||||
end
|
||||
else begin
|
||||
// prioritize RS with smaller valid-entry count
|
||||
return valid_cnt[a] < valid_cnt[b] ? a : b;
|
||||
end
|
||||
endfunction
|
||||
Vector#(n, idxT) idxVec = genWith(fromInteger);
|
||||
idxT idx = fold(getRS, idxVec);
|
||||
return rdy[idx] ? Valid (idx) : Invalid;
|
||||
endfunction
|
||||
|
||||
// rename correct path inst
|
||||
rule doRenaming(
|
||||
!inIfc.pendingMMIOPRq // stall when MMIO pRq is pending
|
||||
&& epochManager.checkEpoch[0].check(fetchStage.pipelines[0].first.main_epoch) // correct path
|
||||
&& !isValid(firstTrap) // not trap
|
||||
&& !firstReplay // not system inst
|
||||
`ifdef SECURITY
|
||||
// stall for ROB empty if we don't allow speculation at all
|
||||
&& (!specNone || rob.isEmpty)
|
||||
// don't process mem inst if we don't allow speculation for mem inst only
|
||||
&& !(specNonMem && firstMem)
|
||||
`endif
|
||||
);
|
||||
// we stop superscalar rename when an instruction cannot be processed:
|
||||
// (a) It has trap
|
||||
// (b) It is wrong path
|
||||
// (c) It is system inst (we handle system inst in a separate rule)
|
||||
// (d) It does not have enough resource
|
||||
Bool stop = False;
|
||||
// We automatically stop after an inst cannot be deq from fetch stage
|
||||
// because canDeq signal for sup-fifo is consecutive
|
||||
|
||||
// Note that epoch will not change in this rule
|
||||
|
||||
// track limited resource usage
|
||||
Vector#(AluExeNum, Bool) aluExeUsed = replicate(False);
|
||||
Vector#(FpuMulDivExeNum, Bool) fpuMulDivExeUsed = replicate(False);
|
||||
Bool memExeUsed = False;
|
||||
Bool specTagClaimed = False; // specTagManager
|
||||
|
||||
// track rename activity
|
||||
Bool doCorrectPath = False;
|
||||
SupCnt renameCnt = 0;
|
||||
|
||||
// initial spec bits at the beginning of this cycle
|
||||
// we may update it during the processing
|
||||
SpecBits spec_bits = specTagManager.currentSpecBits;
|
||||
|
||||
// ALU RS valid counts
|
||||
Vector#(AluExeNum, Bit#(TLog#(TAdd#(`RS_ALU_SIZE, 1)))) aluRSCount;
|
||||
for(Integer i = 0; i < valueof(AluExeNum); i = i+1) begin
|
||||
aluRSCount[i] = reservationStationAlu[i].approximateCount;
|
||||
end
|
||||
// FPU/MUL/DIV RS valid counts
|
||||
Vector#(FpuMulDivExeNum, Bit#(TLog#(TAdd#(`RS_FPUMULDIV_SIZE, 1)))) fpuMulDivRSCount;
|
||||
for(Integer i = 0; i < valueof(FpuMulDivExeNum); i = i+1) begin
|
||||
fpuMulDivRSCount[i] = reservationStationFpuMulDiv[i].approximateCount;
|
||||
end
|
||||
|
||||
// We apply actions at the end of each iteration
|
||||
// We **cannot** apply actions at the end of rule,
|
||||
// because intermediate iterations may change state
|
||||
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
|
||||
if(!stop && fetchStage.pipelines[i].canDeq) begin
|
||||
let x = fetchStage.pipelines[i].first; // don't deq now, inst may not have resource
|
||||
let pc = x.pc;
|
||||
let ppc = x.ppc;
|
||||
let main_epoch = x.main_epoch;
|
||||
let dpTrain = x.dpTrain;
|
||||
let inst = x.inst;
|
||||
let dInst = x.dInst;
|
||||
let arch_regs = x.regs;
|
||||
let cause = x.cause;
|
||||
|
||||
// check for wrong path, if wrong path, don't process it, leave to the other rule in next cycle
|
||||
if(!epochManager.checkEpoch[i].check(main_epoch)) begin
|
||||
stop = True;
|
||||
end
|
||||
// for correct path
|
||||
// check ROB can be enq, otherwise cannot process
|
||||
if(!rob.enqPort[i].canEnq) begin
|
||||
stop = True;
|
||||
end
|
||||
// check trap, if trap, cannot process, leave to the other rule in next cycle
|
||||
if(isValid(getTrap(x))) begin
|
||||
stop = True;
|
||||
end
|
||||
// for system inst, process in next cycle (in a different rule)
|
||||
if(doReplay(dInst.iType)) begin
|
||||
stop = True;
|
||||
end
|
||||
`ifdef SECURITY
|
||||
// When speculation is not allowed at all, the second inst
|
||||
// cannot be processed
|
||||
if(specNone && i != 0) begin
|
||||
stop = True;
|
||||
end
|
||||
// When speculation is not allowed for mem inst only, stop when
|
||||
// we seen any mem inst
|
||||
if(specNonMem && isMemInst(dInst.execFunc)) begin
|
||||
stop = True;
|
||||
end
|
||||
`endif
|
||||
// check renaming table can be enq, otherwise cannot process now
|
||||
if(!regRenamingTable.rename[i].canRename) begin
|
||||
stop = True;
|
||||
end
|
||||
// Figure out if there is new speculation and if there is
|
||||
// speculative renaming happening.
|
||||
Bool new_speculation = False;
|
||||
Bool speculative_renaming = False; // deprecated: this is originally for Ld/St checkpoint
|
||||
if (dInst.execFunc matches tagged Br .br) begin
|
||||
// This instruction can cause a redirection due to branch
|
||||
// misprediction. Lets claim a checkpoint for this instruction.
|
||||
// If this instruction is a JAL or JRAL instruction, then the
|
||||
// checkpoint should include the renaming for the destination
|
||||
// register.
|
||||
new_speculation = True;
|
||||
speculative_renaming = False;
|
||||
end
|
||||
// if need spec tag, check spec tag is available, otherwise cannot process
|
||||
if(new_speculation && (specTagClaimed || !specTagManager.canClaim)) begin
|
||||
stop = True;
|
||||
end
|
||||
|
||||
if(!stop) begin
|
||||
// we can continue to analyze this inst
|
||||
// Claim a speculation tag from the specTagManager if necessary
|
||||
Maybe#(SpecTag) spec_tag = tagged Invalid;
|
||||
if (new_speculation) begin
|
||||
spec_tag = tagged Valid specTagManager.nextSpecTag;
|
||||
end
|
||||
|
||||
// get renaming
|
||||
// If the renaming is speculative, then the renaming will
|
||||
// depend on the current spec_tag too.
|
||||
let renaming_spec_bits = spec_bits | (speculative_renaming ? (1 << fromMaybe(?,spec_tag)) : 0);
|
||||
let rename_result = regRenamingTable.rename[i].getRename(arch_regs);
|
||||
let phy_regs = rename_result.phy_regs;
|
||||
|
||||
// scoreboard lookup
|
||||
let regs_ready_cons = sbCons.eagerLookup[i].get(phy_regs);
|
||||
let regs_ready_aggr = sbAggr.eagerLookup[i].get(phy_regs);
|
||||
|
||||
// get ROB tag
|
||||
let inst_tag = rob.enqPort[i].getEnqInstTag;
|
||||
|
||||
// LSQ tag
|
||||
LdStQTag lsq_tag = ?;
|
||||
|
||||
// check execution pipelines availability
|
||||
// this determines whether this inst can finally be processed
|
||||
// so we will directly take actions on exe pipelines
|
||||
Bool to_exec = False;
|
||||
Bool to_mem = False;
|
||||
Bool to_FpuMulDiv = False;
|
||||
case (dInst.execFunc) matches
|
||||
tagged Alu .alu: to_exec = True;
|
||||
tagged Br .br: to_exec = True;
|
||||
tagged MulDiv .muldiv: to_FpuMulDiv = True;
|
||||
tagged Fpu .fpu: to_FpuMulDiv = True;
|
||||
tagged Mem .mem: to_mem = True;
|
||||
default:
|
||||
// no need for execution, directly become Executed
|
||||
noAction;
|
||||
endcase
|
||||
|
||||
if (to_exec) begin
|
||||
// find an ALU pipeline
|
||||
function Bool aluValid(Integer k) = !aluExeUsed[k] && reservationStationAlu[k].canEnq;
|
||||
Vector#(AluExeNum, Bool) aluReady = map(aluValid, genVector);
|
||||
if(scheduleRS(aluRSCount, aluReady) matches tagged Valid .k) begin
|
||||
// can process, send to ALU rs
|
||||
aluExeUsed[k] = True; // mark resource used
|
||||
reservationStationAlu[k].enq(ToReservationStation {
|
||||
data: AluRSData {dInst: dInst, dpTrain: dpTrain},
|
||||
regs: phy_regs,
|
||||
tag: inst_tag,
|
||||
spec_bits: spec_bits,
|
||||
spec_tag: spec_tag,
|
||||
regs_ready: regs_ready_aggr // alu will recv bypass
|
||||
});
|
||||
end
|
||||
else begin
|
||||
// cannot process this inst, stop
|
||||
stop = True;
|
||||
end
|
||||
end
|
||||
else if (to_FpuMulDiv) begin
|
||||
function Bool fpuMulDivValid(Integer k) = !fpuMulDivExeUsed[k] && reservationStationFpuMulDiv[k].canEnq;
|
||||
Vector#(FpuMulDivExeNum, Bool) fpuMulDivReady = map(fpuMulDivValid, genVector);
|
||||
if(scheduleRS(fpuMulDivRSCount, fpuMulDivReady) matches tagged Valid .k) begin
|
||||
// can process, send to FPU MUL DIV rs
|
||||
fpuMulDivExeUsed[k] = True; // mark resource used
|
||||
reservationStationFpuMulDiv[k].enq(ToReservationStation {
|
||||
data: FpuMulDivRSData {execFunc: dInst.execFunc},
|
||||
regs: phy_regs,
|
||||
tag: inst_tag,
|
||||
spec_bits: spec_bits,
|
||||
spec_tag: spec_tag,
|
||||
regs_ready: regs_ready_aggr // fpu mul div recv bypass
|
||||
});
|
||||
doAssert(ppc == pc + 4, "FpuMulDiv next PC is not PC+4");
|
||||
doAssert(!isValid(dInst.csr), "FpuMulDiv never explicitly read/write CSR");
|
||||
doAssert(!isValid(spec_tag), "should not have spec tag");
|
||||
end
|
||||
else begin
|
||||
// cannot process this inst, stop
|
||||
stop = True;
|
||||
end
|
||||
end
|
||||
else if (to_mem) begin
|
||||
if (dInst.execFunc matches tagged Mem .mem_inst) begin
|
||||
Bool isLdQ = isLdQMemFunc(mem_inst.mem_func);
|
||||
Maybe#(LdStQTag) lsqEnqTag = isLdQ ? lsq.enqLdTag : lsq.enqStTag;
|
||||
if (!memExeUsed &&& reservationStationMem.canEnq &&&
|
||||
lsqEnqTag matches tagged Valid .lsqTag) begin
|
||||
// can process, send to Mem rs and LSQ
|
||||
memExeUsed = True; // mark resource used
|
||||
lsq_tag = lsqTag; // record LSQ tag
|
||||
if (dInst.iType != Fence) begin // fence does not go to RS
|
||||
reservationStationMem.enq(ToReservationStation {
|
||||
data: MemRSData {
|
||||
mem_func: mem_inst.mem_func,
|
||||
imm: validValue(dInst.imm),
|
||||
ldstq_tag: lsqTag
|
||||
},
|
||||
regs: phy_regs,
|
||||
tag: inst_tag,
|
||||
spec_bits: spec_bits,
|
||||
spec_tag: spec_tag,
|
||||
regs_ready: regs_ready_aggr // mem currently recv bypass
|
||||
});
|
||||
end
|
||||
doAssert(ppc == pc + 4, "Mem next PC is not PC+4");
|
||||
doAssert(!isValid(dInst.csr), "Mem never explicitly read/write CSR");
|
||||
doAssert((dInst.iType != Fence) == isValid(dInst.imm),
|
||||
"Mem (non-Fence) needs imm for virtual addr");
|
||||
doAssert(!isValid(spec_tag), "should not have spec tag");
|
||||
// put in ldstq
|
||||
if(isLdQ) begin
|
||||
lsq.enqLd(inst_tag, mem_inst, phy_regs.dst, spec_bits);
|
||||
end
|
||||
else begin
|
||||
lsq.enqSt(inst_tag, mem_inst, phy_regs.dst, spec_bits);
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// cannot process this inst, stop
|
||||
stop = True;
|
||||
end
|
||||
end
|
||||
else begin
|
||||
stop = True;
|
||||
doAssert(False, "non memory instruction has to_mem == True");
|
||||
end
|
||||
end
|
||||
|
||||
// apply remaining actions if inst can be processed
|
||||
if(!stop) begin
|
||||
if(verbose) $display("[doRenaming - %d] ", i, fshow(x));
|
||||
|
||||
// deq fetch & update epochs match
|
||||
fetchStage.pipelines[i].deq;
|
||||
epochManager.updatePrevEpoch[i].update(main_epoch);
|
||||
|
||||
// Claim a speculation tag
|
||||
if (new_speculation) begin
|
||||
specTagClaimed = True; // mark resource used
|
||||
specTagManager.claimSpecTag;
|
||||
end
|
||||
|
||||
// Do renaming
|
||||
regRenamingTable.rename[i].claimRename(arch_regs, renaming_spec_bits);
|
||||
|
||||
// Scoreboard Operations
|
||||
sbCons.setBusy[i].set(phy_regs.dst);
|
||||
sbAggr.setBusy[i].set(phy_regs.dst);
|
||||
|
||||
// display information
|
||||
if (verbose) begin
|
||||
printRename(i, regs_ready_cons, regs_ready_aggr, arch_regs, phy_regs);
|
||||
end
|
||||
|
||||
// Enqueue into reorder buffer
|
||||
Bool will_dirty_fpu_state = False;
|
||||
if (arch_regs.dst matches tagged Valid( tagged Fpu .r )) begin
|
||||
will_dirty_fpu_state = True;
|
||||
end
|
||||
RobInstState rob_inst_state = (to_exec || to_mem || to_FpuMulDiv) ? NotDone : Executed;
|
||||
|
||||
let y = ToReorderBuffer{pc: pc,
|
||||
iType: dInst.iType,
|
||||
csr: dInst.csr,
|
||||
claimed_phy_reg: True, // XXX we always claim a free reg in rename
|
||||
trap: Invalid, // no trap
|
||||
// default values of FullResult
|
||||
ppc_vaddr_csrData: PPC (ppc), // default use PPC
|
||||
fflags: 0,
|
||||
////////
|
||||
will_dirty_fpu_state: will_dirty_fpu_state,
|
||||
rob_inst_state: rob_inst_state,
|
||||
lsqTag: lsq_tag,
|
||||
ldKilled: Invalid,
|
||||
memAccessAtCommit: False, // set by ROB in case of fence
|
||||
lsqAtCommitNotified: False,
|
||||
nonMMIOStDone: False,
|
||||
epochIncremented: False,
|
||||
spec_bits: spec_bits
|
||||
};
|
||||
rob.enqPort[i].enq(y);
|
||||
|
||||
// record activity
|
||||
doCorrectPath = True;
|
||||
renameCnt = renameCnt + 1;
|
||||
|
||||
// update spec bits if spec tag is claimed
|
||||
if(spec_tag matches tagged Valid .t) begin
|
||||
spec_bits = spec_bits | (1 << t);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
// only fire this rule if we make some progress
|
||||
// otherwise this rule may block other rules forever
|
||||
when(doCorrectPath, noAction);
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
if(inIfc.doStats) begin
|
||||
if(renameCnt > 1) begin
|
||||
supRenameCnt.incr(1);
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
if(doCorrectPath) begin
|
||||
renameCorrectPath.send;
|
||||
end
|
||||
`endif
|
||||
endrule
|
||||
|
||||
|
||||
`ifdef CHECK_DEADLOCK
|
||||
interface renameInstStuck = toGet(renameInstStuckQ);
|
||||
interface renameCorrectPathStuck = toGet(renameCorrectPathStuckQ);
|
||||
`else
|
||||
interface renameInstStuck = nullGet;
|
||||
interface renameCorrectPathStuck = nullGet;
|
||||
`endif
|
||||
|
||||
method Data getPerf(ExeStagePerfType t);
|
||||
return (case(t)
|
||||
`ifdef PERF_COUNT
|
||||
SupRenameCnt: supRenameCnt;
|
||||
`ifdef SECURITY
|
||||
SpecNoneCycles: specNoneCycles;
|
||||
SpecNonMemCycles: specNoneCycles;
|
||||
`endif
|
||||
`endif
|
||||
default: 0;
|
||||
endcase);
|
||||
endmethod
|
||||
endmodule
|
||||
44
src_Core/RISCY_OOO/procs/RV64G_OOO/ReorderBufferSynth.bsv
Normal file
44
src_Core/RISCY_OOO/procs/RV64G_OOO/ReorderBufferSynth.bsv
Normal file
@@ -0,0 +1,44 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import ReorderBuffer::*;
|
||||
import SynthParam::*;
|
||||
|
||||
typedef ReorderBufferRowEhr#(AluExeNum, FpuMulDivExeNum) RobRowSynth;
|
||||
(* synthesize *)
|
||||
module mkRobRowSynth(RobRowSynth);
|
||||
let m <- mkReorderBufferRowEhr;
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
// use superscalar ROB
|
||||
|
||||
typedef SupReorderBuffer#(AluExeNum, FpuMulDivExeNum) ReorderBufferSynth;
|
||||
|
||||
(* synthesize *)
|
||||
module mkReorderBufferSynth(ReorderBufferSynth);
|
||||
let m <- mkSupReorderBuffer(`ROB_LAZY_ENQ, mkRobRowSynth);
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
43
src_Core/RISCY_OOO/procs/RV64G_OOO/ReservationStationAlu.bsv
Normal file
43
src_Core/RISCY_OOO/procs/RV64G_OOO/ReservationStationAlu.bsv
Normal file
@@ -0,0 +1,43 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import ReservationStationEhr::*;
|
||||
import SynthParam::*;
|
||||
import BrPred::*;
|
||||
import DirPredictor::*;
|
||||
|
||||
typedef struct {
|
||||
DecodedInst dInst;
|
||||
DirPredTrainInfo dpTrain;
|
||||
} AluRSData deriving(Bits, Eq, FShow);
|
||||
|
||||
// ALU pipeline is aggressive, i.e. it recv bypass and early RS wakeup
|
||||
typedef ReservationStation#(`RS_ALU_SIZE, WrAggrPortNum, AluRSData) ReservationStationAlu;
|
||||
(* synthesize *)
|
||||
module mkReservationStationAlu(ReservationStationAlu);
|
||||
ReservationStationAlu m <- mkReservationStation(`LAZY_RS_RF, `RS_LAZY_ENQ, valueof(AluExeNum) > 1);
|
||||
return m;
|
||||
endmodule
|
||||
@@ -0,0 +1,40 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import ReservationStationEhr::*;
|
||||
import SynthParam::*;
|
||||
|
||||
typedef struct {
|
||||
ExecFunc execFunc;
|
||||
} FpuMulDivRSData deriving(Bits, Eq, FShow);
|
||||
|
||||
// FPU MUL DIV pipeline is aggressive, i.e. it recv bypass and early RS wakeup
|
||||
typedef ReservationStation#(`RS_FPUMULDIV_SIZE, WrAggrPortNum, FpuMulDivRSData) ReservationStationFpuMulDiv;
|
||||
(* synthesize *)
|
||||
module mkReservationStationFpuMulDiv(ReservationStationFpuMulDiv);
|
||||
let m <- mkReservationStation(`LAZY_RS_RF, `RS_LAZY_ENQ, valueof(FpuMulDivExeNum) > 1);
|
||||
return m;
|
||||
endmodule
|
||||
42
src_Core/RISCY_OOO/procs/RV64G_OOO/ReservationStationMem.bsv
Normal file
42
src_Core/RISCY_OOO/procs/RV64G_OOO/ReservationStationMem.bsv
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import ReservationStationEhr::*;
|
||||
import SynthParam::*;
|
||||
|
||||
typedef struct {
|
||||
MemFunc mem_func;
|
||||
ImmData imm;
|
||||
LdStQTag ldstq_tag;
|
||||
} MemRSData deriving(Bits, Eq, FShow);
|
||||
|
||||
// MEM pipeline is aggressive, i.e. it recv bypass and early RS wakeup
|
||||
typedef ReservationStation#(`RS_MEM_SIZE, WrAggrPortNum, MemRSData) ReservationStationMem;
|
||||
(* synthesize *)
|
||||
module mkReservationStationMem(ReservationStationMem);
|
||||
let m <- mkReservationStation(`LAZY_RS_RF, `RS_LAZY_ENQ, False);
|
||||
return m;
|
||||
endmodule
|
||||
42
src_Core/RISCY_OOO/procs/RV64G_OOO/ScoreboardSynth.bsv
Normal file
42
src_Core/RISCY_OOO/procs/RV64G_OOO/ScoreboardSynth.bsv
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Scoreboard::*;
|
||||
import SynthParam::*;
|
||||
|
||||
// conservative sb
|
||||
typedef RenamingScoreboard#(WrConsPortNum, SbLazyLookupPortNum) ScoreboardCons;
|
||||
(* synthesize *)
|
||||
module mkScoreboardCons(ScoreboardCons);
|
||||
let m <- mkRenamingScoreboard;
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
// aggressive sb
|
||||
typedef RenamingScoreboard#(WrAggrPortNum, 0) ScoreboardAggr;
|
||||
(* synthesize *)
|
||||
module mkScoreboardAggr(ScoreboardAggr);
|
||||
let m <- mkRenamingScoreboard;
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
77
src_Core/RISCY_OOO/procs/RV64G_OOO/SynthParam.bsv
Normal file
77
src_Core/RISCY_OOO/procs/RV64G_OOO/SynthParam.bsv
Normal file
@@ -0,0 +1,77 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import DefaultValue::*;
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import HasSpecBits::*;
|
||||
import ReservationStationEhr::*;
|
||||
|
||||
// ALU exe num = superscalar size
|
||||
// FPU exe num = superscalar size / 2
|
||||
typedef SupSize AluExeNum;
|
||||
typedef TDiv#(SupSize, 2) FpuMulDivExeNum;
|
||||
|
||||
// Phy RFile
|
||||
// write: Alu < FpuMulDiv < Mem
|
||||
// read: Alu, FpuMulDiv, Mem
|
||||
typedef TAdd#(1, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileWrPortNum;
|
||||
typedef TAdd#(1, TAdd#(FpuMulDivExeNum, AluExeNum)) RFileRdPortNum;
|
||||
|
||||
// sb lazy lookup num: same as RFile read, becaues all pipelines recv bypass
|
||||
typedef RFileRdPortNum SbLazyLookupPortNum;
|
||||
|
||||
// ports for writing conservative elements
|
||||
// i.e. write rf & set conservative sb & wake up rs in conservative pipeline (i.e. not recv bypass)
|
||||
// ordering: Alu < FpuMulDiv < Mem
|
||||
typedef RFileWrPortNum WrConsPortNum;
|
||||
function Integer aluWrConsPort(Integer i) = i;
|
||||
function Integer fpuMulDivWrConsPort(Integer i) = valueof(AluExeNum) + i;
|
||||
Integer memWrConsPort = valueof(FpuMulDivExeNum) + valueof(AluExeNum);
|
||||
|
||||
// ports for writing aggressive elements
|
||||
// i.e. set aggressive sb & wake up rs in aggressive pipeline (i.e. recv bypass)
|
||||
// Mem pipeline has two places: mem resp and forwarding. Since the rule
|
||||
// containing forwarding calls lsq.issue, and the rule containing mem resp
|
||||
// calls lsq.wakeupLd, we order forward before mem.
|
||||
// Ordering: Alu < FpuMulDiv < Forward < Mem
|
||||
typedef TAdd#(2, TAdd#(FpuMulDivExeNum, AluExeNum)) WrAggrPortNum;
|
||||
function Integer aluWrAggrPort(Integer i) = i;
|
||||
function Integer fpuMulDivWrAggrPort(Integer i) = valueof(AluExeNum) + i;
|
||||
Integer forwardWrAggrPort = valueof(FpuMulDivExeNum) + valueof(AluExeNum);
|
||||
Integer memWrAggrPort = 1 + valueof(FpuMulDivExeNum) + valueof(AluExeNum);
|
||||
|
||||
// ports for read rf & lazy lookup in sb, ordering doesn't matter
|
||||
function Integer aluRdPort(Integer i) = i;
|
||||
function Integer fpuMulDivRdPort(Integer i) = valueof(AluExeNum) + i;
|
||||
Integer memRdPort = valueof(FpuMulDivExeNum) + valueof(AluExeNum);
|
||||
|
||||
// ports for correct spec, ordering doesn't matter
|
||||
typedef TAdd#(2, AluExeNum) CorrectSpecPortNum;
|
||||
function Integer finishAluCorrectSpecPort(Integer i) = i;
|
||||
Integer deqLSQCorrectSpecPort = valueof(AluExeNum);
|
||||
Integer finishMemCorrectSpecPort = 1 + valueof(AluExeNum);
|
||||
|
||||
// ports for manual conflict with wrong spec, ordering doesn't matter
|
||||
typedef FpuMulDivExeNum ConflictWrongSpecPortNum;
|
||||
function Integer finishFpuMulDivConflictWrongSpecPort(Integer i) = i;
|
||||
84
src_Core/RISCY_OOO/procs/lib/Amo.bsv
Normal file
84
src_Core/RISCY_OOO/procs/lib/Amo.bsv
Normal file
@@ -0,0 +1,84 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
|
||||
(* noinline *)
|
||||
function Data amoExec(AmoInst amo_inst, Data current_data, Data in_data, Bool upper_32_bits);
|
||||
// upper_32_bits is true if the operation is a 32-bit operation and the
|
||||
// address is the upper 32-bits of a 64-bit region.
|
||||
Data new_data = 0;
|
||||
Data old_data = current_data;
|
||||
|
||||
if (!amo_inst.doubleWord) begin
|
||||
if (!upper_32_bits) begin
|
||||
current_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(current_data[31:0]) : zeroExtend(current_data[31:0]);
|
||||
in_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(in_data[31:0]) : zeroExtend(in_data[31:0]);
|
||||
end else begin
|
||||
// use upper 32-bits instead
|
||||
current_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(current_data[63:32]) : zeroExtend(current_data[63:32]);
|
||||
in_data = (amo_inst.func == Min || amo_inst.func == Max) ? signExtend(in_data[31:0]) : zeroExtend(in_data[31:0]);
|
||||
end
|
||||
end
|
||||
|
||||
case (amo_inst.func)
|
||||
Swap: new_data = in_data;
|
||||
Add: new_data = current_data + in_data;
|
||||
Xor: new_data = current_data ^ in_data;
|
||||
And: new_data = current_data & in_data;
|
||||
Or: new_data = current_data | in_data;
|
||||
Min: new_data = sMin(current_data, in_data);
|
||||
Max: new_data = sMax(current_data, in_data);
|
||||
Minu: new_data = uMin(current_data, in_data);
|
||||
Maxu: new_data = uMax(current_data, in_data);
|
||||
endcase
|
||||
|
||||
if (!amo_inst.doubleWord) begin
|
||||
if (!upper_32_bits) begin
|
||||
return {old_data[63:32], new_data[31:0]};
|
||||
end else begin
|
||||
// change upper 32-bits instead
|
||||
return {new_data[31:0], old_data[31:0]};
|
||||
end
|
||||
end else begin
|
||||
return new_data;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function Bit#(t) sMax( Bit#(t) a, Bit#(t) b );
|
||||
Int#(t) x = max(unpack(a), unpack(b));
|
||||
return pack(x);
|
||||
endfunction
|
||||
function Bit#(t) sMin( Bit#(t) a, Bit#(t) b );
|
||||
Int#(t) x = min(unpack(a), unpack(b));
|
||||
return pack(x);
|
||||
endfunction
|
||||
function Bit#(t) uMax( Bit#(t) a, Bit#(t) b );
|
||||
UInt#(t) x = max(unpack(a), unpack(b));
|
||||
return pack(x);
|
||||
endfunction
|
||||
function Bit#(t) uMin( Bit#(t) a, Bit#(t) b );
|
||||
UInt#(t) x = min(unpack(a), unpack(b));
|
||||
return pack(x);
|
||||
endfunction
|
||||
81
src_Core/RISCY_OOO/procs/lib/Bht.bsv
Normal file
81
src_Core/RISCY_OOO/procs/lib/Bht.bsv
Normal file
@@ -0,0 +1,81 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import RegFile::*;
|
||||
import Vector::*;
|
||||
import BrPred::*;
|
||||
|
||||
export BhtTrainInfo;
|
||||
export mkBht;
|
||||
|
||||
typedef Bit#(0) BhtTrainInfo; // no training info needs to be remembered
|
||||
|
||||
// Local BHT Typedefs
|
||||
typedef 128 BhtEntries;
|
||||
typedef Bit#(TLog#(BhtEntries)) BhtIndex;
|
||||
|
||||
(* synthesize *)
|
||||
module mkBht(DirPredictor#(BhtTrainInfo));
|
||||
// Read and Write ordering doesn't matter since this is a predictor
|
||||
// mkRegFileWCF is the RegFile version of mkConfigReg
|
||||
RegFile#(BhtIndex, Bit#(2)) hist <- mkRegFileWCF(0,fromInteger(valueOf(BhtEntries)-1));
|
||||
|
||||
function BhtIndex getIndex(Addr pc);
|
||||
return truncate(pc >> 2);
|
||||
endfunction
|
||||
|
||||
Vector#(SupSize, DirPred#(BhtTrainInfo)) predIfc;
|
||||
for(Integer i = 0; i < valueof(SupSize); i = i+1) begin
|
||||
predIfc[i] = (interface DirPred;
|
||||
method ActionValue#(DirPredResult#(BhtTrainInfo)) pred(Addr pc);
|
||||
let index = getIndex(pc);
|
||||
Bit#(2) cnt = hist.sub(index);
|
||||
Bool taken = cnt[1] == 1;
|
||||
return DirPredResult {
|
||||
taken: taken,
|
||||
train: 0
|
||||
};
|
||||
endmethod
|
||||
endinterface);
|
||||
end
|
||||
|
||||
interface pred = predIfc;
|
||||
|
||||
method Action update(Addr pc, Bool taken, BhtTrainInfo train, Bool mispred);
|
||||
let index = getIndex(pc);
|
||||
let current_hist = hist.sub(index);
|
||||
Bit#(2) next_hist;
|
||||
if(taken) begin
|
||||
next_hist = (current_hist == 2'b11) ? 2'b11 : current_hist + 1;
|
||||
end else begin
|
||||
next_hist = (current_hist == 2'b00) ? 2'b00 : current_hist - 1;
|
||||
end
|
||||
hist.upd(index, next_hist);
|
||||
endmethod
|
||||
|
||||
method flush = noAction;
|
||||
method flush_done = True;
|
||||
endmodule
|
||||
|
||||
68
src_Core/RISCY_OOO/procs/lib/BrPred.bsv
Normal file
68
src_Core/RISCY_OOO/procs/lib/BrPred.bsv
Normal file
@@ -0,0 +1,68 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import Vector::*;
|
||||
|
||||
(* noinline *)
|
||||
function Maybe#(Addr) decodeBrPred( Addr pc, DecodedInst dInst, Bool histTaken );
|
||||
Addr pcPlus4 = pc + 4;
|
||||
Data imm_val = fromMaybe(?, getDInstImm(dInst));
|
||||
Maybe#(Addr) nextPc = tagged Invalid;
|
||||
if( dInst.iType == J ) begin
|
||||
Addr jTarget = pc + imm_val;
|
||||
nextPc = tagged Valid jTarget;
|
||||
end else if( dInst.iType == Br ) begin
|
||||
if( histTaken ) begin
|
||||
nextPc = tagged Valid (pc + imm_val);
|
||||
end else begin
|
||||
nextPc = tagged Valid pcPlus4;
|
||||
end
|
||||
end else if( dInst.iType == Jr ) begin
|
||||
// target is unknown until RegFetch
|
||||
nextPc = tagged Invalid;
|
||||
end else begin
|
||||
nextPc = tagged Valid pcPlus4;
|
||||
end
|
||||
return nextPc;
|
||||
endfunction
|
||||
|
||||
// general types for direction predictor
|
||||
|
||||
typedef struct {
|
||||
Bool taken;
|
||||
trainInfoT train; // info that a branch must keep for future training
|
||||
} DirPredResult#(type trainInfoT) deriving(Bits, Eq, FShow);
|
||||
|
||||
interface DirPred#(type trainInfoT);
|
||||
method ActionValue#(DirPredResult#(trainInfoT)) pred(Addr pc);
|
||||
endinterface
|
||||
|
||||
interface DirPredictor#(type trainInfoT);
|
||||
interface Vector#(SupSize, DirPred#(trainInfoT)) pred;
|
||||
method Action update(Addr pc, Bool taken, trainInfoT train, Bool mispred);
|
||||
method Action flush;
|
||||
method Bool flush_done;
|
||||
endinterface
|
||||
|
||||
121
src_Core/RISCY_OOO/procs/lib/Btb.bsv
Normal file
121
src_Core/RISCY_OOO/procs/lib/Btb.bsv
Normal file
@@ -0,0 +1,121 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import ConfigReg::*;
|
||||
import RegFile::*;
|
||||
import Vector::*;
|
||||
|
||||
export NextAddrPred(..);
|
||||
export mkBtb;
|
||||
|
||||
interface NextAddrPred;
|
||||
method Addr predPc(Addr pc);
|
||||
method Action update(Addr pc, Addr nextPc, Bool taken);
|
||||
// security
|
||||
method Action flush;
|
||||
method Bool flush_done;
|
||||
endinterface
|
||||
|
||||
// Local BTB Typedefs
|
||||
typedef 256 BtbEntries; // 4KB BTB
|
||||
typedef Bit#(TLog#(BtbEntries)) BtbIndex;
|
||||
typedef Bit#(TSub#(TSub#(AddrSz, TLog#(BtbEntries)), 2)) BtbTag;
|
||||
|
||||
typedef struct {
|
||||
Addr pc;
|
||||
Addr nextPc;
|
||||
Bool taken;
|
||||
} BtbUpdate deriving(Bits, Eq, FShow);
|
||||
|
||||
// No synthesize boundary because we need to call predPC several times
|
||||
module mkBtb(NextAddrPred);
|
||||
// Read and Write ordering doesn't matter since this is a predictor
|
||||
// mkRegFileWCF is the RegFile version of mkConfigReg
|
||||
RegFile#(BtbIndex, Addr) next_addrs <- mkRegFileWCF(0,fromInteger(valueOf(BtbEntries)-1));
|
||||
RegFile#(BtbIndex, BtbTag) tags <- mkRegFileWCF(0,fromInteger(valueOf(BtbEntries)-1));
|
||||
Vector#(BtbEntries, Reg#(Bool)) valid <- replicateM(mkConfigReg(False));
|
||||
|
||||
RWire#(BtbUpdate) updateEn <- mkRWire;
|
||||
|
||||
`ifdef SECURITY
|
||||
Reg#(Bool) flushDone <- mkReg(True);
|
||||
`else
|
||||
Bool flushDone = True;
|
||||
`endif
|
||||
|
||||
function BtbIndex getIndex(Addr pc) = truncate(pc >> 2);
|
||||
function BtbTag getTag(Addr pc) = truncateLSB(pc);
|
||||
|
||||
// no flush, accept update
|
||||
(* fire_when_enabled, no_implicit_conditions *)
|
||||
rule canonUpdate(flushDone &&& updateEn.wget matches tagged Valid .upd);
|
||||
let pc = upd.pc;
|
||||
let nextPc = upd.nextPc;
|
||||
let taken = upd.taken;
|
||||
|
||||
let index = getIndex(pc);
|
||||
let tag = getTag(pc);
|
||||
if(taken) begin
|
||||
valid[index] <= True;
|
||||
tags.upd(index, tag);
|
||||
next_addrs.upd(index, nextPc);
|
||||
end else if( tags.sub(index) == tag ) begin
|
||||
// current instruction has target in btb, so clear it
|
||||
valid[index] <= False;
|
||||
end
|
||||
endrule
|
||||
|
||||
`ifdef SECURITY
|
||||
// flush, clear everything (and drop update)
|
||||
rule doFlush(!flushDone);
|
||||
writeVReg(valid, replicate(False));
|
||||
flushDone <= True;
|
||||
endrule
|
||||
`endif
|
||||
|
||||
method Addr predPc(Addr pc);
|
||||
BtbIndex index = getIndex(pc);
|
||||
BtbTag tag = getTag(pc);
|
||||
if(valid[index] && tag == tags.sub(index))
|
||||
return next_addrs.sub(index);
|
||||
else
|
||||
return (pc + 4);
|
||||
endmethod
|
||||
|
||||
method Action update(Addr pc, Addr nextPc, Bool taken);
|
||||
updateEn.wset(BtbUpdate {pc: pc, nextPc: nextPc, taken: taken});
|
||||
endmethod
|
||||
|
||||
`ifdef SECURITY
|
||||
method Action flush if(flushDone);
|
||||
flushDone <= False;
|
||||
endmethod
|
||||
method flush_done = flushDone._read;
|
||||
`else
|
||||
method flush = noAction;
|
||||
method flush_done = True;
|
||||
`endif
|
||||
endmodule
|
||||
|
||||
75
src_Core/RISCY_OOO/procs/lib/Bypass.bsv
Normal file
75
src_Core/RISCY_OOO/procs/lib/Bypass.bsv
Normal file
@@ -0,0 +1,75 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import Vector::*;
|
||||
|
||||
interface SendBypass;
|
||||
method Action send(PhyRIndx dst, Data data);
|
||||
endinterface
|
||||
|
||||
interface RecvBypass;
|
||||
method Action recv(PhyRIndx dst, Data data);
|
||||
endinterface
|
||||
|
||||
function ActionValue#(Data) readRFBypass(
|
||||
PhyRIndx src,
|
||||
Bool rfReady, Data rfData,
|
||||
Vector#(n, RWire#(Tuple2#(PhyRIndx, Data))) bypassWire
|
||||
);
|
||||
actionvalue
|
||||
if(rfReady) begin
|
||||
return rfData;
|
||||
end
|
||||
else begin
|
||||
// search through all bypass
|
||||
function Maybe#(Data) getBypassData(RWire#(Tuple2#(PhyRIndx, Data)) w);
|
||||
if(w.wget matches tagged Valid {.dst, .d} &&& dst == src) begin
|
||||
return Valid (d);
|
||||
end
|
||||
else begin
|
||||
return Invalid;
|
||||
end
|
||||
endfunction
|
||||
Vector#(n, Maybe#(Data)) bypassData = map(getBypassData, bypassWire);
|
||||
if(find(isValid, bypassData) matches tagged Valid .b) begin
|
||||
doAssert(isValid(b), "bypass found must be valid");
|
||||
return validValue(b);
|
||||
end
|
||||
else begin
|
||||
when(False, noAction);
|
||||
return ?;
|
||||
end
|
||||
end
|
||||
endactionvalue
|
||||
endfunction
|
||||
|
||||
function RecvBypass getRecvBypassIfc(RWire#(Tuple2#(PhyRIndx, Data)) w);
|
||||
return (interface RecvBypass;
|
||||
method Action recv(PhyRIndx dst, Data data);
|
||||
w.wset(tuple2(dst, data));
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
153
src_Core/RISCY_OOO/procs/lib/CacheUtils.bsv
Normal file
153
src_Core/RISCY_OOO/procs/lib/CacheUtils.bsv
Normal file
@@ -0,0 +1,153 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import BRAM::*;
|
||||
import Types::*;
|
||||
import MemoryTypes::*;
|
||||
import GetPut::*;
|
||||
import ClientServer::*;
|
||||
import Connectable::*;
|
||||
import Vector::*;
|
||||
import Fifo::*;
|
||||
import Ehr::*;
|
||||
import FIFO::*;
|
||||
import FIFOF::*;
|
||||
import Performance::*;
|
||||
import FShow::*;
|
||||
import MsgFifo::*;
|
||||
|
||||
// 64B cache line
|
||||
typedef 8 CLineNumData;
|
||||
typedef TLog#(CLineNumData) LogCLineNumData;
|
||||
typedef Bit#(LogCLineNumData) CLineDataSel;
|
||||
|
||||
function CLineDataSel getCLineDataSel(Addr a);
|
||||
return truncate(a >> valueOf(TLog#(NumBytes)));
|
||||
endfunction
|
||||
|
||||
typedef TMul#(CLineNumData, DataSz) CacheLineSz;
|
||||
typedef Bit#(CacheLineSz) CacheLine;
|
||||
|
||||
typedef TMul#(CLineNumData, NumBytes) CLineNumBytes;
|
||||
typedef TLog#(CLineNumBytes) LogCLineNumBytes;
|
||||
typedef Vector#(CLineNumBytes, Bool) CLineByteEn;
|
||||
|
||||
function Bool isCLineAlignAddr(Addr a);
|
||||
Bit#(LogCLineNumBytes) offset = truncate(a);
|
||||
return offset == 0;
|
||||
endfunction
|
||||
|
||||
// cache line addr (drop the offset within cache line)
|
||||
typedef TSub#(AddrSz, LogCLineNumBytes) CLineAddrSz;
|
||||
typedef Bit#(CLineAddrSz) CLineAddr;
|
||||
|
||||
// cache line v.s. instruction
|
||||
typedef TDiv#(CacheLineSz, InstSz) CLineNumInst;
|
||||
typedef Bit#(TLog#(CLineNumInst)) CLineInstSel;
|
||||
|
||||
function CLineInstSel getCLineInstSel(Addr a);
|
||||
return truncate(a >> valueof(TLog#(TDiv#(InstSz, 8))));
|
||||
endfunction
|
||||
|
||||
// FIFO enq/deq ifc
|
||||
interface FifoEnq#(type t);
|
||||
method Bool notFull;
|
||||
method Action enq(t x);
|
||||
endinterface
|
||||
|
||||
function FifoEnq#(t) toFifoEnq(Fifo#(n, t) f);
|
||||
return (interface FifoEnq;
|
||||
method notFull = f.notFull;
|
||||
method enq = f.enq;
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function FifoEnq#(t) nullFifoEnq;
|
||||
return (interface FifoEnq;
|
||||
method Bool notFull = True;
|
||||
method Action enq(t x);
|
||||
noAction;
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
instance ToPut#(FifoEnq#(t), t);
|
||||
function Put#(t) toPut(FifoEnq#(t) ifc);
|
||||
return (interface Put;
|
||||
method Action put(t x);
|
||||
ifc.enq(x);
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
endinstance
|
||||
|
||||
interface FifoDeq#(type t);
|
||||
method Bool notEmpty;
|
||||
method Action deq;
|
||||
method t first;
|
||||
endinterface
|
||||
|
||||
function FifoDeq#(t) toFifoDeq(Fifo#(n, t) f);
|
||||
return (interface FifoDeq;
|
||||
method notEmpty = f.notEmpty;
|
||||
method deq = f.deq;
|
||||
method first = f.first;
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
function FifoDeq#(t) nullFifoDeq;
|
||||
return (interface FifoDeq;
|
||||
method Bool notEmpty = False;
|
||||
method Action deq if(False);
|
||||
noAction;
|
||||
endmethod
|
||||
method t first if(False);
|
||||
return ?;
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
|
||||
instance ToGet#(FifoDeq#(t), t);
|
||||
function Get#(t) toGet(FifoDeq#(t) ifc);
|
||||
return (interface Get;
|
||||
method ActionValue#(t) get;
|
||||
ifc.deq;
|
||||
return ifc.first;
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
endinstance
|
||||
|
||||
instance Connectable#(FifoEnq#(t), FifoDeq#(t));
|
||||
module mkConnection#(FifoEnq#(t) enq, FifoDeq#(t) deq)(Empty);
|
||||
mkConnection(toGet(deq), toPut(enq));
|
||||
endmodule
|
||||
endinstance
|
||||
|
||||
instance Connectable#(FifoDeq#(t), FifoEnq#(t));
|
||||
module mkConnection#(FifoDeq#(t) deq, FifoEnq#(t) enq)(Empty);
|
||||
mkConnection(toGet(deq), toPut(enq));
|
||||
endmodule
|
||||
endinstance
|
||||
|
||||
506
src_Core/RISCY_OOO/procs/lib/ConcatReg.bsv
Normal file
506
src_Core/RISCY_OOO/procs/lib/ConcatReg.bsv
Normal file
@@ -0,0 +1,506 @@
|
||||
// This file was created by gen_ConcatReg.py. If you want to modify this file,
|
||||
// please modify gen_ConcatReg.py instead. If you need a wider concatReg
|
||||
// function, change the value of n in gen_ConcatReg.py and run it again.
|
||||
|
||||
// The Bluespec provided BuildVector.bsv provides another example of
|
||||
// constructing a function that takes a variable number of arguments
|
||||
|
||||
// Typeclass for creating _concatReg with a variable number of arguments.
|
||||
typeclass ConcatReg#(type r, numeric type n1, numeric type n2)
|
||||
dependencies ((r,n1) determines n2, (r,n2) determines n1);
|
||||
// dependencies (r determines (n1,n2));
|
||||
function r _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2);
|
||||
endtypeclass
|
||||
// Base case
|
||||
instance ConcatReg#(Reg#(Bit#(n3)), n1, n2) provisos (Add#(n1, n2, n3));
|
||||
function Reg#(Bit#(TAdd#(n1,n2))) _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2);
|
||||
return (interface Reg;
|
||||
method Bit#(TAdd#(n1,n2)) _read = {r1._read, r2._read};
|
||||
method Action _write(Bit#(TAdd#(n1,n2)) x);
|
||||
r1._write(truncateLSB(x));
|
||||
r2._write(truncate(x));
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
endinstance
|
||||
// Recursion
|
||||
instance ConcatReg#(function r f(Reg#(Bit#(n3)) r3), n1, n2) provisos (ConcatReg#(r, TAdd#(n1, n2), n3));
|
||||
function function r f(Reg#(Bit#(n3)) r3) _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2);
|
||||
return _concatReg(interface Reg;
|
||||
method Bit#(TAdd#(n1,n2)) _read = {r1._read, r2._read};
|
||||
method Action _write(Bit#(TAdd#(n1,n2)) x);
|
||||
r1._write(truncateLSB(x));
|
||||
r2._write(truncate(x));
|
||||
endmethod
|
||||
endinterface);
|
||||
endfunction
|
||||
endinstance
|
||||
|
||||
// Wrapper function for users. This can take a variable number of arguments.
|
||||
// You will need to use asReg() for the third argument and beyond.
|
||||
function r concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2) provisos(ConcatReg#(r, n1, n2));
|
||||
return _concatReg(asReg(r1),asReg(r2));
|
||||
endfunction
|
||||
|
||||
// Automatically generated macros with a set number of registers.
|
||||
// These don't require asReg when used.
|
||||
function Reg#(Bit#(n)) concatReg2(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2
|
||||
) provisos (
|
||||
Add#(n1,n2,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg3(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3
|
||||
) provisos (
|
||||
Add#(TAdd#(n1,n2),n3,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg4(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(n1,n2),n3),n4,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg5(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg6(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg7(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg8(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg9(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg10(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg11(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg12(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg13(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg14(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13,
|
||||
Reg#(Bit#(n14)) r14
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg15(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13,
|
||||
Reg#(Bit#(n14)) r14,
|
||||
Reg#(Bit#(n15)) r15
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg16(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13,
|
||||
Reg#(Bit#(n14)) r14,
|
||||
Reg#(Bit#(n15)) r15,
|
||||
Reg#(Bit#(n16)) r16
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg17(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13,
|
||||
Reg#(Bit#(n14)) r14,
|
||||
Reg#(Bit#(n15)) r15,
|
||||
Reg#(Bit#(n16)) r16,
|
||||
Reg#(Bit#(n17)) r17
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg18(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13,
|
||||
Reg#(Bit#(n14)) r14,
|
||||
Reg#(Bit#(n15)) r15,
|
||||
Reg#(Bit#(n16)) r16,
|
||||
Reg#(Bit#(n17)) r17,
|
||||
Reg#(Bit#(n18)) r18
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg19(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13,
|
||||
Reg#(Bit#(n14)) r14,
|
||||
Reg#(Bit#(n15)) r15,
|
||||
Reg#(Bit#(n16)) r16,
|
||||
Reg#(Bit#(n17)) r17,
|
||||
Reg#(Bit#(n18)) r18,
|
||||
Reg#(Bit#(n19)) r19
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg20(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13,
|
||||
Reg#(Bit#(n14)) r14,
|
||||
Reg#(Bit#(n15)) r15,
|
||||
Reg#(Bit#(n16)) r16,
|
||||
Reg#(Bit#(n17)) r17,
|
||||
Reg#(Bit#(n18)) r18,
|
||||
Reg#(Bit#(n19)) r19,
|
||||
Reg#(Bit#(n20)) r20
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg21(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13,
|
||||
Reg#(Bit#(n14)) r14,
|
||||
Reg#(Bit#(n15)) r15,
|
||||
Reg#(Bit#(n16)) r16,
|
||||
Reg#(Bit#(n17)) r17,
|
||||
Reg#(Bit#(n18)) r18,
|
||||
Reg#(Bit#(n19)) r19,
|
||||
Reg#(Bit#(n20)) r20,
|
||||
Reg#(Bit#(n21)) r21
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg22(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13,
|
||||
Reg#(Bit#(n14)) r14,
|
||||
Reg#(Bit#(n15)) r15,
|
||||
Reg#(Bit#(n16)) r16,
|
||||
Reg#(Bit#(n17)) r17,
|
||||
Reg#(Bit#(n18)) r18,
|
||||
Reg#(Bit#(n19)) r19,
|
||||
Reg#(Bit#(n20)) r20,
|
||||
Reg#(Bit#(n21)) r21,
|
||||
Reg#(Bit#(n22)) r22
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21),n22,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21),asReg(r22));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg23(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13,
|
||||
Reg#(Bit#(n14)) r14,
|
||||
Reg#(Bit#(n15)) r15,
|
||||
Reg#(Bit#(n16)) r16,
|
||||
Reg#(Bit#(n17)) r17,
|
||||
Reg#(Bit#(n18)) r18,
|
||||
Reg#(Bit#(n19)) r19,
|
||||
Reg#(Bit#(n20)) r20,
|
||||
Reg#(Bit#(n21)) r21,
|
||||
Reg#(Bit#(n22)) r22,
|
||||
Reg#(Bit#(n23)) r23
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21),n22),n23,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21),asReg(r22),asReg(r23));
|
||||
endfunction
|
||||
|
||||
function Reg#(Bit#(n)) concatReg24(
|
||||
Reg#(Bit#(n1)) r1,
|
||||
Reg#(Bit#(n2)) r2,
|
||||
Reg#(Bit#(n3)) r3,
|
||||
Reg#(Bit#(n4)) r4,
|
||||
Reg#(Bit#(n5)) r5,
|
||||
Reg#(Bit#(n6)) r6,
|
||||
Reg#(Bit#(n7)) r7,
|
||||
Reg#(Bit#(n8)) r8,
|
||||
Reg#(Bit#(n9)) r9,
|
||||
Reg#(Bit#(n10)) r10,
|
||||
Reg#(Bit#(n11)) r11,
|
||||
Reg#(Bit#(n12)) r12,
|
||||
Reg#(Bit#(n13)) r13,
|
||||
Reg#(Bit#(n14)) r14,
|
||||
Reg#(Bit#(n15)) r15,
|
||||
Reg#(Bit#(n16)) r16,
|
||||
Reg#(Bit#(n17)) r17,
|
||||
Reg#(Bit#(n18)) r18,
|
||||
Reg#(Bit#(n19)) r19,
|
||||
Reg#(Bit#(n20)) r20,
|
||||
Reg#(Bit#(n21)) r21,
|
||||
Reg#(Bit#(n22)) r22,
|
||||
Reg#(Bit#(n23)) r23,
|
||||
Reg#(Bit#(n24)) r24
|
||||
) provisos (
|
||||
Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21),n22),n23),n24,n)
|
||||
);
|
||||
return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21),asReg(r22),asReg(r23),asReg(r24));
|
||||
endfunction
|
||||
|
||||
606
src_Core/RISCY_OOO/procs/lib/DTlb.bsv
Normal file
606
src_Core/RISCY_OOO/procs/lib/DTlb.bsv
Normal file
@@ -0,0 +1,606 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
import ClientServer::*;
|
||||
import DefaultValue::*;
|
||||
import GetPut::*;
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import TlbTypes::*;
|
||||
import Performance::*;
|
||||
import FullAssocTlb::*;
|
||||
import ConfigReg::*;
|
||||
import Fifo::*;
|
||||
import Cntrs::*;
|
||||
import SafeCounter::*;
|
||||
import CacheUtils::*;
|
||||
import LatencyTimer::*;
|
||||
import HasSpecBits::*;
|
||||
import Vector::*;
|
||||
import Ehr::*;
|
||||
|
||||
export DTlbReq(..);
|
||||
export DTlbResp(..);
|
||||
export DTlbRqToP(..);
|
||||
export DTlbTransRsFromP(..);
|
||||
export DTlbToParent(..);
|
||||
export DTlb(..);
|
||||
export mkDTlb;
|
||||
|
||||
typedef `L1_TLB_SIZE DTlbSize;
|
||||
|
||||
// req & resp with core
|
||||
// D TLB also keeps the information of the requesting inst, so we don't need
|
||||
// extra bookkeeping outside D TLB.
|
||||
typedef struct {
|
||||
instT inst;
|
||||
SpecBits specBits;
|
||||
} DTlbReq#(type instT) deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
TlbResp resp;
|
||||
instT inst;
|
||||
SpecBits specBits;
|
||||
} DTlbResp#(type instT) deriving(Bits, Eq, FShow);
|
||||
|
||||
// req & resp with L2 TLB
|
||||
typedef struct {
|
||||
Vpn vpn;
|
||||
DTlbReqIdx id;
|
||||
} DTlbRqToP deriving(Bits, Eq, FShow);
|
||||
|
||||
typedef struct {
|
||||
// may get page fault: i.e. hit invalid page or
|
||||
// get non-leaf page at last-level page table
|
||||
Maybe#(TlbEntry) entry;
|
||||
DTlbReqIdx id;
|
||||
} DTlbTransRsFromP deriving(Bits, Eq, FShow);
|
||||
|
||||
interface DTlbToParent;
|
||||
interface FifoDeq#(DTlbRqToP) rqToP;
|
||||
interface FifoEnq#(DTlbTransRsFromP) ldTransRsFromP;
|
||||
// after DTLB flush itself, it notifies L2, and wait L2 to flush
|
||||
interface Client#(void, void) flush;
|
||||
endinterface
|
||||
|
||||
interface DTlb#(type instT);
|
||||
// system consistency related
|
||||
method Bool flush_done;
|
||||
method Action flush;
|
||||
method Action updateVMInfo(VMInfo vm);
|
||||
method Bool noPendingReq;
|
||||
|
||||
// req/resp with core
|
||||
method Action procReq(DTlbReq#(instT) req);
|
||||
method DTlbResp#(instT) procResp;
|
||||
method Action deqProcResp;
|
||||
|
||||
// req/resp with L2 TLB
|
||||
interface DTlbToParent toParent;
|
||||
|
||||
// speculation
|
||||
interface SpeculationUpdate specUpdate;
|
||||
|
||||
// performance
|
||||
interface Perf#(L1TlbPerfType) perf;
|
||||
endinterface
|
||||
|
||||
typedef FullAssocTlb#(DTlbSize) DTlbArray;
|
||||
module mkDTlbArray(DTlbArray);
|
||||
let m <- mkFullAssocTlb(True); // randomness in replacement
|
||||
return m;
|
||||
endmodule
|
||||
|
||||
// a pending tlb req may be in following states
|
||||
typedef union tagged {
|
||||
void None;
|
||||
void WaitParent;
|
||||
DTlbReqIdx WaitPeer;
|
||||
} DTlbWait deriving(Bits, Eq, FShow);
|
||||
|
||||
module mkDTlb#(
|
||||
function TlbReq getTlbReq(instT inst)
|
||||
)(DTlb::DTlb#(instT)) provisos(Bits#(instT, a__));
|
||||
Bool verbose = True;
|
||||
|
||||
// TLB array
|
||||
DTlbArray tlb <- mkDTlbArray;
|
||||
|
||||
// processor init flushing by setting this flag
|
||||
Reg#(Bool) needFlush <- mkReg(False);
|
||||
// after flushing ITLB itself, we want parent TLB to flush
|
||||
Reg#(Bool) waitFlushP <- mkReg(False);
|
||||
|
||||
// current processor VM information
|
||||
Reg#(VMInfo) vm_info <- mkReg(defaultValue);
|
||||
|
||||
// pending reqs
|
||||
// pendWait should be meaningful even when entry is invalid. pendWait =
|
||||
// WaitParent True means this entry is waiting for parent TLB resp;
|
||||
// pendWait = WaitPeer means this entry is waiting for a resp initiated by
|
||||
// another req. Thus, pendWait must be None when entry is invalid.
|
||||
Vector#(DTlbReqNum, Ehr#(2, Bool)) pendValid <- replicateM(mkEhr(False));
|
||||
Vector#(DTlbReqNum, Reg#(DTlbWait)) pendWait <- replicateM(mkReg(None));
|
||||
Vector#(DTlbReqNum, Reg#(Bool)) pendPoisoned <- replicateM(mkRegU);
|
||||
Vector#(DTlbReqNum, Reg#(instT)) pendInst <- replicateM(mkRegU);
|
||||
Vector#(DTlbReqNum, Reg#(TlbResp)) pendResp <- replicateM(mkRegU);
|
||||
Vector#(DTlbReqNum, Ehr#(2, SpecBits)) pendSpecBits <- replicateM(mkEhr(?));
|
||||
|
||||
// ordering of methods/rules that access pend reqs
|
||||
// procReq mutually exclusive with doPRs (no procReq when pRs ready)
|
||||
// procResp < {doPRs, procReq}
|
||||
// wrongSpec C {procReq, doPRs, procResp}
|
||||
// correctSpec C wrongSpec
|
||||
// correctSpec CF doPRs
|
||||
// {procReq, procResp} < correctSpec (correctSpec is always at end)
|
||||
|
||||
RWire#(void) wrongSpec_procResp_conflict <- mkRWire;
|
||||
RWire#(void) wrongSpec_doPRs_conflict <- mkRWire;
|
||||
RWire#(void) wrongSpec_procReq_conflict <- mkRWire;
|
||||
|
||||
let pendValid_noMiss = getVEhrPort(pendValid, 0);
|
||||
let pendValid_wrongSpec = getVEhrPort(pendValid, 0);
|
||||
let pendValid_procResp = getVEhrPort(pendValid, 0); // write
|
||||
let pendValid_doPRs = getVEhrPort(pendValid, 1); // assert
|
||||
let pendValid_procReq = getVEhrPort(pendValid, 1); // write
|
||||
|
||||
let pendSpecBits_wrongSpec = getVEhrPort(pendSpecBits, 0);
|
||||
let pendSpecBits_procResp = getVEhrPort(pendSpecBits, 0);
|
||||
let pendSpecBits_procReq = getVEhrPort(pendSpecBits, 0); // write
|
||||
let pendSpecBits_correctSpec = getVEhrPort(pendSpecBits, 1);
|
||||
|
||||
// free list of pend entries, to cut off path from procResp to procReq
|
||||
Fifo#(DTlbReqNum, DTlbReqIdx) freeQ <- mkCFFifo;
|
||||
Reg#(Bool) freeQInited <- mkReg(False);
|
||||
Reg#(DTlbReqIdx) freeQInitIdx <- mkReg(0);
|
||||
|
||||
// req & resp with parent TLB
|
||||
Fifo#(DTlbReqNum, DTlbRqToP) rqToPQ <- mkCFFifo; // large enough so won't block on enq
|
||||
Fifo#(2, DTlbTransRsFromP) ldTransRsFromPQ <- mkCFFifo;
|
||||
// When a resp comes, we first process for the initiating req, then process
|
||||
// other reqs that in WaitPeer.
|
||||
Reg#(Maybe#(DTlbReqIdx)) respForOtherReq <- mkReg(Invalid);
|
||||
// flush req/resp with parent TLB
|
||||
Fifo#(1, void) flushRqToPQ <- mkCFFifo;
|
||||
Fifo#(1, void) flushRsFromPQ <- mkCFFifo;
|
||||
|
||||
// perf counters
|
||||
Fifo#(1, L1TlbPerfType) perfReqQ <- mkCFFifo;
|
||||
`ifdef PERF_COUNT
|
||||
Fifo#(1, PerfResp#(L1TlbPerfType)) perfRespQ <- mkCFFifo;
|
||||
Reg#(Bool) doStats <- mkConfigReg(False);
|
||||
Count#(Data) accessCnt <- mkCount(0);
|
||||
Count#(Data) missParentCnt <- mkCount(0);
|
||||
Count#(Data) missParentLat <- mkCount(0);
|
||||
Count#(Data) missPeerCnt <- mkCount(0);
|
||||
Count#(Data) missPeerLat <- mkCount(0);
|
||||
Count#(Data) hitUnderMissCnt <- mkCount(0);
|
||||
Count#(Data) allMissCycles <- mkCount(0);
|
||||
|
||||
LatencyTimer#(DTlbReqNum, 12) latTimer <- mkLatencyTimer; // max latency: 4K cycles
|
||||
|
||||
rule doPerf;
|
||||
let t <- toGet(perfReqQ).get;
|
||||
Data d = (case(t)
|
||||
L1TlbAccessCnt: (accessCnt);
|
||||
L1TlbMissParentCnt: (missParentCnt);
|
||||
L1TlbMissParentLat: (missParentLat);
|
||||
L1TlbMissPeerCnt: (missPeerCnt);
|
||||
L1TlbMissPeerLat: (missPeerLat);
|
||||
L1TlbHitUnderMissCnt: (hitUnderMissCnt);
|
||||
L1TlbAllMissCycles: (allMissCycles);
|
||||
default: (0);
|
||||
endcase);
|
||||
perfRespQ.enq(PerfResp {
|
||||
pType: t,
|
||||
data: d
|
||||
});
|
||||
endrule
|
||||
|
||||
rule incrAllMissCycles(doStats);
|
||||
function Bool isMiss(DTlbWait x) = x != None;
|
||||
when(all(isMiss, readVReg(pendWait)), allMissCycles.incr(1));
|
||||
endrule
|
||||
`endif
|
||||
|
||||
// do flush: start when all misses resolve
|
||||
Bool noMiss = all(\== (False) , readVReg(pendValid_noMiss));
|
||||
|
||||
rule doStartFlush(needFlush && !waitFlushP && noMiss);
|
||||
tlb.flush;
|
||||
// request parent TLB to flush
|
||||
flushRqToPQ.enq(?);
|
||||
waitFlushP <= True;
|
||||
if(verbose) $display("[DTLB] flush begin");
|
||||
endrule
|
||||
|
||||
rule doFinishFlush(needFlush && waitFlushP);
|
||||
flushRsFromPQ.deq;
|
||||
needFlush <= False;
|
||||
waitFlushP <= False;
|
||||
if(verbose) $display("[DTLB] flush done");
|
||||
endrule
|
||||
|
||||
// get resp from parent TLB
|
||||
// At high level, this rule is always exclusive with doStartFlush, though
|
||||
// we don't bother to make compiler understand this...
|
||||
rule doPRs(ldTransRsFromPQ.notEmpty);
|
||||
let pRs = ldTransRsFromPQ.first;
|
||||
// the current req being served is either the initiating req or other
|
||||
// req pending on the same resp
|
||||
let idx = fromMaybe(pRs.id, respForOtherReq);
|
||||
TlbReq r = getTlbReq(pendInst[idx]);
|
||||
|
||||
if(pendPoisoned[idx]) begin
|
||||
// poisoned inst, do nothing
|
||||
if(verbose) $display("[DTLB] refill poisoned: idx %d; ", idx, fshow(r));
|
||||
end
|
||||
else if(pRs.entry matches tagged Valid .en) begin
|
||||
// check permission
|
||||
if(hasVMPermission(vm_info,
|
||||
en.pteType,
|
||||
en.ppn,
|
||||
en.level,
|
||||
r.write ? DataStore : DataLoad)) begin
|
||||
// fill TLB, and record resp
|
||||
tlb.addEntry(en);
|
||||
let trans_addr = translate(r.addr, en.ppn, en.level);
|
||||
pendResp[idx] <= tuple2(trans_addr, Invalid);
|
||||
if(verbose) begin
|
||||
$display("[DTLB] refill: idx %d; ", idx, fshow(r),
|
||||
"; ", fshow(trans_addr));
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// page fault
|
||||
Exception fault = r.write ? StorePageFault : LoadPageFault;
|
||||
pendResp[idx] <= tuple2(?, Valid (fault));
|
||||
if(verbose) begin
|
||||
$display("[DTLB] refill no permission: idx %d; ", idx, fshow(r));
|
||||
end
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// page fault
|
||||
Exception fault = r.write ? StorePageFault : LoadPageFault;
|
||||
pendResp[idx] <= tuple2(?, Valid (fault));
|
||||
if(verbose) $display("[DTLB] refill page fault: idx %d; ", idx, fshow(r));
|
||||
end
|
||||
|
||||
// get parent resp, miss resolved, reset wait bit
|
||||
pendWait[idx] <= None;
|
||||
|
||||
doAssert(pendValid_doPRs[idx], "entry must be valid");
|
||||
if(isValid(respForOtherReq)) begin
|
||||
doAssert(pendWait[idx] == WaitPeer (pRs.id), "entry must be waiting for resp");
|
||||
end
|
||||
else begin
|
||||
doAssert(pendWait[idx] == WaitParent, "entry must be waiting for resp");
|
||||
end
|
||||
|
||||
// find another req waiting for this resp
|
||||
function Bool waitForResp(DTlbReqIdx i);
|
||||
// we can ignore pendValid here, because not-None pendWait implies
|
||||
// pendValid is true
|
||||
return pendWait[i] == WaitPeer (pRs.id) && i != idx;
|
||||
endfunction
|
||||
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
|
||||
if(find(waitForResp, idxVec) matches tagged Valid .i) begin
|
||||
// still have req waiting for this resp, keep processing
|
||||
respForOtherReq <= Valid (i);
|
||||
doAssert(pendValid_doPRs[i], "waiting entry must be valid");
|
||||
end
|
||||
else begin
|
||||
// all req done, deq the pRs
|
||||
respForOtherReq <= Invalid;
|
||||
ldTransRsFromPQ.deq;
|
||||
end
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
// perf: miss
|
||||
let lat <- latTimer.done(idx);
|
||||
if(doStats) begin
|
||||
if(isValid(respForOtherReq)) begin
|
||||
missPeerLat.incr(zeroExtend(lat));
|
||||
missPeerCnt.incr(1);
|
||||
end
|
||||
else begin
|
||||
missParentLat.incr(zeroExtend(lat));
|
||||
missParentCnt.incr(1);
|
||||
end
|
||||
end
|
||||
`endif
|
||||
|
||||
// conflict with wrong spec
|
||||
wrongSpec_doPRs_conflict.wset(?);
|
||||
endrule
|
||||
|
||||
// init freeQ
|
||||
rule doInitFreeQ(!freeQInited);
|
||||
freeQ.enq(freeQInitIdx);
|
||||
freeQInitIdx <= freeQInitIdx + 1;
|
||||
if(freeQInitIdx == fromInteger(valueof(DTlbReqNum) - 1)) begin
|
||||
freeQInited <= True;
|
||||
end
|
||||
endrule
|
||||
|
||||
// idx of entries that are ready to resp to proc
|
||||
function Maybe#(DTlbReqIdx) validProcRespIdx;
|
||||
function Bool validResp(DTlbReqIdx i);
|
||||
return pendValid_procResp[i] && pendWait[i] == None && !pendPoisoned[i];
|
||||
endfunction
|
||||
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
|
||||
return find(validResp, idxVec);
|
||||
endfunction
|
||||
|
||||
function Maybe#(DTlbReqIdx) poisonedProcRespIdx;
|
||||
function Bool poisonedResp(DTlbReqIdx i);
|
||||
return pendValid_procResp[i] && pendWait[i] == None && pendPoisoned[i];
|
||||
endfunction
|
||||
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
|
||||
return find(poisonedResp, idxVec);
|
||||
endfunction
|
||||
|
||||
// drop poisoned resp
|
||||
rule doPoisonedProcResp(poisonedProcRespIdx matches tagged Valid .idx &&& freeQInited);
|
||||
pendValid_procResp[idx] <= False;
|
||||
freeQ.enq(idx);
|
||||
// conflict with wrong spec
|
||||
wrongSpec_procResp_conflict.wset(?);
|
||||
endrule
|
||||
|
||||
method Action flush if(!needFlush);
|
||||
needFlush <= True;
|
||||
waitFlushP <= False;
|
||||
// this won't interrupt current processing, since
|
||||
// (1) miss process will continue even if needFlush=True
|
||||
// (2) flush truly starts when there is no pending req
|
||||
endmethod
|
||||
|
||||
method Bool flush_done = !needFlush;
|
||||
|
||||
method Action updateVMInfo(VMInfo vm);
|
||||
vm_info <= vm;
|
||||
endmethod
|
||||
|
||||
// Since this method is called at commit stage to determine no in-flight
|
||||
// TLB req, even poisoned req should be considered as pending, because it
|
||||
// may be in L2 TLB.
|
||||
method Bool noPendingReq = noMiss;
|
||||
|
||||
// We do not accept new req when flushing flag is set. We also do not
|
||||
// accept new req when parent resp is ready. This avoids bypass in TLB. We
|
||||
// also check rqToPQ not full. This simplifies the guard, i.e., it does not
|
||||
// depend on whether we hit in TLB or not.
|
||||
method Action procReq(DTlbReq#(instT) req) if(
|
||||
!needFlush && !ldTransRsFromPQ.notEmpty && rqToPQ.notFull && freeQInited
|
||||
);
|
||||
// allocate MSHR entry
|
||||
freeQ.deq;
|
||||
DTlbReqIdx idx = freeQ.first;
|
||||
doAssert(!pendValid_procReq[idx], "free entry cannot be valid");
|
||||
doAssert(pendWait[idx] == None, "entry cannot wait for parent resp");
|
||||
|
||||
pendValid_procReq[idx] <= True;
|
||||
pendPoisoned[idx] <= False;
|
||||
pendInst[idx] <= req.inst;
|
||||
pendSpecBits_procReq[idx] <= req.specBits;
|
||||
// pendWait and pendResp are set later in this method
|
||||
|
||||
// try to translate
|
||||
TlbReq r = getTlbReq(req.inst);
|
||||
|
||||
`ifdef SECURITY
|
||||
// Security check
|
||||
// Forbid any data load shared outside of the protection domain
|
||||
// if shared load are not allowed
|
||||
// No need to special case M mode with special vm_info value because we
|
||||
// assume that we allow shared load all the time when in M mode.
|
||||
// (Because we are always non speculative in M mode)
|
||||
if (!vm_info.sanctum_authShared && outOfProtectionDomain(vm_info, r.addr))begin
|
||||
pendWait[idx] <= None;
|
||||
pendResp[idx] <= tuple2(?, Valid (LoadAccessFault));
|
||||
end
|
||||
`else
|
||||
// No security check
|
||||
if (False) begin
|
||||
noAction;
|
||||
end
|
||||
`endif
|
||||
else if (vm_info.sv39) begin
|
||||
let vpn = getVpn(r.addr);
|
||||
let trans_result = tlb.translate(vpn, vm_info.asid);
|
||||
if (trans_result.hit) begin
|
||||
// TLB hit
|
||||
let entry = trans_result.entry;
|
||||
// check permission
|
||||
if (hasVMPermission(vm_info,
|
||||
entry.pteType,
|
||||
entry.ppn,
|
||||
entry.level,
|
||||
r.write ? DataStore : DataLoad)) begin
|
||||
// update TLB replacement info
|
||||
tlb.updateRepByHit(trans_result.index);
|
||||
// translate addr
|
||||
Addr trans_addr = translate(r.addr, entry.ppn, entry.level);
|
||||
pendWait[idx] <= None;
|
||||
pendResp[idx] <= tuple2(trans_addr, Invalid);
|
||||
if(verbose) begin
|
||||
$display("[DTLB] req (hit): idx %d; ", idx, fshow(r),
|
||||
"; ", fshow(trans_result));
|
||||
end
|
||||
`ifdef PERF_COUNT
|
||||
// perf: hit under miss
|
||||
if(doStats && readVReg(pendWait) != replicate(None)) begin
|
||||
hitUnderMissCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
end
|
||||
else begin
|
||||
// page fault
|
||||
Exception fault = r.write ? StorePageFault : LoadPageFault;
|
||||
pendWait[idx] <= None;
|
||||
pendResp[idx] <= tuple2(?, Valid (fault));
|
||||
if(verbose) $display("[DTLB] req no permission: idx %d; ", idx, fshow(r));
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// TLB miss, req to parent TLB only if there is no existing req
|
||||
// for the same VPN already waiting for parent TLB resp
|
||||
function Bool reqSamePage(DTlbReqIdx i);
|
||||
// we can ignore pendValid here, because not-None pendWait implies
|
||||
// pendValid is true
|
||||
let r_i = getTlbReq(pendInst[i]);
|
||||
return pendWait[i] == WaitParent && getVpn(r.addr) == getVpn(r_i.addr);
|
||||
endfunction
|
||||
Vector#(DTlbReqNum, DTlbReqIdx) idxVec = genWith(fromInteger);
|
||||
if(find(reqSamePage, idxVec) matches tagged Valid .i) begin
|
||||
// peer entry has already requested, so don't send duplicate req
|
||||
pendWait[idx] <= WaitPeer (i);
|
||||
doAssert(pendValid_procReq[i], "peer entry must be valid");
|
||||
if(verbose) begin
|
||||
$display("[DTLB] req miss, pend on peer: idx %d, ",
|
||||
idx, "; ", fshow(r), "; ", fshow(i));
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// this is the first req for this VPN
|
||||
pendWait[idx] <= WaitParent;
|
||||
rqToPQ.enq(DTlbRqToP {
|
||||
vpn: vpn,
|
||||
id: idx
|
||||
});
|
||||
if(verbose) begin
|
||||
$display("[DTLB] req miss, send to parent: idx %d, ",
|
||||
idx, fshow(r));
|
||||
end
|
||||
end
|
||||
`ifdef PERF_COUNT
|
||||
// perf: miss
|
||||
latTimer.start(idx);
|
||||
`endif
|
||||
end
|
||||
end
|
||||
else begin
|
||||
// bare mode
|
||||
pendWait[idx] <= None;
|
||||
pendResp[idx] <= tuple2(r.addr, Invalid);
|
||||
if(verbose) $display("DTLB %m req (bare): ", fshow(r));
|
||||
end
|
||||
|
||||
`ifdef PERF_COUNT
|
||||
// perf: access
|
||||
if(doStats) begin
|
||||
accessCnt.incr(1);
|
||||
end
|
||||
`endif
|
||||
// conflict with wrong spec
|
||||
wrongSpec_procReq_conflict.wset(?);
|
||||
endmethod
|
||||
|
||||
method Action deqProcResp if(
|
||||
validProcRespIdx matches tagged Valid .idx &&& freeQInited
|
||||
);
|
||||
pendValid_procResp[idx] <= False;
|
||||
freeQ.enq(idx);
|
||||
// conflict with wrong spec
|
||||
wrongSpec_procResp_conflict.wset(?);
|
||||
endmethod
|
||||
|
||||
method DTlbResp#(instT) procResp if(
|
||||
validProcRespIdx matches tagged Valid .idx &&& freeQInited
|
||||
);
|
||||
return DTlbResp {
|
||||
inst: pendInst[idx],
|
||||
resp: pendResp[idx],
|
||||
specBits: pendSpecBits_procResp[idx]
|
||||
};
|
||||
endmethod
|
||||
|
||||
interface DTlbToParent toParent;
|
||||
interface rqToP = toFifoDeq(rqToPQ);
|
||||
interface ldTransRsFromP = toFifoEnq(ldTransRsFromPQ);
|
||||
interface Client flush;
|
||||
interface request = toGet(flushRqToPQ);
|
||||
interface response = toPut(flushRsFromPQ);
|
||||
endinterface
|
||||
endinterface
|
||||
|
||||
interface SpeculationUpdate specUpdate;
|
||||
method Action incorrectSpeculation(Bool kill_all, SpecTag x);
|
||||
// poison entries
|
||||
for(Integer i = 0 ; i < valueOf(DTlbReqNum) ; i = i+1) begin
|
||||
if(kill_all || pendSpecBits_wrongSpec[i][x] == 1'b1) begin
|
||||
pendPoisoned[i] <= True;
|
||||
end
|
||||
end
|
||||
// make conflicts with procReq, doPRs, procResp
|
||||
wrongSpec_procReq_conflict.wset(?);
|
||||
wrongSpec_doPRs_conflict.wset(?);
|
||||
wrongSpec_procResp_conflict.wset(?);
|
||||
endmethod
|
||||
method Action correctSpeculation(SpecBits mask);
|
||||
// clear spec bits for all entries
|
||||
for(Integer i = 0 ; i < valueOf(DTlbReqNum) ; i = i+1) begin
|
||||
let new_spec_bits = pendSpecBits_correctSpec[i] & mask;
|
||||
pendSpecBits_correctSpec[i] <= new_spec_bits;
|
||||
end
|
||||
endmethod
|
||||
endinterface
|
||||
|
||||
interface Perf perf;
|
||||
method Action setStatus(Bool stats);
|
||||
`ifdef PERF_COUNT
|
||||
doStats <= stats;
|
||||
`else
|
||||
noAction;
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method Action req(L1TlbPerfType r);
|
||||
perfReqQ.enq(r);
|
||||
endmethod
|
||||
|
||||
method ActionValue#(PerfResp#(L1TlbPerfType)) resp;
|
||||
`ifdef PERF_COUNT
|
||||
perfRespQ.deq;
|
||||
return perfRespQ.first;
|
||||
`else
|
||||
perfReqQ.deq;
|
||||
return PerfResp {
|
||||
pType: perfReqQ.first,
|
||||
data: 0
|
||||
};
|
||||
`endif
|
||||
endmethod
|
||||
|
||||
method Bool respValid;
|
||||
`ifdef PERF_COUNT
|
||||
return perfRespQ.notEmpty;
|
||||
`else
|
||||
return perfReqQ.notEmpty;
|
||||
`endif
|
||||
endmethod
|
||||
endinterface
|
||||
endmodule
|
||||
743
src_Core/RISCY_OOO/procs/lib/Decode.bsv
Normal file
743
src_Core/RISCY_OOO/procs/lib/Decode.bsv
Normal file
@@ -0,0 +1,743 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import MemoryTypes::*;
|
||||
import Vector::*;
|
||||
import DefaultValue::*;
|
||||
|
||||
Bit#(3) memWU = 3'b110;
|
||||
|
||||
// Smaller decode functions
|
||||
function Maybe#(MemInst) decodeMemInst(Instruction inst);
|
||||
MemInst mem_inst = MemInst{ mem_func: ?,
|
||||
amo_func: None,
|
||||
unsignedLd: False,
|
||||
byteEn: replicate(False),
|
||||
aq: False,
|
||||
rl: False };
|
||||
Bool illegalInst = False;
|
||||
Opcode opcode = unpackOpcode(inst[6:0]);
|
||||
let funct5 = inst[31:27];
|
||||
let funct3 = inst[14:12];
|
||||
|
||||
// mem_func + amo_func
|
||||
MemFunc mem_func = Ld;
|
||||
AmoFunc amo_func = None;
|
||||
if (opcode == Load || opcode == LoadFp) begin
|
||||
mem_func = Ld;
|
||||
end else if (opcode == Store || opcode == StoreFp) begin
|
||||
mem_func = St;
|
||||
end else if (opcode == Amo) begin
|
||||
case (funct5)
|
||||
fnLR : mem_func = Lr;
|
||||
fnSC : mem_func = Sc;
|
||||
fnAMOSWAP,
|
||||
fnAMOADD,
|
||||
fnAMOXOR,
|
||||
fnAMOAND,
|
||||
fnAMOOR,
|
||||
fnAMOMIN,
|
||||
fnAMOMAX,
|
||||
fnAMOMINU,
|
||||
fnAMOMAXU : mem_func = Amo;
|
||||
default : illegalInst = True;
|
||||
endcase
|
||||
// now for amo_func
|
||||
case (funct5)
|
||||
fnAMOSWAP : amo_func = Swap;
|
||||
fnAMOADD : amo_func = Add;
|
||||
fnAMOXOR : amo_func = Xor;
|
||||
fnAMOAND : amo_func = And;
|
||||
fnAMOOR : amo_func = Or;
|
||||
fnAMOMIN : amo_func = Min;
|
||||
fnAMOMAX : amo_func = Max;
|
||||
fnAMOMINU : amo_func = Minu;
|
||||
fnAMOMAXU : amo_func = Maxu;
|
||||
endcase
|
||||
end else begin
|
||||
illegalInst = True;
|
||||
end
|
||||
|
||||
// unsignedLd
|
||||
// it doesn't matter if this is set to True for stores
|
||||
Bool unsignedLd = False;
|
||||
case (funct3)
|
||||
memB, memH, memW, memD:
|
||||
unsignedLd = False;
|
||||
memBU, memHU, memWU:
|
||||
unsignedLd = True;
|
||||
default:
|
||||
illegalInst = True;
|
||||
endcase
|
||||
// This is a minor fix to make our processor's results match spike since
|
||||
// they don't sign extend when loading single precision values from memory
|
||||
if (opcode == LoadFp) begin
|
||||
unsignedLd = True;
|
||||
end
|
||||
|
||||
// byteEn
|
||||
// TODO: Some combinations of operations and byteEn's are illegal.
|
||||
// They should be detected here.
|
||||
ByteEn byteEn = replicate(False);
|
||||
case (funct3)
|
||||
memB, memBU : byteEn[0] = True;
|
||||
memH, memHU : begin
|
||||
byteEn[0] = True;
|
||||
byteEn[1] = True;
|
||||
end
|
||||
memW, memWU : begin
|
||||
byteEn[0] = True;
|
||||
byteEn[1] = True;
|
||||
byteEn[2] = True;
|
||||
byteEn[3] = True;
|
||||
end
|
||||
memD : byteEn = replicate(True);
|
||||
default : illegalInst = True;
|
||||
endcase
|
||||
|
||||
// aq + rl
|
||||
Bool aq = False;
|
||||
Bool rl = False;
|
||||
if (opcode == Amo) begin
|
||||
// aq and rl are only defined for Amo operations
|
||||
aq = unpack(inst[ 26 ]);
|
||||
rl = unpack(inst[ 25 ]);
|
||||
end
|
||||
|
||||
if (illegalInst) begin
|
||||
return tagged Invalid;
|
||||
end else begin
|
||||
return tagged Valid ( MemInst{
|
||||
mem_func: mem_func,
|
||||
amo_func: amo_func,
|
||||
unsignedLd: unsignedLd,
|
||||
byteEn: byteEn,
|
||||
aq: aq,
|
||||
rl: rl } );
|
||||
end
|
||||
endfunction
|
||||
|
||||
(* noinline *)
|
||||
function DecodeResult decode(Instruction inst);
|
||||
RiscVISASubset isa = defaultValue;
|
||||
|
||||
// initialize dInst with default values
|
||||
DecodedInst dInst = DecodedInst {
|
||||
iType: Unsupported,
|
||||
execFunc: tagged Other,
|
||||
csr: tagged Invalid,
|
||||
imm: tagged Invalid
|
||||
};
|
||||
ArchRegs regs = ArchRegs {
|
||||
src1: tagged Invalid,
|
||||
src2: tagged Invalid,
|
||||
src3: tagged Invalid,
|
||||
dst: tagged Invalid
|
||||
};
|
||||
Bool illegalInst = False;
|
||||
|
||||
Opcode opcode = unpackOpcode(inst[ 6 : 0 ]);
|
||||
let rd = inst[ 11 : 7 ];
|
||||
let funct3 = inst[ 14 : 12 ];
|
||||
let rs1 = inst[ 19 : 15 ];
|
||||
let rs2 = inst[ 24 : 20 ];
|
||||
let funct7 = inst[ 31 : 25 ];
|
||||
// For "F" and "D" ISA extensions
|
||||
let funct5 = inst[ 31 : 27 ];
|
||||
let fmt = inst[ 26 : 25 ];
|
||||
let rs3 = inst[ 31 : 27 ];
|
||||
let funct2 = inst[ 26 : 25 ];
|
||||
// For "A" ISA extension
|
||||
Bool aq = unpack(inst[ 26 ]);
|
||||
Bool rl = unpack(inst[ 25 ]);
|
||||
|
||||
ImmData immI = signExtend(inst[31:20]);
|
||||
ImmData immS = signExtend({ inst[31:25], inst[11:7] });
|
||||
ImmData immB = signExtend({ inst[31], inst[7], inst[30:25], inst[11:8], 1'b0});
|
||||
ImmData immU = signExtend({ inst[31:12], 12'b0 });
|
||||
ImmData immJ = signExtend({ inst[31], inst[19:12], inst[20], inst[30:21], 1'b0});
|
||||
|
||||
// Results of mini-decoders
|
||||
Maybe#(MemInst) mem_inst = decodeMemInst(inst);
|
||||
|
||||
// TODO better detection of illegal insts
|
||||
case (opcode)
|
||||
OpImm: begin
|
||||
dInst.iType = Alu;
|
||||
dInst.execFunc = tagged Alu (case (funct3)
|
||||
fnADD: Add;
|
||||
fnSLT: Slt;
|
||||
fnSLTU: Sltu;
|
||||
fnAND: And;
|
||||
fnOR: Or;
|
||||
fnXOR: Xor;
|
||||
fnSLL: Sll;
|
||||
fnSR: (immI[10] == 0 ? Srl : Sra);
|
||||
endcase);
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Invalid;
|
||||
dInst.imm = Valid(immI);
|
||||
dInst.csr = tagged Invalid;
|
||||
end
|
||||
|
||||
OpImm32: begin
|
||||
dInst.iType = Alu;
|
||||
dInst.execFunc = tagged Alu (case (funct3)
|
||||
fnADD: Addw;
|
||||
fnSLL: Sllw;
|
||||
fnSR: (immI[10] == 0 ? Srlw : Sraw);
|
||||
endcase);
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Invalid;
|
||||
dInst.imm = Valid(immI);
|
||||
dInst.csr = tagged Invalid;
|
||||
end
|
||||
|
||||
Op: begin
|
||||
dInst.iType = Alu;
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Valid(tagged Gpr rs2);
|
||||
dInst.imm = Invalid;
|
||||
dInst.csr = tagged Invalid;
|
||||
case (funct7)
|
||||
opALU1: begin
|
||||
dInst.execFunc = tagged Alu (case(funct3)
|
||||
fnADD: Add;
|
||||
fnSLT: Slt;
|
||||
fnSLTU: Sltu;
|
||||
fnAND: And;
|
||||
fnOR: Or;
|
||||
fnXOR: Xor;
|
||||
fnSLL: Sll;
|
||||
fnSR: Srl;
|
||||
endcase);
|
||||
end
|
||||
opALU2: begin
|
||||
dInst.execFunc = tagged Alu (case (funct3)
|
||||
fnADD: Sub;
|
||||
fnSR: Sra;
|
||||
endcase);
|
||||
end
|
||||
opMULDIV: begin
|
||||
if (isa.m) begin
|
||||
// Processor includes "M" extension
|
||||
MulDivFunc func = (case(funct3)
|
||||
fnMUL : Mul;
|
||||
fnMULH : Mulh;
|
||||
fnMULHSU : Mulh;
|
||||
fnMULHU : Mulh;
|
||||
fnDIV : Div;
|
||||
fnDIVU : Div;
|
||||
fnREM : Rem;
|
||||
fnREMU : Rem;
|
||||
endcase);
|
||||
Bool w = False;
|
||||
MulDivSign sign = (case(funct3)
|
||||
fnMUL : Signed;
|
||||
fnMULH : Signed;
|
||||
fnMULHSU : SignedUnsigned;
|
||||
fnMULHU : Unsigned;
|
||||
fnDIV : Signed;
|
||||
fnDIVU : Unsigned;
|
||||
fnREM : Signed;
|
||||
fnREMU : Unsigned;
|
||||
endcase);
|
||||
dInst.execFunc = tagged MulDiv (MulDivInst {
|
||||
func: func, w: w, sign: sign
|
||||
});
|
||||
end else begin
|
||||
// Processor doesn't include "M" extension
|
||||
illegalInst = True;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
end
|
||||
|
||||
Op32: begin
|
||||
dInst.iType = Alu;
|
||||
case (funct7)
|
||||
opALU1: begin
|
||||
dInst.execFunc = tagged Alu (case(funct3)
|
||||
fnADD: Addw;
|
||||
fnSLL: Sllw;
|
||||
fnSR: Srlw;
|
||||
endcase);
|
||||
end
|
||||
opALU2: begin
|
||||
dInst.execFunc = tagged Alu (case (funct3)
|
||||
fnADD: Subw;
|
||||
fnSR: Sraw;
|
||||
endcase);
|
||||
end
|
||||
opMULDIV: begin
|
||||
if (isa.m) begin
|
||||
// Processor includes "M" extension
|
||||
// TODO mark MULH as illegal inst
|
||||
MulDivFunc func = (case(funct3)
|
||||
fnMUL : Mul;
|
||||
fnMULH : Mulh; // illegal
|
||||
fnMULHSU : Mulh; // illegal
|
||||
fnMULHU : Mulh; // illegal
|
||||
fnDIV : Div;
|
||||
fnDIVU : Div;
|
||||
fnREM : Rem;
|
||||
fnREMU : Rem;
|
||||
endcase);
|
||||
Bool w = True;
|
||||
MulDivSign sign = (case(funct3)
|
||||
fnMUL : Signed;
|
||||
fnMULH : Signed; // illegal
|
||||
fnMULHSU : SignedUnsigned; // illegal
|
||||
fnMULHU : Unsigned; // illegal
|
||||
fnDIV : Signed;
|
||||
fnDIVU : Unsigned;
|
||||
fnREM : Signed;
|
||||
fnREMU : Unsigned;
|
||||
endcase);
|
||||
dInst.execFunc = tagged MulDiv (MulDivInst{
|
||||
func: func, w: w, sign: sign
|
||||
});
|
||||
end else begin
|
||||
// Processor doesn't include "M" extension
|
||||
illegalInst = True;
|
||||
end
|
||||
end
|
||||
endcase
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Valid(tagged Gpr rs2);
|
||||
dInst.imm = Invalid;
|
||||
dInst.csr = tagged Invalid;
|
||||
end
|
||||
|
||||
Lui: begin // treated as an x0 + immU
|
||||
dInst.iType = Alu;
|
||||
dInst.execFunc = tagged Alu Add;
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Gpr 0);
|
||||
regs.src2 = Invalid;
|
||||
dInst.imm = Valid(immU);
|
||||
dInst.csr = tagged Invalid;
|
||||
end
|
||||
|
||||
Auipc: begin
|
||||
dInst.iType = Auipc;
|
||||
dInst.execFunc = tagged Alu Add;
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Invalid;
|
||||
regs.src2 = Invalid;
|
||||
dInst.imm = Valid(immU);
|
||||
dInst.csr = tagged Invalid;
|
||||
end
|
||||
|
||||
Jal: begin
|
||||
dInst.iType = J;
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Invalid;
|
||||
regs.src2 = Invalid;
|
||||
dInst.imm = Valid(immJ);
|
||||
dInst.csr = tagged Invalid;
|
||||
dInst.execFunc = tagged Br AT;
|
||||
end
|
||||
|
||||
Jalr: begin
|
||||
dInst.iType = Jr;
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Invalid;
|
||||
dInst.imm = Valid(immI);
|
||||
dInst.csr = tagged Invalid;
|
||||
dInst.execFunc = tagged Br AT;
|
||||
end
|
||||
|
||||
Branch: begin
|
||||
dInst.iType = Br;
|
||||
dInst.execFunc = tagged Br (case(funct3)
|
||||
fnBEQ: Eq;
|
||||
fnBNE: Neq;
|
||||
fnBLT: Lt;
|
||||
fnBLTU: Ltu;
|
||||
fnBGE: Ge;
|
||||
fnBGEU: Geu;
|
||||
endcase);
|
||||
regs.dst = Invalid;
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Valid(tagged Gpr rs2);
|
||||
dInst.imm = Valid(immB);
|
||||
dInst.csr = tagged Invalid;
|
||||
end
|
||||
|
||||
Load: begin
|
||||
dInst.iType = Ld;
|
||||
if (isValid(mem_inst)) begin
|
||||
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
|
||||
end else begin
|
||||
illegalInst = True;
|
||||
end
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Invalid;
|
||||
dInst.imm = Valid(immI);
|
||||
dInst.csr = tagged Invalid;
|
||||
end
|
||||
|
||||
Store: begin
|
||||
dInst.iType = St;
|
||||
if (isValid(mem_inst)) begin
|
||||
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
|
||||
end else begin
|
||||
illegalInst = True;
|
||||
end
|
||||
regs.dst = Invalid;
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Valid(tagged Gpr rs2);
|
||||
dInst.imm = Valid(immS);
|
||||
dInst.csr = tagged Invalid;
|
||||
end
|
||||
|
||||
Amo: begin
|
||||
if (!isa.a) begin
|
||||
// unsupported
|
||||
illegalInst = True;
|
||||
end else begin
|
||||
// AMO defaults
|
||||
dInst.iType = Amo;
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Valid(tagged Gpr rs2);
|
||||
dInst.imm = Valid(0);
|
||||
dInst.csr = Invalid;
|
||||
|
||||
case (funct5)
|
||||
fnLR: begin
|
||||
dInst.iType = Lr;
|
||||
if (isValid(mem_inst)) begin
|
||||
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
|
||||
end else begin
|
||||
illegalInst = True;
|
||||
end
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Invalid;
|
||||
dInst.imm = Valid(0);
|
||||
dInst.csr = Invalid;
|
||||
end
|
||||
|
||||
fnSC: begin
|
||||
dInst.iType = Sc;
|
||||
if (isValid(mem_inst)) begin
|
||||
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
|
||||
end else begin
|
||||
illegalInst = True;
|
||||
end
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Valid(tagged Gpr rs2);
|
||||
dInst.imm = Valid(0);
|
||||
dInst.csr = Invalid;
|
||||
end
|
||||
|
||||
fnAMOSWAP,
|
||||
fnAMOADD,
|
||||
fnAMOXOR,
|
||||
fnAMOAND,
|
||||
fnAMOOR,
|
||||
fnAMOMIN,
|
||||
fnAMOMAX,
|
||||
fnAMOMINU,
|
||||
fnAMOMAXU: begin
|
||||
if (isValid(mem_inst)) begin
|
||||
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
|
||||
end else begin
|
||||
illegalInst = True;
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
illegalInst = True;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
|
||||
// Instructions for "F" and "D" ISA extensions - FPU
|
||||
OpFp: begin
|
||||
// check if instruction is supported
|
||||
if ((fmt == fmtS && !isa.f) || (fmt == fmtD && !isa.d) || (fmt != fmtS && fmt != fmtD)) begin
|
||||
illegalInst = True;
|
||||
end else begin
|
||||
// Instruction is supported
|
||||
dInst.iType = Fpu;
|
||||
regs.dst = Valid(tagged Fpu rd);
|
||||
regs.src1 = Valid(tagged Fpu rs1);
|
||||
regs.src2 = Valid(tagged Fpu rs2);
|
||||
dInst.imm = Invalid;
|
||||
dInst.csr = tagged Invalid;
|
||||
FpuFunc func = (case (funct5)
|
||||
opFADD: FAdd;
|
||||
opFSUB: FSub;
|
||||
opFMUL: FMul;
|
||||
opFDIV: FDiv;
|
||||
opFSQRT: FSqrt;
|
||||
opFSGNJ: ((funct3 == 0) ? FSgnj : ((funct3 == 1) ? FSgnjn : FSgnjx));
|
||||
opFMINMAX: ((funct3 == 0) ? FMin : FMax);
|
||||
opFCMP: ((funct3 == 0) ? FLe : ((funct3 == 1) ? FLt : FEq));
|
||||
opFMV_XF: ((funct3 == 0) ? FMv_XF : FClass); // also CLASS
|
||||
opFMV_FX: FMv_FX;
|
||||
opFCVT_FF: FCvt_FF;
|
||||
opFCVT_WF: ((rs2 == 0) ? FCvt_WF : ((rs2 == 1) ? FCvt_WUF : ((rs2 == 2) ? FCvt_LF : FCvt_LUF)));
|
||||
opFCVT_FW: ((rs2 == 0) ? FCvt_FW : ((rs2 == 1) ? FCvt_FWU : ((rs2 == 2) ? FCvt_FL : FCvt_FLU)));
|
||||
endcase);
|
||||
FpuPrecision precision = (fmt == fmtS) ? Single : Double;
|
||||
dInst.execFunc = tagged Fpu(FpuInst{func: func, rm: unpack(funct3), precision: precision});
|
||||
// Special cases
|
||||
case (funct5)
|
||||
opFSQRT: begin
|
||||
regs.dst = Valid(tagged Fpu rd);
|
||||
regs.src1 = Valid(tagged Fpu rs1);
|
||||
regs.src2 = Invalid;
|
||||
end
|
||||
opFCMP: begin
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Fpu rs1);
|
||||
regs.src2 = Valid(tagged Fpu rs2);
|
||||
end
|
||||
opFMV_XF: begin
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Fpu rs1);
|
||||
regs.src2 = Invalid;
|
||||
end
|
||||
opFMV_FX: begin
|
||||
regs.dst = Valid(tagged Fpu rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Invalid;
|
||||
end
|
||||
opFCVT_FF: begin
|
||||
regs.dst = Valid(tagged Fpu rd);
|
||||
regs.src1 = Valid(tagged Fpu rs1);
|
||||
regs.src2 = Invalid;
|
||||
end
|
||||
opFCVT_WF: begin
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Valid(tagged Fpu rs1);
|
||||
regs.src2 = Invalid;
|
||||
end
|
||||
opFCVT_FW: begin
|
||||
regs.dst = Valid(tagged Fpu rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Invalid;
|
||||
end
|
||||
endcase
|
||||
end
|
||||
end
|
||||
LoadFp: begin
|
||||
// check if instruction is supported
|
||||
if (!isa.f && !isa.d) begin
|
||||
// FIXME: Check more cases
|
||||
illegalInst = True;
|
||||
end else begin
|
||||
// Same decode logic as Int Ld
|
||||
dInst.iType = Ld;
|
||||
if (isValid(mem_inst)) begin
|
||||
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
|
||||
end else begin
|
||||
illegalInst = True;
|
||||
end
|
||||
regs.dst = Valid(tagged Fpu rd);
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Invalid;
|
||||
dInst.imm = Valid(immI);
|
||||
dInst.csr = tagged Invalid;
|
||||
end
|
||||
end
|
||||
StoreFp: begin
|
||||
// check if instruction is supported
|
||||
if (!isa.f && !isa.d) begin
|
||||
// FIXME: Check more cases
|
||||
illegalInst = True;
|
||||
end else begin
|
||||
// Same decode logic as Int St
|
||||
dInst.iType = St;
|
||||
if (isValid(mem_inst)) begin
|
||||
dInst.execFunc = tagged Mem fromMaybe(?, mem_inst);
|
||||
end else begin
|
||||
illegalInst = True;
|
||||
end
|
||||
regs.dst = Invalid;
|
||||
regs.src1 = Valid(tagged Gpr rs1);
|
||||
regs.src2 = Valid(tagged Fpu rs2);
|
||||
dInst.imm = Valid(immS);
|
||||
dInst.csr = tagged Invalid;
|
||||
end
|
||||
end
|
||||
Fmadd, Fmsub, Fnmsub, Fnmadd: begin
|
||||
// check if instruction is supported
|
||||
if ((fmt == fmtS && !isa.f) ||
|
||||
(fmt == fmtD && !isa.d) ||
|
||||
(fmt != fmtS && fmt != fmtD)) begin
|
||||
dInst.iType = Unsupported;
|
||||
illegalInst = True;
|
||||
end else begin
|
||||
// Instruction is supported
|
||||
dInst.iType = Fpu;
|
||||
FpuFunc func = ?;
|
||||
case (opcode)
|
||||
Fmadd: func = FMAdd;
|
||||
Fmsub: func = FMSub;
|
||||
Fnmsub: func = FNMSub;
|
||||
Fnmadd: func = FNMAdd;
|
||||
default: illegalInst = True;
|
||||
endcase
|
||||
dInst.execFunc = tagged Fpu (FpuInst {
|
||||
func: func,
|
||||
rm: unpack(funct3),
|
||||
precision: fmt == fmtS ? Single : Double
|
||||
});
|
||||
regs.src1 = Valid(tagged Fpu rs1);
|
||||
regs.src2 = Valid(tagged Fpu rs2);
|
||||
regs.src3 = Valid(rs3);
|
||||
regs.dst = Valid(tagged Fpu rd);
|
||||
dInst.csr = Invalid;
|
||||
dInst.imm = Invalid;
|
||||
end
|
||||
end
|
||||
|
||||
MiscMem: begin
|
||||
case (funct3)
|
||||
fnFENCEI: begin
|
||||
dInst.iType = FenceI;
|
||||
dInst.execFunc = tagged Other;
|
||||
end
|
||||
fnFENCE: begin
|
||||
// extract bits for P/S IORW
|
||||
Bool old_st = unpack(inst[26] | inst[24]); // PO, PW
|
||||
Bool young_ld = unpack(inst[23] | inst[21]); // SI, SR
|
||||
// get acq/reconcile and rel/commit needed to enforce
|
||||
// different orderings
|
||||
`ifdef TSO_MM
|
||||
// Orderings enfored by fence in TSO:
|
||||
// St -> Ld: commit
|
||||
// Others: N/A
|
||||
Bool reconcile = False;
|
||||
Bool commit = old_st && young_ld;
|
||||
`else
|
||||
// Orderings enforced by fence in WMM
|
||||
// St -> St: commit
|
||||
// St -> Ld: commit + reconcile
|
||||
// Ld -> Ld: reconcile
|
||||
// Ld -> St: N/A
|
||||
Bool reconcile = young_ld; // reconcile when younger load is in ordering
|
||||
Bool commit = old_st; // commit when older store is in ordering
|
||||
`endif
|
||||
// set up fence inst
|
||||
if (reconcile || commit) begin
|
||||
dInst.iType = Fence;
|
||||
dInst.execFunc = tagged Mem (MemInst {
|
||||
mem_func: Fence,
|
||||
amo_func: None,
|
||||
unsignedLd: False,
|
||||
byteEn: replicate(False),
|
||||
aq: reconcile,
|
||||
rl: commit
|
||||
});
|
||||
end
|
||||
else begin
|
||||
dInst.iType = Nop;
|
||||
dInst.execFunc = tagged Other;
|
||||
end
|
||||
end
|
||||
default: illegalInst = True;
|
||||
endcase
|
||||
regs.dst = Invalid;
|
||||
regs.src1 = Invalid;
|
||||
regs.src2 = Invalid;
|
||||
dInst.imm = Invalid;
|
||||
dInst.csr = Invalid;
|
||||
end
|
||||
|
||||
System: begin
|
||||
if (funct3 == fnPRIV) begin
|
||||
if (funct7 == privSFENCEVMA) begin
|
||||
dInst.iType = SFence;
|
||||
// FIXME SFENCE.VMA is implemented in coarse grain,
|
||||
// ignoring rs1 and rs2
|
||||
end
|
||||
else begin
|
||||
case (truncate(immI))
|
||||
privSRET: dInst.iType = Sret;
|
||||
privMRET: dInst.iType = Mret;
|
||||
privECALL: dInst.iType = Ecall;
|
||||
privEBREAK: dInst.iType = Ebreak;
|
||||
privWFI: dInst.iType = Nop; // treat as NOP
|
||||
default: illegalInst = True;
|
||||
endcase
|
||||
end
|
||||
regs.dst = Invalid;
|
||||
regs.src1 = Invalid;
|
||||
regs.src2 = Invalid;
|
||||
dInst.csr = Invalid;
|
||||
dInst.imm = Invalid;
|
||||
end
|
||||
else begin // fnCSRRWI, fnCSRRW, fnCSRRSI, fnCSRRS, fnCSRRCI, fnCSRRC
|
||||
dInst.iType = Csr;
|
||||
dInst.execFunc = (case (funct3)
|
||||
fnCSRRWI, fnCSRRW: tagged Alu Csrw;
|
||||
fnCSRRSI, fnCSRRS: tagged Alu Csrs;
|
||||
fnCSRRCI, fnCSRRC: tagged Alu Csrc;
|
||||
endcase);
|
||||
|
||||
regs.dst = Valid(tagged Gpr rd);
|
||||
regs.src1 = Invalid; // going to be CSR Reg
|
||||
regs.src2 = (funct3[2] == 0 ? Valid(tagged Gpr rs1) : Invalid);
|
||||
dInst.imm = (funct3[2] == 0 ? Invalid : Valid(zeroExtend(rs1)));
|
||||
dInst.csr = Valid(unpackCSR(truncate(immI)));
|
||||
end
|
||||
end
|
||||
|
||||
default: begin
|
||||
illegalInst = True;
|
||||
end
|
||||
endcase
|
||||
|
||||
// XXX to ensure renaming + phy regs works correctly, must remove any write
|
||||
// to x0
|
||||
if(regs.dst matches tagged Valid .dst &&& dst == tagged Gpr 0) begin
|
||||
regs.dst = tagged Invalid;
|
||||
end
|
||||
|
||||
return DecodeResult{dInst: dInst, regs: regs, illegalInst: illegalInst};
|
||||
endfunction
|
||||
|
||||
// All this does is add the CSR state to the decoding
|
||||
function FpuInst updateRoundingMode(FpuInst fpu_f, CsrDecodeInfo csrState);
|
||||
let new_fpu_f = fpu_f;
|
||||
new_fpu_f.rm = (fpu_f.rm == RDyn) ? unpack(csrState.frm) : fpu_f.rm;
|
||||
return new_fpu_f;
|
||||
endfunction
|
||||
85
src_Core/RISCY_OOO/procs/lib/DirPredictor.bsv
Normal file
85
src_Core/RISCY_OOO/procs/lib/DirPredictor.bsv
Normal file
@@ -0,0 +1,85 @@
|
||||
|
||||
// Copyright (c) 2017 Massachusetts Institute of Technology
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person
|
||||
// obtaining a copy of this software and associated documentation
|
||||
// files (the "Software"), to deal in the Software without
|
||||
// restriction, including without limitation the rights to use, copy,
|
||||
// modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
// of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be
|
||||
// included in all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
|
||||
// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
`include "ProcConfig.bsv"
|
||||
|
||||
import Assert::*;
|
||||
import Types::*;
|
||||
import ProcTypes::*;
|
||||
import Vector::*;
|
||||
import BrPred::*;
|
||||
import Bht::*;
|
||||
import GSelectPred::*;
|
||||
import GSharePred::*;
|
||||
import TourPred::*;
|
||||
import TourPredSecure::*;
|
||||
|
||||
export DirPredTrainInfo(..);
|
||||
export mkDirPredictor;
|
||||
|
||||
`ifdef DIR_PRED_BHT
|
||||
typedef BhtTrainInfo DirPredTrainInfo;
|
||||
`endif
|
||||
`ifdef DIR_PRED_GSELECT
|
||||
typedef GSelectTrainInfo DirPredTrainInfo;
|
||||
`endif
|
||||
`ifdef DIR_PRED_GSHARE
|
||||
typedef GShareTrainInfo DirPredTrainInfo;
|
||||
`endif
|
||||
`ifdef DIR_PRED_TOUR
|
||||
typedef TourTrainInfo DirPredTrainInfo;
|
||||
`endif
|
||||
|
||||
(* synthesize *)
|
||||
module mkDirPredictor(DirPredictor#(DirPredTrainInfo));
|
||||
`ifdef DIR_PRED_BHT
|
||||
`ifdef SECURITY
|
||||
staticAssert(False, "BHT with flush methods is not implemented");
|
||||
`endif
|
||||
let m <- mkBht;
|
||||
`endif
|
||||
|
||||
`ifdef DIR_PRED_GSELECT
|
||||
`ifdef SECURITY
|
||||
staticAssert(False, "GSelect with flush methods is not implemented");
|
||||
`endif
|
||||
let m <- mkGSelectPred;
|
||||
`endif
|
||||
|
||||
`ifdef DIR_PRED_GSHARE
|
||||
`ifdef SECURITY
|
||||
staticAssert(False, "GShare with flush methods is not implemented");
|
||||
`endif
|
||||
let m <- mkGSharePred;
|
||||
`endif
|
||||
|
||||
`ifdef DIR_PRED_TOUR
|
||||
`ifdef SECURITY
|
||||
let m <- mkTourPredSecure;
|
||||
`else
|
||||
let m <- mkTourPred;
|
||||
`endif
|
||||
`endif
|
||||
|
||||
return m;
|
||||
endmodule
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user