Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add performance monitor module #2

Merged
merged 9 commits into from
Sep 21, 2020
19 changes: 19 additions & 0 deletions AXI/AXI4_Types.bsv
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ import MasterSlave :: *;

import AXI4_AXI4Lite_Types :: *;

`ifdef PERFORMANCE_MONITORING
import PerformanceMonitor :: *;
`endif

//////////////////
// helper types //
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -879,6 +883,21 @@ interface AXI4_Shim_Synth#(
) slave;
endinterface

`ifdef PERFORMANCE_MONITORING
typedef struct {
Bool evt_AR_FLIT;
Bool evt_AW_FLIT;
Bool evt_W_FLIT;
Bool evt_R_FLIT;
Bool evt_R_FLIT_FINAL;
Bool evt_B_FLIT;
} EventsAXI4 deriving (Bits, FShow);

instance BitVectorable#(EventsAXI4, 1, n) provisos (Bits#(EventsAXI4, n));
function to_vector = struct_to_vector;
endinstance
`endif

////////////////////////////////
// AXI4 Connectable instances //
////////////////////////////////////////////////////////////////////////////////
Expand Down
69 changes: 69 additions & 0 deletions AXI/AXI4_Utils.bsv
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ import AXI4_R_Utils :: *;

// BlueStuff import
import Routable :: *;
`ifdef PERFORMANCE_MONITORING
import MonitorWrapper :: *;
`endif
// BlueBasics import
import SourceSink :: *;
import MasterSlave :: *;
Expand Down Expand Up @@ -610,6 +613,72 @@ module mkAXI4DebugShim #(String debugTag) (AXI4_Shim#(a,b,c,d,e,f,g,h));
interface clear = shim.clear;
endmodule

//////////////////////////////
// AXI4 Performance monitor utils //
////////////////////////////////////////////////////////////////////////////////

`ifdef PERFORMANCE_MONITORING
module monitorAXI4_Shim #(AXI4_Shim#(a, b, c, d, e, f, g, h) shim)
(Monitored#(AXI4_Shim#(a, b, c, d, e, f, g, h), Tuple2#(EventsAXI4, EventsAXI4)));
let masterMonitor <- monitorAXI4_Master(shim.master);
let slaveMonitor <- monitorAXI4_Slave(shim.slave);
interface ifc = interface AXI4_Shim;
interface clear = shim.clear;
interface master = masterMonitor.ifc;
interface slave = slaveMonitor.ifc;
endinterface;
method events = tuple2(masterMonitor.events, slaveMonitor.events);
endmodule

module monitorAXI4_Master #(AXI4_Master#(a, b, c, d, e, f, g, h) master)
(Monitored#(AXI4_Master#(a, b, c, d, e, f, g, h), EventsAXI4));
let awMonitor <- monitorSource(master.aw);
let wMonitor <- monitorSource(master.w);
let bMonitor <- monitorSink(master.b);
let arMonitor <- monitorSource(master.ar);
let rMonitor <- monitorLastSink(master.r);
interface ifc = interface AXI4_Master;
interface aw = awMonitor.ifc;
interface w = wMonitor.ifc;
interface b = bMonitor.ifc;
interface ar = arMonitor.ifc;
interface r = rMonitor.ifc;
endinterface;
method events = EventsAXI4 {
evt_AR_FLIT : unpack(arMonitor.events),
evt_AW_FLIT : unpack(awMonitor.events),
evt_W_FLIT : unpack(wMonitor.events),
evt_R_FLIT : unpack(rMonitor.events[0]),
evt_R_FLIT_FINAL : unpack(rMonitor.events[1]),
evt_B_FLIT : unpack(bMonitor.events)
};
endmodule

module monitorAXI4_Slave #(AXI4_Slave#(a, b, c, d, e, f, g, h) slave)
(Monitored#(AXI4_Slave#(a, b, c, d, e, f, g, h), EventsAXI4));
let awMonitor <- monitorSink(slave.aw);
let wMonitor <- monitorSink(slave.w);
let bMonitor <- monitorSource(slave.b);
let arMonitor <- monitorSink(slave.ar);
let rMonitor <- monitorLastSource(slave.r);
interface ifc = interface AXI4_Slave;
interface aw = awMonitor.ifc;
interface w = wMonitor.ifc;
interface b = bMonitor.ifc;
interface ar = arMonitor.ifc;
interface r = rMonitor.ifc;
endinterface;
method events = EventsAXI4 {
evt_AR_FLIT : unpack(arMonitor.events),
evt_AW_FLIT : unpack(awMonitor.events),
evt_W_FLIT : unpack(wMonitor.events),
evt_R_FLIT : unpack(rMonitor.events[0]),
evt_R_FLIT_FINAL : unpack(rMonitor.events[1]),
evt_B_FLIT : unpack(bMonitor.events)
};
endmodule
`endif

