package PriQ; // NOTE that in the following discussion we shall use the following // conventions: // 1. The head of the queue (the next to be dequeued) is at the right-hand // end, and has the lowest index (0). // 2. A higher priority is indicated by the priority value being higher. // Entries with higher priority come out earlier. import RevertingVirtualReg::*; import QType::*; // The type of "moves". The deriving clause tells the compiler to use the // obvious definitions of equality of moves and their representation in bits. typedef enum {LEFT, SAME, RIGHT, REPLACE} Move // LEFT means "receives from deriving (Eq, Bits); // left" etc. // This is the main module definition. The parameter pipelining is a flag to // tell whether we wish the implementation to be pipelined, n is the depth of // the queue to be made, and qe is the type of the elements. The provisos // ensure (1) that values of this type can be represented as bits (so that // they can be stored in registers); (2) that they can be ordered (so that we // can use the <, >, etc. operators on them). module mkSizedPriQ#(Bool pipelining, Integer n)(Q#(qe)) provisos (Bits#(qe,sqe), Ord#(qe)); // Note: a "tagged union" value of type "Maybe#(a)" is either "Invalid", or // "Valid(x)", where x is a value of type a. // We declare an array of registers to hold the queue. They contain // Invalid if they are empty, and Valid(v) if they contain the value v. Reg#(Maybe#(qe)) queue[n]; for (Integer i = 0; i= ordering. // If neither an enqueue nor a dequeue request has been received, all the // elements stay the same. function Move neither(Integer i) = SAME; // If only a dequeue is to happen, each element is replaced by the one to // its left. function Move deq_only(Integer i) = LEFT; // If only an enqueue is to happen, the new element must be slotted in at // the appropriate place; all to the left of it are replaced by the one to // their right, while all to the right of it stay the same. function Move enq_only(qe v, Integer i); let self = current_entry(i); let right =current_entry(i-1); if (i==0) return (case (self) matches tagged Invalid: return REPLACE; tagged Valid .s: return (s>=v) ? SAME : REPLACE; endcase); else return (case (tuple2(self,right)) matches {.*, tagged Invalid}: return SAME; {tagged Invalid, tagged Valid .p}: return ((p>=v) ? REPLACE : RIGHT ); {tagged Valid .s, tagged Valid .p}: return ((s>=v) ? SAME : (p>=v) ? REPLACE : RIGHT ); endcase); endfunction // Finally comes the function to handle simultaneous enqueue and dequeue. function Move both(qe v, Integer i); let self = current_entry(i); let left = (i==n-1 ? Invalid : current_entry(i+1)); return (case (tuple2(left,self)) matches {.*, tagged Invalid}: return SAME; {tagged Valid .l, tagged Valid .s}: return ((l>=v) ? LEFT : (s>=v || i==1) ? REPLACE : SAME ); {tagged Invalid, tagged Valid .s}: return ((s>=v || i==1) ? REPLACE : SAME ); endcase); endfunction // This is the rule which does all the work. It is essential that it fires // on every clock, so we specify attributes which the tool will check (and // give an error report if they are not satisfied). (* no_implicit_conditions, fire_when_enabled *) rule process_requests; // We define two arrays of moves: the first will be initialised with the // moves required by the incoming commands, while the second will be // used to control the queue elements. Move stage1_moves[n]; Move stage2_moves[n]; // These next two variable hold incoming queue entries: the first is // initialised from the enq method; the second will be stored in the // appropriate queue element. Maybe#(qe) stage1_v = rw_enq.wget; Maybe#(qe) stage2_v; // We now choose the appropriate function from the group defined above, // by testing the values on the RWires. NOTE that in BSV arguments can // be supplied to functions in stages. Here "enq_only" and "both" need // two arguments; we give them one here, and then they require just one // more, exactly like the other two functions. let the_fun = (case (tuple2(rw_enq.wget, rw_deq.wget)) matches {tagged Valid .v, tagged Invalid}: return enq_only(v); {tagged Invalid, tagged Valid .*}: return deq_only; {tagged Valid .v, tagged Valid .*}: return both(v); {tagged Invalid, tagged Invalid}: return neither; endcase); // This loop applies the chosen function (in parallel) to all the // slots, initializing the array of moves for (Integer i = 0; i