generated from saji/ecp5-template
This commit is contained in:
parent
386403bd12
commit
ca472d7112
|
@ -70,6 +70,7 @@ class Hub75Data(wiring.Signature):
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class SwapBuffer(wiring.Component):
|
class SwapBuffer(wiring.Component):
|
||||||
"""A pair of BRAMs for holdling line data that are swapped between using an external signal.
|
"""A pair of BRAMs for holdling line data that are swapped between using an external signal.
|
||||||
|
|
||||||
|
@ -129,7 +130,6 @@ class SwapBuffer(wiring.Component):
|
||||||
# self.write_port.data.eq(Mux(self.selector, write0.data, write1.data)),
|
# self.write_port.data.eq(Mux(self.selector, write0.data, write1.data)),
|
||||||
write0.data.eq(self.write_port.data),
|
write0.data.eq(self.write_port.data),
|
||||||
write1.data.eq(self.write_port.data),
|
write1.data.eq(self.write_port.data),
|
||||||
|
|
||||||
read0.addr.eq(self.read_port.addr),
|
read0.addr.eq(self.read_port.addr),
|
||||||
read1.addr.eq(self.read_port.addr),
|
read1.addr.eq(self.read_port.addr),
|
||||||
read0.en.eq(~self.selector),
|
read0.en.eq(~self.selector),
|
||||||
|
@ -141,63 +141,49 @@ class SwapBuffer(wiring.Component):
|
||||||
|
|
||||||
|
|
||||||
class Hub75StringDriver(wiring.Component):
|
class Hub75StringDriver(wiring.Component):
|
||||||
"""A data driver for Hub75 panels. This accesses the line memory and feeds out the data"""
|
"""A data driver for Hub75 panels. This accesses the line memory and feeds out the data.
|
||||||
|
It is controlled by a Hub75Coordinator to signal when it should run and what bit of the data
|
||||||
|
it should send.
|
||||||
|
"""
|
||||||
|
|
||||||
bcm_select: In(3)
|
bcm_select: In(3)
|
||||||
done: Out(1)
|
done: Out(1)
|
||||||
start: In(1)
|
start: In(1)
|
||||||
active_bank: In(1) # the bank to use
|
bram_port: In(ReadPort.Signature(addr_width=9, shape=Rgb888Layout))
|
||||||
bram_port: Out(WritePort.Signature(
|
|
||||||
addr_width=9, shape=Rgb888Layout
|
|
||||||
)) # the other line to be swapped to.
|
|
||||||
display_out: Out(Hub75Data()) # data signal output.
|
display_out: Out(Hub75Data()) # data signal output.
|
||||||
|
|
||||||
|
def __init__(self, panel_length: int = 128, *, src_loc_at=0):
|
||||||
|
self.panel_length = panel_length
|
||||||
|
super().__init__(None, src_loc_at=src_loc_at)
|
||||||
|
|
||||||
def elaborate(self, platform: Platform) -> Module:
|
def elaborate(self, platform: Platform) -> Module:
|
||||||
m = Module()
|
m = Module()
|
||||||
|
|
||||||
# add two memories
|
# add two memories
|
||||||
m.submodules.bram0 = bram0 = Memory(shape=Rgb888Layout, depth=512, init=[])
|
|
||||||
m.submodules.bram1 = bram1 = Memory(shape=Rgb888Layout, depth=512, init=[])
|
|
||||||
|
|
||||||
# We use two brams here so we can swap between each - that way we can update the data
|
self._counter = counter = Signal(32)
|
||||||
# while displaying the other line. Switching is controlled by the coordinator.
|
|
||||||
|
|
||||||
readports = Mux([bram0.read_port(), bram1.read_port()])
|
|
||||||
active_readport = readports[self.active_bank]
|
|
||||||
m.d.comb += active_readport.eq(readports[self.active_bank])
|
|
||||||
writeports = Array([bram0.write_port(), bram1.write_port()])
|
|
||||||
m.d.comb += self.bram_port.eq(writeports[~self.active_bank])
|
|
||||||
|
|
||||||
# We want to set up the bram ports
|
|
||||||
port0 = bram0.read_port()
|
|
||||||
port1 = bram1.read_port()
|
|
||||||
# the enable for these ports is based on active bank.
|
|
||||||
m.d.comb += port0.en.eq(self.active_bank)
|
|
||||||
m.d.comb += port1.en.eq(~self.active_bank)
|
|
||||||
|
|
||||||
counter = Signal(32)
|
|
||||||
m.d.sync += counter.eq(counter + 1)
|
m.d.sync += counter.eq(counter + 1)
|
||||||
|
|
||||||
ram_rgb_slice = Cat(
|
ram_rgb_slice = Cat(
|
||||||
active_readport.data["red"].bit_select(self.bcm_shift, 1),
|
self.bram_port.data["red"].bit_select(self.bcm_select, 1),
|
||||||
active_readport.data["blue"].bit_select(self.bcm_shift, 1),
|
self.bram_port.data["blue"].bit_select(self.bcm_select, 1),
|
||||||
active_readport.data["green"].bit_select(self.bcm_shift, 1),
|
self.bram_port.data["green"].bit_select(self.bcm_select, 1),
|
||||||
)
|
)
|
||||||
|
|
||||||
pixnum = Signal(8, reset=127)
|
pixnum = Signal(range(self.panel_length), init=self.panel_length - 1)
|
||||||
pixrow = Signal(1, reset=0)
|
pixrow = Signal(1, init=0)
|
||||||
m.d.comb += self.buf_addr.eq(Cat(pixrow, pixnum))
|
m.d.comb += self.bram_port.addr.eq(Cat(pixrow, pixnum))
|
||||||
|
|
||||||
with m.FSM():
|
with m.FSM():
|
||||||
with m.State("init"):
|
with m.State("init"):
|
||||||
m.d.sync += [
|
m.d.sync += [
|
||||||
self.done.eq(0),
|
self.done.eq(0),
|
||||||
counter.eq(0),
|
counter.eq(0),
|
||||||
pixnum.eq(127),
|
pixnum.eq(self.panel_length - 1),
|
||||||
pixrow.eq(0),
|
pixrow.eq(0),
|
||||||
]
|
]
|
||||||
with m.If(self.start == 1):
|
with m.If(self.start == 1):
|
||||||
m.d.sync += active_readport.en.eq(1)
|
m.d.sync += self.bram_port.en.eq(1)
|
||||||
m.next = "prefetch"
|
m.next = "prefetch"
|
||||||
with m.State("prefetch"):
|
with m.State("prefetch"):
|
||||||
with m.If(counter == 0):
|
with m.If(counter == 0):
|
||||||
|
@ -209,23 +195,32 @@ class Hub75StringDriver(wiring.Component):
|
||||||
with m.Elif(counter == 3):
|
with m.Elif(counter == 3):
|
||||||
m.d.sync += [self.display_out.rgb1.eq(ram_rgb_slice), counter.eq(0)]
|
m.d.sync += [self.display_out.rgb1.eq(ram_rgb_slice), counter.eq(0)]
|
||||||
m.next = "writerow"
|
m.next = "writerow"
|
||||||
|
with m.State("writerow"):
|
||||||
|
with m.If(counter[0:1] == 0):
|
||||||
|
# rising edge of the clock
|
||||||
|
m.d.sync += pixrow.eq(0)
|
||||||
|
|
||||||
|
with m.If(counter[0:1] == 1):
|
||||||
|
m.d.sync += [
|
||||||
|
pixrow.eq(1),
|
||||||
|
self.display_out.rgb0.eq(ram_rgb_slice),
|
||||||
|
]
|
||||||
|
with m.If(counter[0:1] == 2):
|
||||||
|
m.d.sync += [
|
||||||
|
pixnum.eq(pixnum - 1),
|
||||||
|
pixrow.eq(0),
|
||||||
|
self.display_out.rgb1.eq(ram_rgb_slice),
|
||||||
|
]
|
||||||
|
with m.If(counter == 128 * 2 + 1):
|
||||||
|
|
||||||
|
m.next = "done"
|
||||||
|
with m.State("done"):
|
||||||
|
m.d.sync += self.done.eq(1)
|
||||||
|
m.next = "init"
|
||||||
|
|
||||||
return m
|
return m
|
||||||
|
|
||||||
|
|
||||||
def test_stringdriver():
|
|
||||||
dut = Hub75StringDriver()
|
|
||||||
sim = Simulator(dut)
|
|
||||||
sim.add_clock(1e-6)
|
|
||||||
|
|
||||||
async def testbench(ctx):
|
|
||||||
pass
|
|
||||||
|
|
||||||
sim.add_testbench(testbench)
|
|
||||||
with sim.write_vcd("output.vcd"):
|
|
||||||
sim.run_until(1e-6 * 1000)
|
|
||||||
|
|
||||||
|
|
||||||
class Hub75Coordinator(wiring.Component):
|
class Hub75Coordinator(wiring.Component):
|
||||||
"""A shared-control hub75 driver"""
|
"""A shared-control hub75 driver"""
|
||||||
|
|
||||||
|
|
37
src/groovylight/test_hub75.py
Normal file
37
src/groovylight/test_hub75.py
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
from amaranth import Array, Module, Cat, Signal, Assert, unsigned
|
||||||
|
from amaranth.build import Platform
|
||||||
|
from amaranth.lib import wiring, data
|
||||||
|
from amaranth.lib.wiring import In, Out
|
||||||
|
from amaranth.lib.memory import Memory, WritePort
|
||||||
|
from amaranth.sim import Simulator
|
||||||
|
|
||||||
|
from .bitslicer import Hub75StringDriver, Rgb888Layout
|
||||||
|
|
||||||
|
|
||||||
|
def test_stringdriver():
|
||||||
|
# the string driver test must
|
||||||
|
# 1. finish
|
||||||
|
# 2. strobe through all of the data in the array
|
||||||
|
# 3. slice the correct bit from the data.
|
||||||
|
m = Module()
|
||||||
|
m.submodules.dut = dut = Hub75StringDriver()
|
||||||
|
m.submodules.mem = mem = Memory(shape=Rgb888Layout, depth=512, init=[])
|
||||||
|
port = mem.read_port()
|
||||||
|
|
||||||
|
wiring.connect(m, port, dut.bram_port)
|
||||||
|
|
||||||
|
async def testbench(ctx):
|
||||||
|
# select a bit, strobe start, read values, test against known.
|
||||||
|
ctx.set(dut.bcm_select, 5)
|
||||||
|
ctx.set(dut.start, 1)
|
||||||
|
await ctx.tick()
|
||||||
|
ctx.set(dut.start, 0)
|
||||||
|
assert(ctx.get(dut.bram_port.en) == 1)
|
||||||
|
pass
|
||||||
|
|
||||||
|
sim = Simulator(m)
|
||||||
|
sim.add_clock(1e-6)
|
||||||
|
|
||||||
|
with sim.write_vcd("output.vcd"):
|
||||||
|
|
||||||
|
sim.run_until(1e-6 * 1000)
|
|
@ -27,7 +27,8 @@ def test_swapbuffer():
|
||||||
ctx.set(dut.selector, 1)
|
ctx.set(dut.selector, 1)
|
||||||
await ctx.tick().repeat(2) # takes two clocks after switching selector to output data.
|
await ctx.tick().repeat(2) # takes two clocks after switching selector to output data.
|
||||||
assert ctx.get(dut.read_port.data) == test_color
|
assert ctx.get(dut.read_port.data) == test_color
|
||||||
|
|
||||||
|
# TODO: add more assertions/verification
|
||||||
sim.add_testbench(testbench)
|
sim.add_testbench(testbench)
|
||||||
with sim.write_vcd("output.vcd"):
|
with sim.write_vcd("output.vcd"):
|
||||||
sim.run_until(1e-6 * 1000)
|
sim.run_until(1e-6 * 1000)
|
Loading…
Reference in a new issue