package LBus; import ModuleContext::*; import Counter::*; import Vector::*; import List::*; import ConfigReg::*; import GetPut::*; import ClientServer::*; import Connectable::*; import RAM::*; import HList::*; // The LBus package provides a way to create registers that are accessible // through some type of local bus (e.g., PCI). // DEFAULT VALUES: typedef 24 LBUS_ADDR_SIZE; typedef 32 LBUS_DATA_SIZE; typedef LBusContextP#(LBUS_ADDR_SIZE, LBUS_DATA_SIZE) LBusContext; typedef LBusContextIfcP#(LBUS_ADDR_SIZE, LBUS_DATA_SIZE) LBusContextIfc; LBusContext initLBusContext = LBusContextP{ items: Nil, leaves: Nil }; // ABBREVIATIONS: typedef LBUS_ADDR_SIZE SA; typedef LBUS_DATA_SIZE SD; // TYPES AND INTERFACES: // The LBSReg type is normally never seen by a user of the LbSModule; it is // only needed when creating new kinds of local bus registers. This LBSReg // interface is what the local bus uses to access a register. interface LBSReg #(type sa, type sd); method LbAddr#(sa,sd) lbsAddr(); method Action lbsSet( Bit#(sd) x1); method ActionValue#(Bit#(sd)) lbsGet(); endinterface: LBSReg // Note that the lbsGet method allows an action to be performed when the local // bus reads the value. This allows implementing, e.g., clear-on-read // registers. interface IntFlag #(type sa, type sd); method Bit#(1) flag(); endinterface: IntFlag typedef union tagged { LBSReg#(sa, sd) Rg; IntFlag#(sa, sd) Flg; } LBSItem #(type sa, type sd); // The type LbAddr is the address used to get and set registers from the local // bus. (This type is exported abstractly.) typedef union tagged { Bit#(sa) LbAddr; } LbAddr #(type sa, type sd) deriving (Literal, Eq, Bits); function Bit#(sa) unLbAddr(LbAddr#(sa,sd) x); case (x) matches tagged LbAddr .a: return (a); default: return(?); // cannot happen endcase endfunction: unLbAddr function LbAddr#(sa,sd) lbB2W(LbAddr#(sa,sd) x); case (x) matches tagged LbAddr .a: return (LbAddr (a >> 2)); default: return (?); // cannot happen endcase endfunction: lbB2W // The local bus registers are collected automagically by the ModuleCollect // monad. An LbSModule#(sa,sd,i) corresponds to a Module#(i) except that it // also keeps a set of registers. The address is sa bits wide and data items // are sd bits wide. typedef struct { List#(LBSItem#(sa, sd)) items; List#(ILbLeaf#(sa, sd)) leaves; } LBusContextP#(numeric type sa, numeric type sd); // P for polymorphic typedef ILbLeaf#(sa, sd) LBusContextIfcP#(numeric type sa, numeric type sd); // P for polymorphic interface IWithLBus#(type busIfc, type deviceIfc); interface busIfc bus; interface deviceIfc device; endinterface typedef IWithLBus#(LBSReg#(sa, sd), i) LBReg#(type sa, type sd, type i); module [mc] lbs#(Module#(LBReg#(sa, sd, i)) m)(i) provisos (IsModule#(mc, _a), Context#(mc, LBusContextP#(sa,sd))); (*hide*)LBReg#(sa, sd, i) bd <- liftModule(m); LBusContextP#(sa,sd) c <- getContext(); c.items = Cons(tagged Rg bd.bus, c.items); putContext(c); return(bd.device); endmodule module [mc] lbsInt#(mc#(IWithLBus#(IntFlag#(sa, sd), i)) m)(i) provisos (IsModule#(mc, _a), Context#(mc, LBusContextP#(sa,sd))); IWithLBus#(IntFlag#(sa,sd), i) bd <- m; LBusContextP#(sa,sd) c <- getContext(); c.items = Cons(tagged Flg bd.bus, c.items); putContext(c); return(bd.device); endmodule: lbsInt // CREATING REGISTERS typedef enum { SYNC, ASYNC, NONE } ResetType deriving (Bits, Eq); module regRW#(ResetType syncType, LbAddr#(sa,sd) aw, Integer an, r x)(LBReg#(sa, sd, Reg#(r))) provisos (Bits#(r, sr), Add#(k, sr, sd)); Reg#(r) r <- (syncType== SYNC ? mkReg(x) : syncType==ASYNC ?mkRegA(x) : mkRegU); (*hide*) Reg#(r) w <- mkWire; let vsr = valueOf(sr); let vsd = valueOf(sd); if (vsr + an > vsd) begin let sr_s = integerToString(vsr); let an_s = integerToString(an); let aw_s = bitToString(unLbAddr(aw)); String s = "LBus register: field won't fit. " + "LBus address (decimal) " + aw_s + ", offset " + an_s +", field length " + sr_s; error(s); end (*hide, fire_when_enabled*) rule complete_write; r <= w; endrule interface LBSReg bus; method lbsAddr(); return (aw); endmethod: lbsAddr method Action lbsSet(v); w <= unpack(truncate(v >> fromInteger(an))); endmethod: lbsSet method ActionValue#(Bit#(sd)) lbsGet(); actionvalue Bit#(sd) tmp = zeroExtend(pack(r)); return(tmp << fromInteger(an)); endactionvalue endmethod: lbsGet endinterface: bus interface device = asReg(r); endmodule: regRW // The mkLbRegRW module creates a register that looks like // a normal register in the module that creates it, but it is also // accessible from the local bus at the given address. module [mc] mkLbRegRW#( LbAddr#(SA,SD) aw, Integer an, r_type x) ( Reg#(r_type)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r_type, sr), Add#(k, sr, SD)); (*hide*) let ifc <- lbs(regRW(SYNC, aw, an, x)); return(ifc); endmodule: mkLbRegRW module [mc] mkLbWdRW#(LbAddr#(SA,SD) aw, r x)(Reg#(r)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r, SD)); (*hide*) let ifc <- lbs(regRW(SYNC, aw, 0, x)); return(ifc); endmodule: mkLbWdRW module [mc] mkLbRegRWA#( LbAddr#(SA,SD) aw, Integer an, r_type x) ( Reg#(r_type)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r_type, sr), Add#(k, sr, SD)); (*hide*) let ifc <- lbs(regRW(ASYNC, aw, an, x)); return(ifc); endmodule: mkLbRegRWA module [mc] mkLbWdRWA#(LbAddr#(SA,SD) aw, r x)(Reg#(r)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r, SD)); (*hide*) let ifc <- lbs(regRW(ASYNC, aw, 0, x)); return(ifc); endmodule: mkLbWdRWA module [mc] mkLbRegRWU#( LbAddr#(SA,SD) aw, Integer an) ( Reg#(r_type)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r_type, sr), Add#(k, sr, SD)); (*hide*) let ifc <- lbs(regRW(NONE, aw, an, ?)); return(ifc); endmodule: mkLbRegRWU module [mc] mkLbWdRWU#(LbAddr#(SA,SD) aw)(Reg#(r)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r, SD)); (*hide*) let ifc <- lbs(regRW(NONE, aw, 0, ?)); return(ifc); endmodule: mkLbWdRWU module regW1tC#(LbAddr#(sa,sd) aw, Integer an, Bit#(1) x)(LBReg#(sa, sd, Reg#(Bit#(1)))) provisos (Add#(k, 1, sd)); Reg#(Bit#(1)) r <- mkReg(x); let vsd = valueOf(sd); if(1 + an > vsd) error("regW1tC: field won't fit"); interface LBSReg bus; method lbsAddr(); return (aw); endmethod: lbsAddr method Action lbsSet(v); r <= unpack(pack(r) & invert(truncate(v >> fromInteger(an)))); endmethod: lbsSet method lbsGet(); actionvalue Bit#(sd) tmp = zeroExtend(pack(r)); return(tmp << fromInteger(an)); endactionvalue endmethod: lbsGet endinterface: bus interface device = asReg(r); endmodule: regW1tC module [mc] mkLbRegW1tC#(LbAddr#(SA,SD) aw, Integer an, Bit#(1) x)(Reg#(Bit#(1))) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Add#(k, 1, SD)); (*hide*) let ifc <- lbs(regW1tC(aw, an, x)); return(ifc); endmodule: mkLbRegW1tC module regRO#(ResetType syncType, LbAddr#(sa,sd) aw, Integer an, r x)(LBReg#(sa, sd, Reg#(r))) provisos (Bits#(r, sr), Add#(k, sr, sd)); Reg#(r) r <- (syncType== SYNC ? mkReg(x) : syncType==ASYNC ?mkRegA(x) : mkRegU); let vsr = valueOf(sr); let vsd = valueOf(sd); if (vsr + an > vsd) error("regRO: field won't fit"); interface LBSReg bus; method lbsAddr(); return (aw); endmethod: lbsAddr method Action lbsSet(v); endmethod: lbsSet method lbsGet(); actionvalue Bit#(sd) tmp = zeroExtend(pack(r)); return(tmp << fromInteger(an)); endactionvalue endmethod: lbsGet endinterface: bus interface device = asReg(r); endmodule: regRO // The mkLbRegRO module creates a register that looks like // a normal register in the module that creates it, but it is also // accessible from the local bus at the given address. // From the local bus the register is read-only; attempts to write it // have no effect. // The created register has to have a bit width smaller than or equal to the // local bus width. If it is smaller it will padded with zeroes on the left. module [mc] mkLbRegRO#(LbAddr#(SA,SD) aw, Integer an, r x)(Reg#(r)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r, sr), Add#(k, sr, SD)); (*hide*) let ifc <- lbs(regRO(SYNC, aw, an, x)); return(ifc); endmodule: mkLbRegRO module [mc] mkLbWdRO#(LbAddr#(SA,SD) aw, r x)(Reg#(r)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r, SD)); (*hide*) let ifc <- lbs(regRO(SYNC, aw, 0, x)); return(ifc); endmodule: mkLbWdRO module [mc] mkLbRegROA#(LbAddr#(SA,SD) aw, Integer an, r x)(Reg#(r)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r, sr), Add#(k, sr, SD)); (*hide*) let ifc <- lbs(regRO(ASYNC, aw, an, x)); return(ifc); endmodule: mkLbRegROA module [mc] mkLbWdROA#(LbAddr#(SA,SD) aw, r x)(Reg#(r)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r, SD)); (*hide*) let ifc <- lbs(regRO(ASYNC, aw, 0, x)); return(ifc); endmodule: mkLbWdROA module [mc] mkLbRegROU#(LbAddr#(SA,SD) aw, Integer an)(Reg#(r)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r, sr), Add#(k, sr, SD)); (*hide*) let ifc <- lbs(regRO(NONE, aw, an, ?)); return(ifc); endmodule: mkLbRegROU module [mc] mkLbWdROU#(LbAddr#(SA,SD) aw)(Reg#(r)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r, SD)); (*hide*) let ifc <- lbs(regRO(NONE, aw, 0, ?)); return(ifc); endmodule: mkLbWdROU module [mc] mkLbConfigRegRO#(LbAddr#(SA,SD) aw, Integer an, r x)(Reg#(r)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Bits#(r, sr), Add#(k, sr, SD)); (*hide*) let ifc <- lbs(regRO(SYNC, aw, an, x)); Wire#(r) w <- mkWire; (* fire_when_enabled *) rule commit; ifc <= w; endrule method _write = w._write; method _read = ifc._read; endmodule: mkLbConfigRegRO interface Accum #(type n); method Action add(Bit#(n) x1); method Bit#(n) value(); endinterface: Accum module accum#(LbAddr#(sa,sd) aw, Integer an, Bit#(k) x)(LBReg#(sa, sd, Accum#(k))) provisos (Add#(k, i, sd)); Counter#(k) c <- mkCounter(x); PulseWire getting <- mkPulseWire; let vk = valueOf(k); let vsd = valueOf(sd); if (vk + an > vsd) error("accum: field won't fit"); interface LBSReg bus; method lbsAddr(); return (aw); endmethod: lbsAddr method Action lbsSet(v); c.setF(truncate(v >> fromInteger(an))); endmethod: lbsSet method lbsGet(); actionvalue getting.send(); c.setC(0); Bit#(sd) tmp = zeroExtend(c.value); return(tmp << fromInteger(an)); endactionvalue endmethod: lbsGet endinterface: bus interface Accum device; method add if (!getting) = c.inc; method value = c.value; endinterface: device endmodule: accum module [mc] mkLbAccum#(LbAddr#(SA,SD) aw, Integer an, Bit#(k) x)(Accum#(k)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Add#(k, i, SD)); (*hide*) let ifc <- lbs(accum(aw, an, x)); return(ifc); endmodule: mkLbAccum interface Interrupt; method Action set(); method Bool _read(); endinterface: Interrupt module w1tc#(LbAddr#(sa,sd) aw, Integer an)(LBReg#(sa, sd, Reg#(Bit#(1)))) provisos (Add#(k, 1, sd)); Reg#(Bit#(1)) r <- mkReg(0); Reg#(Bit#(1)) s <- mkReg(0); PulseWire setting <- mkPulseWire; rule transfer (!setting); r <= r | s; s <= 0; endrule: transfer interface LBSReg bus; method lbsAddr(); return (aw); endmethod: lbsAddr method Action lbsSet(v); if ((v >> fromInteger(an))[0]==1) begin r <= s; setting.send(); end endmethod: lbsSet method lbsGet(); actionvalue Bit#(sd) tmp = zeroExtend(r); return(tmp << fromInteger(an)); endactionvalue endmethod: lbsGet endinterface: bus interface Reg device; method _read(); return (r); endmethod: _read method Action _write(x); s <= 1; endmethod: _write endinterface: device endmodule: w1tc module [mc] mkLbW1tC#(LbAddr#(SA,SD) aw, Integer an)(Reg#(Bit#(1))) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Add#(k, 1, SD)); (*hide*) let ifc <- lbs(w1tc(aw, an)); return(ifc); endmodule: mkLbW1tC module [mc] interrupt#(LbAddr#(SA,SD) aw1, Integer an1, LbAddr#(SA,SD) aw2, Integer an2) (IWithLBus#(IntFlag#(SA,SD), Interrupt)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Add#(k, 1, SD)); Reg#(Bit#(1)) r <- mkLbW1tC(aw1, an1); Reg#(Bit#(1)) m <- mkLbRegRW(aw2, an2, 0); interface IntFlag bus; method flag(); return (r & invert(m)); endmethod: flag endinterface: bus interface Interrupt device; method Action set(); r <= 1; endmethod: set method _read = (r == 1); endinterface: device endmodule: interrupt module [mc] mkLbInterrupt#(LbAddr#(SA,SD) aw1, Integer an1, LbAddr#(SA,SD) aw2, Integer an2) (Interrupt) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Add#(k, 1, SD)); (*hide*) let ifc <- lbsInt(interrupt(aw1, an1, aw2, an2)); return(ifc); endmodule: mkLbInterrupt interface RegHandler #(type a, type b); method a getRequest(); method Action storeResponse(b x1); endinterface: RegHandler module [mc] mkLbClient#(LbAddr#(SA,SD) reqw, Integer reqn, LbAddr#(SA,SD) ackw, Integer ackn, mc#(RegHandler#(a, b)) mkRH) (Client#(a,b)) provisos (IsModule#(mc, _a), Context#(mc, LBusContext), Add#(k, 1, SD)); RegHandler#(a, b) rh <- mkRH; Reg#(Bit#(1)) req <- mkLbRegRW(reqw, reqn, 0); Reg#(Bit#(1)) ack <- mkLbRegW1tC(ackw, ackn, 0); Reg#(Bit#(1)) prevReq <- mkReg(0); Reg#(Bool) reqFlag <- mkReg(False); rule trigger; prevReq <= req; if (prevReq == 0 && req == 1) reqFlag <= True; endrule: trigger interface Get request; method get() if (reqFlag) ; actionvalue reqFlag <= False; return(rh.getRequest); endactionvalue endmethod: get endinterface: request interface Put response; method Action put(x) ; rh.storeResponse(x); ack <= 1; endmethod: put endinterface: response endmodule: mkLbClient // The mkLbOffset function can be used to add an offset to all local // bus register addresses in an LbSModule. module [mc] mkLbOffset#(LbAddr#(SA,SD) a, mc#(i) m)(i) provisos (IsModule#(mc, _a), Context#(mc, LBusContext)); function LBSItem#(SA,SD) f(LBSItem#(SA,SD) x); case (a) matches tagged LbAddr .o: case (x) matches tagged Rg .r: return (tagged Rg (interface LBSReg; method lbsAddr(); return (LbAddr (unLbAddr(r.lbsAddr) + o)); endmethod: lbsAddr method lbsSet = r.lbsSet; method lbsGet = r.lbsGet; endinterface: LBSReg)); default: return (x); endcase default: return (?); // cannot happen endcase endfunction LBusContext c <- getContext(); c.items = List::map(f, c.items); putContext(c); (*hide*) let ifc <- m; return (ifc); endmodule // COLLECTING REGISTERS TOGETHER // The external interface of a local bus is as follows. // It is through this interface that register accesses normally happen. typedef enum {LbRead, LbWrite} LbRWop deriving (Bits, Eq); (*always_ready, always_enabled*) interface ILBus #(type sa, type sd); method Action req(Bool valid, LbRWop op, Bit#(sa) addr, Bit#(sd) dat); method Bit#(sd) rdDat(); method Bit#(1) ack(); method Bit#(1) inrpt(); endinterface: ILBus typedef struct { LbRWop wr; Bit#(sa) adr; Bit#(sd) dat; } LbReq#(type sa, type sd) deriving (Bits, Eq); interface ILbLeaf#(type sa, type sd); interface Put#(Maybe#(LbReq#(sa, sd))) req; interface Get#(Maybe#(Bit#(sd))) ack; (*always_ready*) interface Get#(Bit#(1)) inrpt; endinterface: ILbLeaf interface ILbNode#(type sa, type sd); interface Get#(Maybe#(LbReq#(sa, sd))) req; interface Put#(Maybe#(Bit#(sd))) ack; interface Put#(Bit#(1)) inrpt; endinterface: ILbNode instance Connectable#(ILbLeaf#(sa, sd), ILbNode#(sa, sd)); module mkConnection#(ILbLeaf#(sa, sd) l, ILbNode#(sa, sd) n)(Empty); mkConnection(l.req, n.req); mkConnection(l.ack, n.ack); mkConnection(l.inrpt, n.inrpt); endmodule endinstance // Given a LbSModule with a set of register we can extract // the local bus interface and the normal interface. instance Expose#(LBusContextP#(sa,sd), LBusContextIfcP#(sa,sd), CLOCKCONTEXTSIZE); module unburyContext#(LBusContextP#(sa,sd) lbc)(LBusContextIfcP#(sa,sd)); function isReg(x); case (x) matches tagged Rg .* : return True; default : return False; endcase endfunction: isReg function isFlag(x); case (x) matches tagged Flg .* : return True; default : return False; endcase endfunction: isFlag function theReg(x); case (x) matches tagged Rg .y : return (y); default: return (?); // cannot happen endcase endfunction: theReg function theFlag(x); case (x) matches tagged Flg .y : return (y.flag); default: return (?); // cannot happen endcase endfunction: theFlag let lbs = lbc.items; let leaves = lbc.leaves; if (length(lbs)!=0) begin let regs = List::map(theReg, List::filter(isReg, lbs)); let flags = List::map(theFlag, List::filter(isFlag, lbs)); let interruption = foldt(or_f, 0, flags); Reg#(LbReq#(sa, sd)) reqtX <- mkConfigReg(?); Reg#(LbReq#(sa, sd)) reqt <- mkConfigReg(?); Reg#(Bit#(sd)) ackt <- mkConfigReg(?); Reg#(Bool) reqFlag <- mkConfigReg(False); Reg#(Bool) ackFlag <- mkConfigReg(False); Reg#(Bool) reqFlagX <- mkConfigReg(False); Reg#(Bit#(1)) iFlag <- mkConfigReg(0); (* fire_when_enabled, no_implicit_conditions *) rule clear_ackFlag (ackFlag); ackFlag <= False; endrule: clear_ackFlag (* fire_when_enabled, no_implicit_conditions *) rule set_iFlag; iFlag <= interruption; endrule: set_iFlag (* fire_when_enabled, no_implicit_conditions *) rule transfer_reqFlag (reqFlagX); reqFlag <= True; reqt <= reqtX; endrule: transfer_reqFlag rule readAndWrite (reqFlag); reqFlag <= False; function g(lbi); return (actionvalue if (unLbAddr(lbi.lbsAddr) == reqt.adr) actionvalue let v <- lbi.lbsGet; return (v); endactionvalue else actionvalue return (0); endactionvalue endactionvalue); endfunction function h(lbi); action if (unLbAddr(lbi.lbsAddr) == reqt.adr) lbi.lbsSet(reqt.dat); endaction endfunction if (reqt.wr == LbRead) action ackFlag <= True; let vs <- List::mapM(g, regs); ackt <= foldt(or_f, 0, vs); endaction else List::joinActions(List::map(h, regs)); endrule: readAndWrite ILbLeaf#(sa, sd) newLeaf = ( interface ILbLeaf; interface Put req; method Action put(mr); reqFlagX <= isJust(mr); reqtX <= validValue(mr); endmethod: put endinterface: req interface Get ack; method get(); actionvalue if (ackFlag) return (tagged Valid ackt); else return (tagged Invalid); endactionvalue endmethod: get endinterface: ack interface Get inrpt; method get() ; actionvalue return(iFlag); endactionvalue endmethod: get endinterface: inrpt endinterface ); leaves = Cons(newLeaf, leaves); end // Now deal with the leaves: let n = length(leaves); ILbLeaf#(sa, sd) resLeaf = ?; case (n) 1: resLeaf = List::head(leaves); 0: resLeaf = ( interface ILbLeaf; interface Put req; method Action put(mr); endmethod: put endinterface: req interface Get ack; method get(); actionvalue return (tagged Valid 0); endactionvalue endmethod: get endinterface: ack interface Get inrpt; method get() ; actionvalue return(0); endactionvalue endmethod: get endinterface: inrpt endinterface ); default: begin Reg#(Maybe#(LbReq#(sa, sd))) maybeReq <- mkConfigReg(Invalid); rule transmitReq; for(Integer i = 0; i