generated from saji/ecp5-template
add wip sdram controller, start multistring hub75
Some checks failed
Verilator Unit Tests / Test (push) Failing after 3m28s
Some checks failed
Verilator Unit Tests / Test (push) Failing after 3m28s
This commit is contained in:
parent
bf35262640
commit
d42e227c4d
|
@ -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
70
src/groovylight/sdram.py
Normal 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
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue