add wip sdram controller, start multistring hub75
Some checks failed
Verilator Unit Tests / Test (push) Failing after 3m28s

This commit is contained in:
saji 2024-08-23 20:06:29 -05:00
parent bf35262640
commit d42e227c4d
2 changed files with 155 additions and 18 deletions

View file

@ -1,7 +1,8 @@
from amaranth import Module, Cat, Signal, Assert, unsigned from amaranth import Array, Module, Cat, Signal, Assert, unsigned
from amaranth.build import Platform from amaranth.build import Platform
from amaranth.lib import wiring, data from amaranth.lib import wiring, data
from amaranth.lib.wiring import In, Out from amaranth.lib.wiring import In, Out
from amaranth.lib.memory import Memory, WritePort
class RGBLayout(data.StructLayout): class RGBLayout(data.StructLayout):
@ -55,9 +56,10 @@ class Hub75Ctrl(wiring.Signature):
class Hub75Data(wiring.Signature): class Hub75Data(wiring.Signature):
""" Data lines for HUB75 displays. When combined with Hub75Ctrl, this forms a complete HUB75 interface. """Data lines for HUB75 displays. When combined with Hub75Ctrl, this forms a complete HUB75 interface.
These are kept separate as some devices have a single set of control signals. These are kept separate as some devices have a single set of control signals.
""" """
def __init__(self): def __init__(self):
super().__init__( super().__init__(
{ {
@ -65,31 +67,91 @@ class Hub75Data(wiring.Signature):
"rgb1": Out(rgb111_hub75), "rgb1": Out(rgb111_hub75),
} }
) )
def get_lineram() -> Memory:
return Memory(shape=rgb888_layout, depth=512, init=[])
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"""
"""
bcm_select: In(3) bcm_select: In(3)
class Hub75Coordinator(wiring.Component):
""" A shared-control hub75 driver"""
pass
class Bitslicer(wiring.Component):
start_write: In(1)
done: Out(1) done: Out(1)
bitplane_addr: Out(11) start: In(1)
bitplane_wren: Out(1) active_bank: In(1) # the bank to use
bitplane_data: Out(6) bram_port: WritePort.Signature(addr_width=9, shape=rgb888_layout)
out: Out(Hub75Data())
def elaborate(self, platform: Platform) -> Module: def elaborate(self, platform: Platform) -> Module:
m = Module() m = Module()
bitplane_bit = Signal(3)
# add two memories
m.submodules.bram0 = bram0 = Memory(shape=rgb888_layout, depth=512, init=[])
m.submodules.bram1 = bram1 = Memory(shape=rgb888_layout, depth=512, init=[])
# We use two brams here so we can swap between each - that way we can update the data
# while displaying the other line. Switching is controlled by the coordinator.
readports = Array([bram0.read_port(), bram1.read_port()])
active_readport = readports[self.active_bank]
m.d.comb += self.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])
counter = Signal(32)
m.d.sync += counter.eq(counter + 1)
ram_rgb_slice = Cat(
active_readport.data['red'].bit_select(self.bcm_shift, 1),
active_readport.data['blue'].bit_select(self.bcm_shift, 1),
active_readport.data['green'].bit_select(self.bcm_shift, 1),
)
pixnum = Signal(8, reset=127)
pixrow = Signal(1, reset=0)
m.d.comb += self.buf_addr.eq(Cat(pixrow, pixnum))
with m.FSM(): with m.FSM():
with m.State("init"): with m.State("init"):
m.d.sync += bitplane_bit.eq(0) m.d.sync += [
m.d.sync += self.done.eq(0) self.done.eq(0),
counter.eq(0),
pixnum.eq(127),
pixrow.eq(0),
]
with m.If(self.start == 1):
m.d.sync += active_readport.en.eq(1)
m.next = "prefetch"
with m.State("prefetch"):
with m.If(counter == 0):
m.d.sync += pixrow.eq(0)
with m.Elif(counter == 1):
m.d.sync += pixrow.eq(1)
with m.Elif(counter == 2):
m.d.sync += self.out.rgb0.eq(ram_rgb_slice)
with m.Elif(counter == 3):
m.d.sync += [self.out.rgb1.eq(ram_rgb_slice), counter.eq(0)]
m.next = "writerow"
return m
class Hub75Coordinator(wiring.Component):
"""A shared-control hub75 driver"""
def __init__(self, n_strings=1):
self.n_strings = n_strings
super().__init__()
def elaborate(self, platform: Platform) -> Module:
m = Module()
return m return m
@ -208,3 +270,8 @@ class Hub75EDriver(wiring.Component):
m.next = "init" m.next = "init"
return m return m
if __name__ == "__main__":
m = Hub75EDriver()
from amaranth.cli import main
main(m)

70
src/groovylight/sdram.py Normal file
View file

@ -0,0 +1,70 @@
# SDRAM controller for the Colorlight 5a-75b
from amaranth import Module, Cat, Signal, Assert, unsigned
from amaranth.build import Platform
from amaranth.lib import wiring, data, enum
from amaranth.lib.wiring import In, Out
# RAM Specs:
# 4 banks, 2048 rows, 256 columns.
# 11 row bits, 8 column bits.
# word size = 32
# 8 megabytes data.
sdram_control_layout = data.StructLayout(
{
"nCS": 1,
"nRAS": 1,
"nCAS": 1,
"nWE": 1,
}
)
class _WriteBurstLength(enum.Enum, shape=1):
"""MRS field"""
BURST = 0
SINGLE_BIT = 1
class _TestMode(enum.Enum, shape=2):
""" The "test mode" of the sdram. This is always zero pretty much"""
MODE_REGISTER_SET = 0
RESERVED0 = 1
RESERVED1 = 2
RESERVED2 = 3
class _CASLatency(enum.Enum, shape=3):
""" How many cycles of latency for the column address select to complete """
CYCL2 = 2
CYCL3 = 3
class _BurstType(enum.Enum, shape=1):
SEQUENTIAL = 0
INTERLEAVED = 1
class _BurstLength(enum.IntEnum, shape=3):
""" The size of the burst """
SINGLE = 0
DUAL = 1
QUAD = 2
OCT = 3
FULL_PAGE = 7
class _Command(enum.Enum):
""" Command set for SDRAM """
MRS_WRITE = 0
ACTIVATE = 1
PRECHARGE = 2
WRITE = 3
READ = 4
CBR = 5 # auto refresh
SELF_REFRESH = 6
BRST_STOP = 7
NOP = 8