diff --git a/gearbox.py b/gearbox.py index 1ffb0f4c685dbd44faf8c0a080d44725727f36a3..3283bc9ea461aaf3e89f3b43d5c66d79f4f3bc0f 100755 --- a/gearbox.py +++ b/gearbox.py @@ -1,6 +1,7 @@ from enum import Enum from nmigen import * from nmigen.hdl.rec import * +from nmigen.asserts import * from nmigen.cli import main @@ -82,7 +83,7 @@ class GearboxFlowControl(Elaboratable): self.len_storage = len_storage self.bus = GearboxFCBus(len_storage=len_storage) - print(len(self.bus.read_ptr)) + #print(len(self.bus.read_ptr)) def elaborate(self, platform): @@ -235,7 +236,7 @@ class ArbitraryGearbox(Elaboratable): with m.If(self.bus.fault == 0): # read index update: - with m.If(0 == 1): + with m.If(self.bus.ready_in == 1): with m.If(read_ptr + self.out_width >= len_storage): m.d.sync += read_ptr.eq(read_ptr + self.out_width - len_storage) with m.Else(): @@ -280,13 +281,80 @@ class ArbitraryGearbox(Elaboratable): )) + #m.d.sync += Assert(read_ptr == 0) + #m.d.comb += Assume((self.bus.data_in == 3) |( self.bus.data_in==5)) return m +class IdempotentGearbox(Elaboratable): + def __init__(self, *, in_width, out_width): + self.in_width = in_width + self.out_width = out_width + assert(in_width == out_width) + + self.bus = GearboxBus(in_width=in_width, out_width=out_width) + + def elaborate(self, platform): + m = Module() + skid_buffer = Signal(self.in_width) + skid_buffer_frozen = Signal(1) + + # we signal ready to upstream if and only if the skid buffer is empty + m.d.comb += self.bus.ready_out.eq(~skid_buffer_frozen) + + + # should we capture a value in the skid buffer? + # we should if upstream has data for us *and* downstream cannot accept + + # If we want to fill our skid buffer, we need: + # 1. valid input data (bus.valid_in == 1) + # 2. a buffer that is empty (skid_buffer_frozen == 0) + # 3. stalled downstream (bus.ready_in == 0) & (bus.valid_out == 1) + + with m.If(self.bus.valid_in & (~skid_buffer_frozen) & (~self.bus.ready_in) & (self.bus.valid_out)): + m.d.sync += skid_buffer_frozen.eq(1) + + # if downstream is accepting data again, we will flush our skid buffer + with m.If(self.bus.ready_in == 1): + m.d.sync += skid_buffer_frozen.eq(0) + + + # Stalled == (bus.ready_in == 0 & bus.valid_out == 1) + # so not stalled = !(bus.ready_in == 0 & bus.valid_out == 1) + # = bus.ready_in == 1 | bus.valid_out == 0 + # by de Morgan + + with m.If((self.bus.ready_in) | (~self.bus.valid_out)): + m.d.sync += self.bus.valid_out.eq(self.bus.valid_in | skid_buffer_frozen) + + # data path + + # always clock data into the skid buffer as long as the buffer isn't filled + + with m.If(skid_buffer_frozen == 0): + m.d.sync += skid_buffer.eq(self.bus.data_in) + + # not stalled condition + with m.If((self.bus.ready_in) | (~self.bus.valid_out)): + with m.If(skid_buffer_frozen == 1): + m.d.sync += self.bus.data_out.eq(skid_buffer) + with m.Elif(self.bus.valid_in == 1): + m.d.sync += self.bus.data_out.eq(self.bus.data_in) + + + eggdrop = Signal(3) + + m.d.comb += eggdrop.eq(Past(self.bus.data_in)) + + with m.If(self.bus.ready_in == 1): + m.d.sync += Assert(self.bus.data_out == eggdrop) + + + return m class DummyPlug(Elaboratable): @@ -297,12 +365,15 @@ class DummyPlug(Elaboratable): def elaborate(self, platform): m = Module() - m.submodules.gearbox = gearbox = ArbitraryGearbox(in_width=(9-3), out_width=3) - counter = Signal(8) - m.d.sync += counter.eq(counter+1) + m.submodules.gearbox = gearbox = IdempotentGearbox(in_width=(3), out_width=3) + #counter = Signal(8) + #m.d.sync += counter.eq(counter+1) - with m.If(counter == 3): - m.d.comb += gearbox.bus.data_in.eq(1) +# with m.If(counter == 3): +# m.d.comb += gearbox.bus.valid_in.eq(1) + m.d.comb += gearbox.bus.data_in.eq(AnySeq(3)) + m.d.comb += gearbox.bus.ready_in.eq(AnySeq(1)) + m.d.comb += gearbox.bus.valid_in.eq(AnySeq(1)) return m