Skip to content

Commit

Permalink
wip: entire script of bee movie
Browse files Browse the repository at this point in the history
  • Loading branch information
whitequark committed Mar 28, 2024
1 parent 635ae2b commit 334cc33
Show file tree
Hide file tree
Showing 2 changed files with 4,231 additions and 170 deletions.
113 changes: 63 additions & 50 deletions software/glasgow/gateware/hyperram.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ def elaborate(self, platform):


class PHYMode(enum.Enum, shape=2):
Select = 0 # decodes CS# from `i.p.data`; if `i.p.data != 0`, samples RWDS and writes to `o.p.data`
Select = 0 # decodes CS# from `i.p.data`;
# if `i.p.data != 0`, samples RWDS and writes to `o.p.data`
CmdAddr = 1 # drives DQ from `i.p.data`
Write = 2 # drives DQ from `i.p.data` and RWDS from `i.p.mask`
Read = 3 # drives `o.p.data` from DQ when RWDS toggles
Expand All @@ -90,10 +91,12 @@ class PHYx1(wiring.Component):
"o": Out(StreamSignature(data.StructLayout({
"mode": PHYMode,
"data": 16,
"mask": 2
"mask": 2,
"last": 1
}))),
"i": In(StreamSignature(data.StructLayout({
"data": 16
"data": 16,
"last": 1 # o.last looped back
})))
})

Expand Down Expand Up @@ -260,11 +263,13 @@ def elaborate(self, platform):
pins_rwds_i_regl.eq(pins_rwds.i),
]
m.d.comb += [
self.i.p.last.eq(o_p_reg.last),
self.i.p.data.eq(Cat(pins_dq.i, pins_dq_i_regh)),
self.i.valid.eq( Cat(pins_rwds.i, pins_rwds_i_regh) == 0b10),
]
with m.Else():
m.d.comb += [
self.i.p.last.eq(o_p_reg.last),
self.i.p.data.eq(Cat(pins_dq_i_regl, pins_dq_i_regh)),
self.i.valid.eq( Cat(pins_rwds_i_regl, pins_rwds_i_regh) == 0b10),
]
Expand All @@ -290,7 +295,7 @@ def elaborate(self, platform):
pins.ck_p.o1.eq(0),
]

return m
return ResetInserter(self.rst)(m)


class Operation(enum.Enum, shape=1):
Expand Down Expand Up @@ -330,18 +335,19 @@ def __init__(self, phy):
self.phy = phy

