package Top { import Chisel._ import Node._; import scala.math._; class ioQueueCtrl(addr_sz: Int) extends Bundle() { val q_reset = Bool(INPUT); val enq_val = Bool(INPUT); val enq_rdy = Bool(OUTPUT); val deq_val = Bool(OUTPUT); val deq_rdy = Bool(INPUT); val wen = Bool(OUTPUT); val waddr = UFix(addr_sz, OUTPUT); val raddr = UFix(addr_sz, OUTPUT); } class queueCtrl(entries: Int) extends Component { val addr_sz = log2up(entries) override val io = new ioQueueCtrl(addr_sz); // Enqueue and dequeue pointers val enq_ptr = Reg(width = addr_sz, resetVal = UFix(0, addr_sz)); val deq_ptr = Reg(width = addr_sz, resetVal = UFix(0, addr_sz)); val full = Reg(width = 1, resetVal = Bool(false)); io.waddr := enq_ptr; io.raddr := deq_ptr; // We enq/deq only when they are both ready and valid val do_enq = io.enq_rdy && io.enq_val; val do_deq = io.deq_rdy && io.deq_val; // Determine if we have pipeline or flowthrough behaviour and // set the write enable accordingly. val empty = ~full && (enq_ptr === deq_ptr); io.wen := do_enq; // Ready signals are calculated from full register. If pipeline // behavior is enabled, then the enq_rdy signal is also calculated // combinationally from the deq_rdy signal. If flowthrough behavior // is enabled then the deq_val signal is also calculated combinationally // from the enq_val signal. io.enq_rdy := ~full; io.deq_val := ~empty; // Control logic for the enq/deq pointers and full register val deq_ptr_inc = deq_ptr + UFix(1, 1); val enq_ptr_inc = enq_ptr + UFix(1, 1); val deq_ptr_next = Mux(do_deq, deq_ptr_inc, deq_ptr); val enq_ptr_next = Mux(do_enq, enq_ptr_inc, enq_ptr); val full_next = Mux(do_enq && ~do_deq && ( enq_ptr_inc === deq_ptr ), Bool(true), Mux(do_deq && full, Bool(false), full)); when (io.q_reset) { enq_ptr <== UFix(0, addr_sz); deq_ptr <== UFix(0, addr_sz); full <== Bool(false); } otherwise { enq_ptr <== enq_ptr_next; deq_ptr <== deq_ptr_next; full <== full_next; } } class ioQueueSimplePF[T <: Data]()(data: => T) extends Bundle { val q_reset = Bool(INPUT); val enq = new ioDecoupled()(data) val deq = new ioDecoupled()(data).flip } class queueSimplePF[T <: Data](entries: Int)(data: => T) extends Component { override val io = new ioQueueSimplePF()(data); val ctrl = new queueCtrl(entries); ctrl.io.q_reset <> io.q_reset; ctrl.io.deq_val <> io.deq.valid; ctrl.io.enq_rdy <> io.enq.ready; ctrl.io.enq_val <> io.enq.valid; ctrl.io.deq_rdy <> io.deq.ready; val ram = Mem(entries, ctrl.io.wen, ctrl.io.waddr, io.enq.bits); ram.read(ctrl.io.raddr) <> io.deq.bits; } // TODO: SHOULD USE INHERITANCE BUT BREAKS INTROSPECTION CODE // class IOqueueCtrlFlow extends IOqueueCtrl class ioQueueCtrlFlow(addr_sz: Int) extends Bundle() /* IOqueueCtrl */ { val enq_val = Bool(INPUT); val enq_rdy = Bool(OUTPUT); val deq_val = Bool(OUTPUT); val deq_rdy = Bool(INPUT); val wen = Bool(OUTPUT); val waddr = UFix(addr_sz, OUTPUT); val raddr = UFix(addr_sz, OUTPUT); val flowthru = Bool(OUTPUT); } class queueCtrlFlow(entries: Int) extends Component { val addr_sz = log2up(entries) override val io = new ioQueueCtrlFlow(addr_sz); // Enqueue and dequeue pointers val enq_ptr = Reg(width = addr_sz, resetVal = UFix(0, addr_sz)); val deq_ptr = Reg(width = addr_sz, resetVal = UFix(0, addr_sz)); val full = Reg(width = 1, resetVal = Bool(false)); io.waddr := enq_ptr; io.raddr := deq_ptr; // We enq/deq only when they are both ready and valid val do_enq = io.enq_rdy && io.enq_val; val do_deq = io.deq_rdy && io.deq_val; // Determine if we have pipeline or flowthrough behaviour and // set the write enable accordingly. val empty = ~full && (enq_ptr === deq_ptr); val do_flowthru = empty && do_enq && do_deq; io.flowthru := do_flowthru; io.wen := do_enq && ~do_flowthru; // Ready signals are calculated from full register. If pipeline // behavior is enabled, then the enq_rdy signal is also calculated // combinationally from the deq_rdy signal. If flowthrough behavior // is enabled then the deq_val signal is also calculated combinationally // from the enq_val signal. io.enq_rdy := ~full; io.deq_val := ~empty || ( empty && io.enq_val ); // Control logic for the enq/deq pointers and full register val deq_ptr_inc = deq_ptr + UFix(1, 1); val enq_ptr_inc = enq_ptr + UFix(1, 1); val deq_ptr_next = Mux(do_deq && ~do_flowthru, deq_ptr_inc, deq_ptr); val enq_ptr_next = Mux(do_enq && ~do_flowthru, enq_ptr_inc, enq_ptr); val full_next = Mux(do_enq && ~do_deq && ( enq_ptr_inc === deq_ptr ), Bool(true), Mux(do_deq && full, Bool(false), full)); enq_ptr <== enq_ptr_next; deq_ptr <== deq_ptr_next; full <== full_next; } class ioQueueDpathFlow[T <: Data](addr_sz: Int)(data: => T) extends Bundle() { val wen = Bool(INPUT); val flowthru = Bool(INPUT); val deq_bits = data.asOutput; val enq_bits = data.asInput; val waddr = UFix(addr_sz, INPUT); val raddr = UFix(addr_sz, INPUT); } class queueDpathFlow[T <: Data](entries: Int)(data: => T) extends Component { val addr_sz = log2up(entries) override val io = new ioQueueDpathFlow(addr_sz)(data); val ram = Mem(entries, io.wen, io.waddr, io.enq_bits); val rout = ram(io.raddr); Mux(io.flowthru, io.enq_bits, rout) <> io.deq_bits; } class ioQueueFlowPF[T <: Data](data: => T) extends Bundle() { val enq_val = Bool(INPUT); val enq_rdy = Bool(OUTPUT); val enq_bits = data.asInput; val deq_val = Bool(OUTPUT); val deq_rdy = Bool(INPUT); val deq_bits = data.asOutput; } class queueFlowPF[T <: Data](entries: Int)(data: => T) extends Component { override val io = new ioQueueFlowPF(data); val ctrl = new queueCtrlFlow(entries); val dpath = new queueDpathFlow(entries)(data); ctrl.io.deq_rdy <> io.deq_rdy; ctrl.io.wen <> dpath.io.wen; ctrl.io.raddr <> dpath.io.raddr; ctrl.io.waddr <> dpath.io.waddr; ctrl.io.flowthru <> dpath.io.flowthru; ctrl.io.enq_val <> io.enq_val; dpath.io.enq_bits <> io.enq_bits; ctrl.io.deq_val <> io.deq_val; ctrl.io.enq_rdy <> io.enq_rdy; dpath.io.deq_bits <> io.deq_bits; } }