Bluespec Lexer: RiscyOO Scoreboard
This page tests my Bluespec lexer on a sample of advanced Bluespec in the open-source RiscyOO/Toooba project. This corresponds to their OOO scoreboard.
There are a few outstanding limitations, which I track on my known issues list.
One limitation of my lexer on this sample is its inability to make the same deductions for implied return types that the Bluespec compiler is able to make, or to make good on =
definitions for explicit return methods. For example:
interface Scoreboard#(numeric type size, type t);
method Bool search1; // search1 is indicated to be a pure function
method Bool some_return; // both return Bool
endinterface
module mkCFScoreboard(Scoreboard#(size,t)) provisos (Bits#(t,tSz), Eq#(t));
method search1 = f.search; // no memory that `search1` is a pure function
// ^ implicit return that the compiler can accept
method Bool some_return = constant_value; // but we also don't track non-action
endmodule
Adding support for implicit typing would require my lexer to employ much more state than it currently does, on a degree closer to semantic highlighting. Adding support for explicit typing and =
method assignment is possible and is on my list of issues to fix.
Another limitation is that the lexer is unable to perform action highlighting when defining a module inside of a return statement. This issue should only appear in complex Bluespec that creates wrappers, and should be unlikely to confuse any beginners.
function ExampleInterface exampleInterfaceWrapper(ExampleParentInterface epi);
return (interface ExampleInterface;
method Action clear;
epi.clear; // this should be annotated as an Action
endmethod
endinterface);
endfunction
But otherwise, I think it does an outstanding job on the rest of the code.
// 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 Fifos::*;
import ProcTypes::*;
import CsrFile::*; // for mkReadOnlyReg
import Ehr::*;
import GetPut::*;
interface Scoreboard#(numeric type size, type t);
method Action insert(Maybe#(t) r);
method Action remove;
method Bool search1(Maybe#(t) r);
method Bool search2(Maybe#(t) r);
method Bool search3(Maybe#(t) r);
method Action clear;
endinterface
interface PhyRegsScoreboard;
method Action insert(Maybe#(PhyDst) r);
method Action remove(Maybe#(PhyDst) r);
method Bool search(PhyRegs r);
method Action clear;
endinterface
function Bool isFound(Maybe#(t) x, Maybe#(t) k) provisos (Eq#(t));
if(x matches tagged Valid .xv &&& k matches tagged Valid .kv &&& kv == xv)
return True;
else
return False;
endfunction
// search CF {enq, deq, first, notEmpty, notFull}
// deq CF enq
// search < clear
module mkCFScoreboard(Scoreboard#(size,t)) provisos (Bits#(t,tSz), Eq#(t));
SFifo#(size, Maybe#(t), Maybe#(t)) f <- mkCFSFifo(isFound);
method insert = f.enq;
method remove = f.deq;
method search1 = f.search;
method search2 = f.search;
method search3 = f.search;
method clear = f.clear;
endmodule
function PhyRegsScoreboard phyRegsScoreboardWrapper(Scoreboard#(size, PhyRIndx) sb);
return (interface PhyRegsScoreboard;
method Action insert(Maybe#(PhyDst) r);
if (r matches tagged Valid .valid_dst) begin
sb.insert(tagged Valid (valid_dst.indx));
end else begin
sb.insert(tagged Invalid);
end
endmethod
method Action remove(Maybe#(PhyDst) r);
sb.remove;
endmethod
method Bool search(PhyRegs r);
return sb.search1(r.src1) || sb.search2(r.src2) || sb.search3(r.src3);
endmethod
method Action clear;
sb.clear;
endmethod
endinterface);
endfunction
module mkBitVectorScoreboard(PhyRegsScoreboard);
Vector#(NumPhyReg, Reg#(Bool)) bitvector <- replicateM(mkReg(False));
bitvector[0] <- mkReadOnlyReg(False);
method Action insert(Maybe#(PhyDst) r);
if (r matches tagged Valid .valid_dst) begin
bitvector[pack(valid_dst.indx)] <= True;
end
endmethod
method Action remove(Maybe#(PhyDst) r);
if (r matches tagged Valid .valid_dst) begin
bitvector[pack(valid_dst.indx)] <= False;
end
endmethod
method Bool search(PhyRegs r);
Bool found = False;
if (r.src1 matches tagged Valid .valid_reg) begin
found = found || bitvector[pack(valid_reg)];
end
if (r.src2 matches tagged Valid .valid_reg) begin
found = found || bitvector[pack(valid_reg)];
end
if (r.src3 matches tagged Valid .valid_reg) begin
found = found || bitvector[pack(valid_reg)];
end
if (r.dst matches tagged Valid .valid_dst) begin
found = found || bitvector[pack(valid_dst.indx)];
end
return found;
endmethod
method Action clear;
writeVReg(bitvector, replicate(False));
endmethod
endmodule
// notEmpty < first < deq < search < notFull < enq < clear
module mkPipelineScoreboard(Scoreboard#(size,t)) provisos (Bits#(t,tSz), Eq#(t));
SFifo#(size, Maybe#(t), Maybe#(t)) f <- mkPipelineSFifo(isFound);
method insert = f.enq;
method remove = f.deq;
method search1 = f.search;
method search2 = f.search;
method search3 = f.search;
method clear = f.clear;
endmodule
interface CountScoreboard#(numeric type size, type t);
method Action insert(Maybe#(t) r);
method Action remove;
method Bit#(TLog#(TAdd#(size, 1))) search1(Maybe#(t) r);
method Bit#(TLog#(TAdd#(size, 1))) search2(Maybe#(t) r);
method Bit#(TLog#(TAdd#(size, 1))) search3(Maybe#(t) r);
method Action clear;
endinterface
// search CF {enq, deq, first, notEmpty, notFull}
// deq CF enq
// search < clear
module mkCFCountScoreboard(CountScoreboard#(size,t)) provisos (Bits#(t,tSz), Eq#(t));
SCountFifo#(size, Maybe#(t), Maybe#(t)) f <- mkCFSCountFifo(isFound);
method insert = f.enq;
method remove = f.deq;
method search1 = f.search;
method search2 = f.search;
method search3 = f.search;
method clear = f.clear;
endmodule
// Scoreboard to use with real register renaming (no WAW hazards)
interface SbLookup;
method RegsReady get(PhyRegs r);
endinterface
interface SbSetBusy;
method Action set(Maybe#(PhyDst) dst);
endinterface
interface RenamingScoreboard#(numeric type setReadyNum, numeric type lazyLookupNum);
// eager look up when insert to reservation station, must see setReady & previous setBusy effects
interface Vector#(SupSize, SbLookup) eagerLookup;
interface Vector#(SupSize, SbSetBusy) setBusy;
interface Vector#(setReadyNum, Put#(PhyRIndx)) setReady;
// lazy look up in reg read, no need to see setReady effect (reg read rule will check bypass)
interface Vector#(lazyLookupNum, SbLookup) lazyLookup;
endinterface
// ordering: setReady < eagerLookup, setBusy
// lazyLookup just check EHR port 0 using wires
module mkRenamingScoreboard(RenamingScoreboard#(setReadyNum, lazyLookupNum)) provisos (
NumAlias#(portNum, TAdd#(SupSize, setReadyNum))
);
// sb[i] == False => register ready to read
// sb[i] == True => register will be written soon, not ready to read
Vector#(NumPhyReg, Ehr#(portNum, Bool)) sb <- replicateM(mkEhr(False));
function Integer setReady_port(Integer i) = i;
function Integer lookup_port(Integer i) = i + valueof(setReadyNum);
function Integer setBusy_port(Integer i) = i + valueof(setReadyNum);
Vector#(setReadyNum, Put#(PhyRIndx)) setReadyIfc = ?;
for(Integer i = 0; i < valueof(setReadyNum); i = i+1) begin
setReadyIfc[i] = (interface Put;
method Action put(PhyRIndx dst);
Integer ehrPort = setReady_port(i);
sb[dst][ehrPort] <= False;
endmethod
endinterface);
end
// wire to do lazy lookup
Wire#(Vector#(NumPhyReg, Bool)) sbWire <- mkBypassWire;
(* fire_when_enabled, no_implicit_conditions *)
rule setSbWire;
sbWire <= readVEhr(0, sb);
endrule
// function to derive lookup interface
function SbLookup getLookupIfc(Vector#(NumPhyReg, Bool) sbVec);
return (interface SbLookup;
method RegsReady get(PhyRegs r);
// everything is ready by default
RegsReady ret = RegsReady{src1: True, src2: True, src3: True, dst: True};
if (r.src1 matches tagged Valid .x) begin
ret.src1 = !sbVec[x];
end
if (r.src2 matches tagged Valid .x) begin
ret.src2 = !sbVec[x];
end
if (r.src3 matches tagged Valid .x) begin
ret.src3 = !sbVec[x];
end
return ret;
endmethod
endinterface);
endfunction
function SbLookup getEagerLookupIfc(Integer i) = getLookupIfc(readVEhr(lookup_port(i), sb));
function SbSetBusy getSetBusyIfc(Integer i);
return (interface SbSetBusy;
method Action set(Maybe#(PhyDst) dst);
if (dst matches tagged Valid .valid_dst) begin
sb[valid_dst.indx][setBusy_port(i)] <= True;
end
endmethod
endinterface);
endfunction
interface setBusy = map(getSetBusyIfc, genVector);
interface eagerLookup = map(getEagerLookupIfc, genVector);
interface lazyLookup = replicate(getLookupIfc(sbWire));
interface setReady = setReadyIfc;
endmodule
// BYPASS scoreboard to use with register renaming in an in-order core
// setReady < lookup < setBusy
interface InorderRenamingScoreboard#(
numeric type setReadyNum, numeric type lookupNum
);
interface Vector#(lookupNum, SbLookup) lookup; // together with RF read
interface Vector#(SupSize, SbSetBusy) setBusy; // at rename
interface Vector#(setReadyNum, Put#(PhyRIndx)) setReady; // exe finishes
endinterface
module mkInorderRenamingScoreboard(
InorderRenamingScoreboard#(setReadyNum, lookupNum)
) provisos (
NumAlias#(portNum, TAdd#(SupSize, setReadyNum))
);
// sb[i] == False => register ready to read
// sb[i] == True => register will be written soon, not ready to read
Vector#(NumPhyReg, Ehr#(portNum, Bool)) sb <- replicateM(mkEhr(False));
function Integer setReady_port(Integer i) = i;
function Integer lookup_port = valueof(setReadyNum);
function Integer setBusy_port(Integer i) = i + valueof(setReadyNum);
// function to derive set ready interface
function Put#(PhyRIndx) getSetReadyIfc(Integer i);
return (interface Put;
method Action put(PhyRIndx dst);
sb[dst][setReady_port(i)] <= False;
endmethod
endinterface);
endfunction
// function to derive lookup interface
function SbLookup getLookupIfc(Integer i);
Vector#(NumPhyReg, Bool) sbVec = readVEhr(lookup_port, sb);
return (interface SbLookup;
method RegsReady get(PhyRegs r);
// everything is ready by default
RegsReady ret = RegsReady{src1: True, src2: True, src3: True, dst: True};
if (r.src1 matches tagged Valid .x) begin
ret.src1 = !sbVec[x];
end
if (r.src2 matches tagged Valid .x) begin
ret.src2 = !sbVec[x];
end
if (r.src3 matches tagged Valid .x) begin
ret.src3 = !sbVec[x];
end
return ret;
endmethod
endinterface);
endfunction
// function to derive set busy interface
function SbSetBusy getSetBusyIfc(Integer i);
return (interface SbSetBusy;
method Action set(Maybe#(PhyDst) dst);
if (dst matches tagged Valid .valid_dst) begin
sb[valid_dst.indx][setBusy_port(i)] <= True;
end
endmethod
endinterface);
endfunction
interface setBusy = map(getSetBusyIfc, genVector);
interface lookup = map(getLookupIfc, genVector);
interface setReady = map(getSetReadyIfc, genVector);
endmodule