super().__init__(Signature({
"control" : In(StreamSignature(data.StructLayout({
"select" : range(self.phy.cs_count + 1),
"cmd_addr" : CommandAddress,
"latency" : range(3, 17),
# "length" : range(TODO)
"ctl": In(StreamSignature(data.StructLayout({
"select" : range(self.phy.cs_count + 1),
"cmd_addr" : CommandAddress,
"latency" : range(0, 16 + 1)
}))),
"o": In(StreamSignature(data.StructLayout({
"data" : 16,
"mask" : 2,
"last" : 1
}))),
"write" : In(StreamSignature(data.StructLayout({
"data" : 16,
"en" : 2,
}), reset={"en": 0b11})),
"read" : Out(StreamSignature(data.StructLayout({
"data" : 16,
"i": Out(StreamSignature(data.StructLayout({
"data" : 16,
"last" : 1
})))
}))

Expand All @@ -352,39 +358,33 @@ def elaborate(self, platform):
# The cycle counter performs double duty: it is used to time the Command/Address phase as
# well as the Latency phase after it. The counting of latency cycles starts from the third
# cycle of the Command/Address phase, so the counter starts at -1 to simplify comparisons.
cycle = Signal(range(-1, 33), reset=-1)
cycle = Signal(range(-1, 16 << 1 + 1), reset=-1)

# The latency must be doubled if RWDS is high after CS# assertion.
latency = Signal.like(self.control.p.latency)
latency = Signal.like(self.ctl.p.latency)

# The command/address word is latched and shifted.
cmd_addr = Signal(48)

leds_o = Cat(platform.request("led", i).o for i in range(5))
leds = Signal(5, reset_less=True)
m.d.comb += leds_o.eq(leds)

with m.FSM():
with m.State("Select"):
m.d.comb += [
phy.o.p.mode.eq(PHYMode.Select),
phy.o.p.data.eq(self.control.p.select),
phy.o.valid.eq(self.control.valid),
phy.o.p.data.eq(self.ctl.p.select),
phy.o.valid.eq(self.ctl.valid),
]
m.d.sync += [
cycle.eq(cycle.reset),
latency.eq(self.control.p.latency),
cmd_addr.eq(self.control.p.cmd_addr),
latency.eq(self.ctl.p.latency),
cmd_addr.eq(self.ctl.p.cmd_addr),
]
with m.If(phy.o.ready & self.control.valid):
with m.If(phy.o.ready & self.ctl.valid):
m.next = "Latency"

with m.State("Latency"):
m.d.comb += phy.i.ready.eq(1)
with m.If(phy.i.valid):
# If RWDS is asserted by the memory when it's selected, latency is doubled.
with m.If(self.control.p.cmd_addr.operation == Operation.Write):
m.d.sync += leds.eq(Cat(phy.i.p.data[0], leds))
m.d.sync += latency.eq(Mux(phy.i.p.data[0], latency << 1, latency))
m.next = "Command/Address"

Expand All @@ -400,19 +400,26 @@ def elaborate(self, platform):
cmd_addr.eq(Cat(C(0, 16), cmd_addr)),
]
with m.If(cycle == 1):
with m.If(self.control.p.cmd_addr.operation == Operation.Write):
with m.If(latency != 0):
m.next = "Latency-Write"
with m.Else():
m.d.comb += self.control.ready.eq(1)
m.next = "Write"
with m.Else():
# Although reads also have initial latency, it is not necessary to know
# it in advance because toggling of RWDS unambiguously delimits data.
# In addition it is also often not possible to know it in advance
# because a read from the register with the initial latency value has
# the same initial latency as any other read.
m.d.comb += self.ctl.ready.eq(1) # Done processing the command.
with m.If(self.ctl.p.cmd_addr.operation == Operation.Read):
# Although reads have initial latency, it is not necessary to know it
# in advance because toggling of RWDS unambiguously delimits data.
# In addition, it is also not possible to know it in advance for reads
# from the register space because the default value for the initial
# latency value is not fixed, and there is no way to find it out other
# than by reading CR0.
m.next = "Latency-Read"
with m.Elif(self.ctl.p.cmd_addr.address_space == AddressSpace.Memory):
# Memory space writes generally have non-zero latency. Although
# the HyperBus specification allows memory space writes with zero
# latency, this is not generally used.
m.next = "Latency-Write"
with m.Else():
# Register space writes generally have zero latency. Although
# the HyperBus specification allows register space writes with non-zero
# latency, this is not generally used, and writes to CR0 must have zero
# latency or it would not be possible to set the initial latency value.
m.next = "Write"

with m.State("Latency-Write"):
m.d.comb += [
Expand All @@ -427,20 +434,19 @@ def elaborate(self, platform):
with m.If(phy.o.ready):
m.d.sync += cycle.eq(cycle + 1)
with m.If(cycle == latency):
m.d.comb += self.control.ready.eq(1)
m.next = "Write"

with m.State("Write"):
m.d.comb += [
# Drive RWDS only if there was a turnaround time; do not drive it during
# a zero-latency write.
phy.o.p.mode.eq(Mux(latency != 0, PHYMode.Write, PHYMode.CmdAddr)),
phy.o.p.data.eq(self.write.p.data),
phy.o.p.mask.eq(~self.write.p.en),
phy.o.valid.eq(1),
self.write.ready.eq(phy.o.ready),
phy.o.p.data.eq(self.o.p.data),
phy.o.p.mask.eq(self.o.p.mask),
phy.o.valid.eq(self.o.valid),
self.o.ready.eq(phy.o.ready),
]
with m.If(self.control.valid):
with m.If(self.o.valid & self.o.ready & self.o.p.last):
m.next = "Deselect"

with m.State("Latency-Read"):
Expand All @@ -456,16 +462,23 @@ def elaborate(self, platform):
# initial latency, there will be a 'high, low' transition at the beginning
# that does not indicate a data word being read.
with m.If(cycle == 3):
m.d.comb += self.control.ready.eq(1)
m.next = "Read"

with m.State("Read"):
# `last` added and removed here
m.d.comb += [
phy.o.p.mode.eq(PHYMode.Read),
phy.o.valid.eq(1),
phy.o.p.last.eq(self.o.p.last),
phy.o.valid.eq(self.o.valid),
self.o.ready.eq(phy.o.ready),
]
m.d.comb += [
self.i.p.data.eq(phy.i.p.data),
self.i.p.last.eq(phy.i.p.last),
self.i.valid.eq(phy.i.valid),
phy.i.ready.eq(self.i.ready),
]
wiring.connect(m, wiring.flipped(self.read), phy.i)
with m.If(self.control.valid):
with m.If(self.i.valid & self.i.ready & self.i.p.last):
m.next = "Deselect"

with m.State("Deselect"):
Expand Down
Loading

0 comments on commit 334cc33

Please sign in to comment.