/////////////////////////////////////
// to/from "Synth" interface utils //
////////////////////////////////////////////////////////////////////////////////
Expand Down
98 changes: 98 additions & 0 deletions MonitorWrapper.bsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*-
* Copyright (c) 2020 Jonas Fiala
* All rights reserved.
*
* This software was developed by SRI International and the University of
* Cambridge Computer Laboratory (Department of Computer Science and
* Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
* DARPA SSITH research programme.
*
* @BERI_LICENSE_HEADER_START@
*
* Licensed to BERI Open Systems C.I.C. (BERI) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. BERI licenses this
* file to you under the BERI Hardware-Software License, Version 1.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.beri-open-systems.org/legal/license-1-0.txt
*
* Unless required by applicable law or agreed to in writing, Work distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* @BERI_LICENSE_HEADER_END@
*/

package MonitorWrapper;

import Vector :: *;

import SourceSink :: *;

import Routable :: *;
import PerformanceMonitor :: *;


// Wrapper interface for reporting events of a given interface
interface Monitored#(type original_module, type bitvec);
interface original_module ifc;
method bitvec events provisos (BitVectorable#(bitvec, n, m));
endinterface


// Count drop events
module monitorSource #(Source#(t) s) (Monitored#(Source#(t), Bit#(1)));
Wire#(Bit#(1)) evt <- mkDWire(0);
interface ifc = interface Source;
method canPeek = s.canPeek;
method peek = s.peek;
method drop = action
s.drop;
evt <= 1;
endaction;
endinterface;
method events = evt;
endmodule
// Count put events
module monitorSink #(Sink#(t) s) (Monitored#(Sink#(t), Bit#(1)));
Wire#(Bit#(1)) evt <- mkDWire(0);
interface ifc = interface Sink;
method canPut = s.canPut;
method put (x) = action
s.put(x);
evt <= 1;
endaction;
endinterface;
method events = evt;
endmodule
// Count drop (at events[0]) and drop last (at events[1])
module monitorLastSource #(Source#(t) s) (Monitored#(Source#(t), Bit#(2)))
provisos (DetectLast#(t));
Wire#(Bit#(2)) evt <- mkDWire(0);
interface ifc = interface Source;
method canPeek = s.canPeek;
method peek = s.peek;
method drop = action
s.drop;
evt <= {pack(detectLast(s.peek)), 1};
endaction;
endinterface;
method events = evt;
endmodule
// Count put (at events[0]) and put last (at events[1])
module monitorLastSink #(Sink#(t) s) (Monitored#(Sink#(t), Bit#(2)))
provisos (DetectLast#(t));
Wire#(Bit#(2)) evt <- mkDWire(0);
interface ifc = interface Sink;
method canPut = s.canPut;
method put (x) = action
s.put(x);
evt <= {pack(detectLast(x)), 1};
endaction;
endinterface;
method events = evt;
endmodule
endpackage
155 changes: 155 additions & 0 deletions PerformanceMonitor.bsv
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*-
* Copyright (c) 2020 Jonas Fiala
* All rights reserved.
*
* This software was developed by SRI International and the University of
* Cambridge Computer Laboratory (Department of Computer Science and
* Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
* DARPA SSITH research programme.
*
* @BERI_LICENSE_HEADER_START@
*
* Licensed to BERI Open Systems C.I.C. (BERI) under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. BERI licenses this
* file to you under the BERI Hardware-Software License, Version 1.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
*
* http://www.beri-open-systems.org/legal/license-1-0.txt
*
* Unless required by applicable law or agreed to in writing, Work distributed
* under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* @BERI_LICENSE_HEADER_END@
*/

package PerformanceMonitor;

import Vector :: *;

// A module which wants to count events must return some vector of events
// However it is easier to count the events as a Struct within a module
// So if the Struct is an instance of this class it can be returned as is
typeclass BitVectorable#(type from, numeric type n, numeric type m) dependencies (from determines (n, m));
function Vector#(m, Bit#(n)) to_vector (from e);
endtypeclass

instance BitVectorable#(Vector#(m, Bit#(n)), n, m);
function to_vector = id;
endinstance

// A Bit#(n) will be converted to a 1 element vector
instance BitVectorable#(Bit#(n), n, 1);
function to_vector = replicate;
endinstance


function Vector#(mm, Bit#(nn)) to_large_vector (from e)
provisos (BitVectorable#(from, n, m), Add#(a__, n, nn), Add#(b__, m, mm));
return append(map(zeroExtend, to_vector(e)), replicate(0));
endfunction

// Use to_vector = struct_to_vector
// with BitVectorable instances for structs of events
// The struct must only contain Bools (or Bit#(1)) for this fn to be reasonable
function Vector#(m, Bit#(1)) struct_to_vector (from e) provisos (Bits#(from, m));
return reverse(unpack(pack(e)));
endfunction


// Write is exposed to only one counter per cycle
// Could change write_* methods to return WriteOnly Vector if needed
interface PerfCounters_IFC#(numeric type ctrs, numeric type ctrW, numeric type evts);
(* always_ready, always_enabled *)
method Action send_performance_events (Vector#(evts, Bit#(ctrW)) evts);

(* always_ready *)
method Vector#(ctrs, ReadOnly#(Bit#(ctrW))) read_counters;
(* always_ready *)
method Vector#(ctrs, ReadOnly#(Bit#(TLog#(evts)))) read_ctr_sels;
(* always_ready *)
method ReadOnly#(Bit#(ctrs)) read_ctr_inhibit;
(* always_ready *)
method ReadOnly#(Bit#(ctrs)) read_ctr_overflow;

(* always_ready *)
method Action write_counter (Bit#(TLog#(ctrs)) idx, Bit#(ctrW) val);
(* always_ready *)
method Action write_ctr_sel (Bit#(TLog#(ctrs)) idx, Bit#(TLog#(evts)) val);
(* always_ready *)
method Action write_ctr_inhibit (Bit#(ctrs) val);
endinterface


module mkPerfCounters (PerfCounters_IFC#(ctrs, ctrW, evts));
let ctrs = valueOf(ctrs);
let ctrW = valueOf(ctrW);
let evts = valueOf(evts);

// Event counters
Vector#(ctrs, Array#(Reg#(Bit#(ctrW))))
vec_rg_counter <- replicateM(mkCRegU(2));
// Which event the corresponding counter will count
Vector#(ctrs, Reg#(Bit#(TLog#(evts))))
vec_rg_event_sel <- replicateM(mkRegU);

Reg#(Bit#(ctrs)) rg_ctr_inhibit <- mkReg(0);
Wire#(Bit#(ctrs)) wr_overflow <- mkDWire(0);

// Since [i] is not a function, cannot map it to a Vector
function a_type sel_snd_port (Array#(a_type) arr) = arr[1];

// Should be called every cycle with vector of how many times each event
// occured in the given cycle. The maximum is 2^ctrW, but an event 'should'
// never happen so many times in a cycle:
// So map zeroExtend to the vector before passing it to this method
method Action send_performance_events (Vector#(evts, Bit#(ctrW)) events);
Bit#(ctrs) overflow = 0;
for (Integer i = 0; i < ctrs; i = i + 1) begin
// Get the number of times the selected event fired this cycle
Bit#(ctrW) event_count = 0;
if (vec_rg_event_sel[i] <= fromInteger(evts - 1))
event_count = events[vec_rg_event_sel[i]];

// Check that the given counter is not inhibited
Bool inhibit = unpack(rg_ctr_inhibit[i]);
// Count event
if (!inhibit) begin
Bit#(TAdd#(ctrW, 1)) count_sum = zeroExtend(vec_rg_counter[i][0])
+ zeroExtend(event_count);
vec_rg_counter[i][0] <= truncate(count_sum);
overflow[i] = truncateLSB(count_sum);
end

// $display("<time %0t, StatCounters> Counter #%2d Counting event %0d: Adding %0d to %0d (Inhibit %0d)",
// $time, i, vec_rg_event_sel[i], event_count, vec_rg_counter[i][0], inhibit);
end
wr_overflow <= overflow;
// $display("-------------------------CLK-------------------------");
endmethod

method Vector#(ctrs, ReadOnly#(Bit#(ctrW))) read_counters =
map(compose(regToReadOnly, sel_snd_port), vec_rg_counter);

method Vector#(ctrs, ReadOnly#(Bit#(TLog#(evts)))) read_ctr_sels =
map(regToReadOnly, vec_rg_event_sel);

method ReadOnly#(Bit#(ctrs)) read_ctr_inhibit =
regToReadOnly(rg_ctr_inhibit);

method ReadOnly#(Bit#(ctrs)) read_ctr_overflow =
regToReadOnly(wr_overflow);


method write_counter (Bit#(TLog#(ctrs)) idx) =
vec_rg_counter[idx][1]._write;

method write_ctr_sel (Bit#(TLog#(ctrs)) idx) =
vec_rg_event_sel[idx]._write;

method write_ctr_inhibit = rg_ctr_inhibit._write;
endmodule
endpackage