# TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
# MODIFICATIONS.
-from nmigen import Module, Signal, Memory, Mux
+from nmigen import Module, Signal, Memory, Mux, Elaboratable
from nmigen.tools import bits_for
from nmigen.cli import main
from nmigen.lib.fifo import FIFOInterface
# translated from https://github.com/freechipsproject/chisel3/blob/a4a29e29c3f1eed18f851dcf10bdc845571dfcb6/src/main/scala/chisel3/util/Decoupled.scala#L185 # noqa
-class Queue(FIFOInterface):
+class Queue(FIFOInterface, Elaboratable):
def __init__(self, width, depth, fwft=True, pipe=False):
""" Queue (FIFO) with pipe mode and first-write fall-through capability
- * width: width of Queue data in/out
- * depth: queue depth. NOTE: may be set to 0 (this is ok)
- * fwft : first-write, fall-through mode (Chisel Queue "flow" mode)
- * pipe : pipe mode. NOTE: this mode can cause unanticipated
- problems. when read is enabled, so is writeable.
- therefore if read is enabled, the data ABSOLUTELY MUST
- be read.
+ * :width: width of Queue data in/out
+ * :depth: queue depth. NOTE: may be set to 0 (this is ok)
+ * :fwft : first-write, fall-through mode (Chisel Queue "flow" mode)
+ * :pipe : pipe mode. NOTE: this mode can cause unanticipated
+ problems. when read is enabled, so is writeable.
+ therefore if read is enabled, the data ABSOLUTELY MUST
+ be read.
+
+ fwft mode = True basically means that the data may be transferred
+ combinatorially from input to output.
+
+ Attributes:
+ * level: available free space (number of unread entries)
din = enq_data, writable = enq_ready, we = enq_valid
dout = deq_data, re = deq_ready, readable = deq_valid
FIFOInterface.__init__(self, width, depth, fwft)
self.pipe = pipe
self.depth = depth
- self.count = Signal(bits_for(depth))
+ self.level = Signal(bits_for(depth))
def elaborate(self, platform):
m = Module()
m.submodules.ram_read = ram_read = ram.read_port(synchronous=False)
m.submodules.ram_write = ram_write = ram.write_port()
+ # convenience names
+ p_ready_o = self.writable
+ p_valid_i = self.we
+ enq_data = self.din
+
+ n_valid_o = self.readable
+ n_ready_i = self.re
+ deq_data = self.dout
+
# intermediaries
ptr_width = bits_for(self.depth - 1) if self.depth > 1 else 0
enq_ptr = Signal(ptr_width) # cyclic pointer to "insert" point (wrport)
deq_max.eq(deq_ptr == self.depth - 1),
empty.eq(ptr_match & ~maybe_full),
full.eq(ptr_match & maybe_full),
- do_enq.eq(self.writable & self.we), # write conditions ok
- do_deq.eq(self.re & self.readable), # read conditions ok
+ do_enq.eq(p_ready_o & p_valid_i), # write conditions ok
+ do_deq.eq(n_ready_i & n_valid_o), # read conditions ok
# set readable and writable (NOTE: see pipe mode below)
- self.readable.eq(~empty), # cannot read if empty!
- self.writable.eq(~full), # cannot write if full!
+ n_valid_o.eq(~empty), # cannot read if empty!
+ p_ready_o.eq(~full), # cannot write if full!
# set up memory and connect to input and output
ram_write.addr.eq(enq_ptr),
- ram_write.data.eq(self.din),
+ ram_write.data.eq(enq_data),
ram_write.en.eq(do_enq),
ram_read.addr.eq(deq_ptr),
- self.dout.eq(ram_read.data) # NOTE: overridden in fwft mode
+ deq_data.eq(ram_read.data) # NOTE: overridden in fwft mode
]
# under write conditions, SRAM write-pointer moves on next clock
# this done combinatorially to give the exact same characteristics
# as Memory "write-through"... without relying on a changing API
if self.fwft:
- with m.If(self.we):
- m.d.comb += self.readable.eq(1)
+ with m.If(p_valid_i):
+ m.d.comb += n_valid_o.eq(1)
with m.If(empty):
- m.d.comb += self.dout.eq(self.din)
+ m.d.comb += deq_data.eq(enq_data)
m.d.comb += do_deq.eq(0)
- with m.If(self.re):
+ with m.If(n_ready_i):
m.d.comb += do_enq.eq(0)
- # pipe mode: read-enabled requires writability.
+ # pipe mode: if next stage says it's ready (readable), we
+ # *must* declare the input ready (writeable).
if self.pipe:
- with m.If(self.re):
- m.d.comb += self.writable.eq(1)
+ with m.If(n_ready_i):
+ m.d.comb += p_ready_o.eq(1)
- if self.depth == 1 << len(self.count): # is depth a power of 2
- m.d.comb += self.count.eq(
- Mux(self.maybe_full & self.ptr_match, self.depth, 0)
- | self.ptr_diff)
+ # set the count (available free space), optimise on power-of-two
+ if self.depth == 1 << ptr_width: # is depth a power of 2
+ m.d.comb += self.level.eq(
+ Mux(maybe_full & ptr_match, self.depth, 0) | ptr_diff)
else:
- m.d.comb += self.count.eq(Mux(ptr_match,
+ m.d.comb += self.level.eq(Mux(ptr_match,
Mux(maybe_full, self.depth, 0),
Mux(deq_ptr > enq_ptr,
self.depth + ptr_diff,
def queue_ports(queue, name_prefix):
retval = []
- for name in ["count",
+ for name in ["level",
"dout",
"readable",
"writable"]:
m.d.comb += port.eq(signal)
retval.append(signal)
return retval
+
m.submodules.reg_stage = reg_stage
ports += queue_ports(reg_stage, "reg_stage_")
m.submodules.break_ready_chain_stage = break_ready_chain_stage