forked from M-Labs/artiq-zynq
frameline GW: add gearbox docs
This commit is contained in:
parent
d53a393f0d
commit
13b68b1e7d
@ -411,170 +411,159 @@ class Custom_Pixel_Gearbox(Module):
|
|||||||
|
|
||||||
# # #
|
# # #
|
||||||
|
|
||||||
shift_reg_size = lcm(sink_dw, source_dw)
|
ring_buf_size = lcm(sink_dw, source_dw)
|
||||||
# ensure the shift register is at least twice the size of sink/source dw
|
# ensure the shift register is at least twice the size of sink/source dw
|
||||||
if (shift_reg_size//sink_dw) < 2:
|
if (ring_buf_size//sink_dw) < 2:
|
||||||
shift_reg_size = shift_reg_size * 2
|
ring_buf_size = ring_buf_size * 2
|
||||||
if (shift_reg_size//source_dw) < 2:
|
if (ring_buf_size//source_dw) < 2:
|
||||||
shift_reg_size = shift_reg_size * 2
|
ring_buf_size = ring_buf_size * 2
|
||||||
|
|
||||||
# Control interface
|
# Control interface
|
||||||
|
|
||||||
reset_reg = Signal()
|
reset_reg = Signal()
|
||||||
we = Signal()
|
we = Signal()
|
||||||
re = Signal()
|
re = Signal()
|
||||||
self.level = Signal(max=shift_reg_size)
|
level = Signal(max=ring_buf_size)
|
||||||
self.w_cnt = Signal(max=shift_reg_size//sink_dw)
|
w_cnt = Signal(max=ring_buf_size//sink_dw)
|
||||||
self.r_cnt = Signal(max=shift_reg_size//source_dw)
|
r_cnt = Signal(max=ring_buf_size//source_dw)
|
||||||
|
|
||||||
self.sync += [
|
self.sync += [
|
||||||
If(reset_reg,
|
If(reset_reg,
|
||||||
self.level.eq(self.level.reset),
|
level.eq(level.reset),
|
||||||
).Else(
|
).Else(
|
||||||
If(we & ~re, self.level.eq(self.level + sink_dw)),
|
If(we & ~re, level.eq(level + sink_dw)),
|
||||||
If(~we & re, self.level.eq(self.level - source_dw)),
|
If(~we & re, level.eq(level - source_dw)),
|
||||||
If(we & re, self.level.eq(self.level + sink_dw - source_dw)),
|
If(we & re, level.eq(level + sink_dw - source_dw)),
|
||||||
),
|
),
|
||||||
|
|
||||||
If(reset_reg,
|
If(reset_reg,
|
||||||
self.w_cnt.eq(self.w_cnt.reset),
|
w_cnt.eq(w_cnt.reset),
|
||||||
self.r_cnt.eq(self.r_cnt.reset),
|
r_cnt.eq(r_cnt.reset),
|
||||||
).Else(
|
).Else(
|
||||||
If(we,
|
If(we,
|
||||||
If(self.w_cnt == ((shift_reg_size//sink_dw) - 1),
|
If(w_cnt == ((ring_buf_size//sink_dw) - 1),
|
||||||
self.w_cnt.eq(self.w_cnt.reset),
|
w_cnt.eq(w_cnt.reset),
|
||||||
).Else(
|
).Else(
|
||||||
self.w_cnt.eq(self.w_cnt + 1),
|
w_cnt.eq(w_cnt + 1),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
If(re,
|
If(re,
|
||||||
If(self.r_cnt == ((shift_reg_size//source_dw) - 1),
|
If(r_cnt == ((ring_buf_size//source_dw) - 1),
|
||||||
self.r_cnt.eq(self.r_cnt.reset),
|
r_cnt.eq(r_cnt.reset),
|
||||||
).Else(
|
).Else(
|
||||||
self.r_cnt.eq(self.r_cnt + 1),
|
r_cnt.eq(r_cnt + 1),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
# FIXME: handle linebreak properly for all bits
|
extra_eol_handling = size in [10, 12, 14]
|
||||||
# 1) the COPY may or may not stb data out
|
if extra_eol_handling:
|
||||||
# 2) the NEWLINE may stb too much
|
# the source need to be stb twice
|
||||||
# FIXME: the fsm need to be intellgent enough to know whether it need to stb twice or once at eop
|
# (one for level >= source_dw and the other for the remaining pixels)
|
||||||
# need a better generic solution
|
# when last word of each line packet satisfied the following condition:
|
||||||
# FIXME: handle last line when the word also are aligned to 4x pixel
|
#
|
||||||
|
# if there exist an integers j such that
|
||||||
# FACT:
|
# sink_dw * i > size * j > source_dw * k
|
||||||
# - self.level is only useful for normal ops (i.e. non-linebreak)
|
# where i,k are postive integers and source_dw * k - sink_dw * (i-1) > 0
|
||||||
# - it should not be used for last pixels
|
#
|
||||||
# - extra_stb should be reserved for non aligned data only
|
# For example size == 10
|
||||||
# - after last word, fsm may need to stb 0-2 times
|
# 32 * 2 > 10 * (5) > 40 * 1
|
||||||
# - which are determined by x_size (varies) & pix size (constant)
|
# 32 * 2 > 10 * (6) > 40 * 1
|
||||||
# - self.valid should be used instead of extra stb when aligned
|
# 32 * 3 > 10 * (9) > 40 * 2
|
||||||
|
stb_aligned = Signal()
|
||||||
extra_stb = Signal()
|
match size:
|
||||||
match size:
|
case 10:
|
||||||
case 8 | 16:
|
stb_cases = {
|
||||||
# no need for extra stb as they are aligned to word boundary no matter what
|
5: stb_aligned.eq(1),
|
||||||
pass
|
6: stb_aligned.eq(1),
|
||||||
case 10:
|
9: stb_aligned.eq(1),
|
||||||
stb_cases = {
|
}
|
||||||
5: extra_stb.eq(1),
|
self.sync += Case(self.x_size[:4], stb_cases) # mod 16
|
||||||
6: extra_stb.eq(1),
|
case 12:
|
||||||
9: extra_stb.eq(1),
|
stb_cases = {
|
||||||
}
|
5: stb_aligned.eq(1),
|
||||||
# mod 16
|
}
|
||||||
self.sync += Case(self.x_size[:4], stb_cases)
|
self.sync += Case(self.x_size[:3], stb_cases) # mod 8
|
||||||
case 12:
|
case 14:
|
||||||
stb_cases = {
|
stb_cases = {
|
||||||
5: extra_stb.eq(1),
|
9: stb_aligned.eq(1),
|
||||||
}
|
13: stb_aligned.eq(1),
|
||||||
# mod 8
|
}
|
||||||
self.sync += Case(self.x_size[:3], stb_cases)
|
self.sync += Case(self.x_size[:4], stb_cases) # mod 16
|
||||||
case 14:
|
|
||||||
stb_cases = {
|
|
||||||
9: extra_stb.eq(1),
|
|
||||||
13: extra_stb.eq(1),
|
|
||||||
}
|
|
||||||
# mod 16
|
|
||||||
self.sync += Case(self.x_size[:4], stb_cases)
|
|
||||||
|
|
||||||
|
|
||||||
# NOTE:
|
|
||||||
# access to levels -> know when 4x pixels are
|
|
||||||
# access to x_size -> know which pixel at last stb are valid
|
|
||||||
# access to eop -> know when end of line ares
|
|
||||||
|
|
||||||
self.submodules.fsm = fsm = FSM(reset_state="COPY_ALIGNED")
|
|
||||||
|
|
||||||
|
self.submodules.fsm = fsm = FSM(reset_state="SHIFTING")
|
||||||
fsm.act(
|
fsm.act(
|
||||||
"COPY_ALIGNED",
|
"SHIFTING",
|
||||||
self.sink.ack.eq(1),
|
self.sink.ack.eq(1),
|
||||||
self.source.stb.eq(self.level >= source_dw),
|
self.source.stb.eq(level >= source_dw),
|
||||||
we.eq(self.sink.stb),
|
we.eq(self.sink.stb),
|
||||||
re.eq((self.source.stb & self.source.ack)),
|
re.eq((self.source.stb & self.source.ack)),
|
||||||
If(self.sink.stb & self.sink.eop,
|
If(self.sink.stb & self.sink.eop,
|
||||||
If(extra_stb,
|
(If(stb_aligned,
|
||||||
NextState("EXTRA_STB"),
|
NextState("MOVE_ALIGNED_PIX"),
|
||||||
).Else(
|
).Else(
|
||||||
NextState("NEWLINE"),
|
NextState("MOVE_REMAINING_PIX"),
|
||||||
|
) if extra_eol_handling else
|
||||||
|
NextState("MOVE_REMAINING_PIX"),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
fsm.act(
|
if extra_eol_handling:
|
||||||
"EXTRA_STB",
|
fsm.act(
|
||||||
self.source.stb.eq(self.level >= source_dw),
|
"MOVE_ALIGNED_PIX",
|
||||||
re.eq((self.source.stb & self.source.ack)),
|
self.source.stb.eq(1),
|
||||||
NextState("NEWLINE"),
|
re.eq((self.source.stb & self.source.ack)),
|
||||||
)
|
NextState("MOVE_REMAINING_PIX"),
|
||||||
|
)
|
||||||
|
|
||||||
# determine to stb 1 or 2 times
|
stb_valid = Signal()
|
||||||
# only the last stb will use valid_stb
|
|
||||||
valid_stb = Signal()
|
|
||||||
fsm.act(
|
fsm.act(
|
||||||
"NEWLINE",
|
"MOVE_REMAINING_PIX",
|
||||||
self.source.stb.eq(1),
|
|
||||||
reset_reg.eq(1),
|
reset_reg.eq(1),
|
||||||
valid_stb.eq(1),
|
self.source.stb.eq(1),
|
||||||
NextState("COPY_ALIGNED"),
|
stb_valid.eq(1),
|
||||||
|
NextState("SHIFTING"),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Data path
|
# Data path
|
||||||
|
|
||||||
self.shift_register = Signal(shift_reg_size, reset_less=True)
|
ring_buf = Signal(ring_buf_size, reset_less=True)
|
||||||
|
|
||||||
sink_cases = {}
|
sink_cases = {}
|
||||||
for i in range(shift_reg_size//sink_dw):
|
for i in range(ring_buf_size//sink_dw):
|
||||||
sink_cases[i] = [
|
sink_cases[i] = [
|
||||||
self.shift_register[sink_dw*i:sink_dw*(i+1)].eq(self.sink.data),
|
ring_buf[sink_dw*i:sink_dw*(i+1)].eq(self.sink.data),
|
||||||
]
|
]
|
||||||
self.sync += If(self.sink.stb, Case(self.w_cnt, sink_cases))
|
self.sync += If(self.sink.stb, Case(w_cnt, sink_cases))
|
||||||
|
|
||||||
source_cases = {}
|
source_cases = {}
|
||||||
for i in range(shift_reg_size//source_dw):
|
for i in range(ring_buf_size//source_dw):
|
||||||
source_cases[i] = []
|
source_cases[i] = []
|
||||||
for j in range(4):
|
for j in range(4):
|
||||||
source_cases[i].append(
|
source_cases[i].append(
|
||||||
self.source.data[pixel_width * j : pixel_width * (j + 1)].eq(
|
self.source.data[pixel_width * j : pixel_width * (j + 1)].eq(
|
||||||
self.shift_register[(source_dw * i) + (size * j) : (source_dw * i) + (size * (j + 1))]
|
ring_buf[(source_dw * i) + (size * j) : (source_dw * i) + (size * (j + 1))]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# precalcule which last pixels are valid
|
# calcule which last pixels are valid
|
||||||
self.valid = Signal(4)
|
valid = Signal(4)
|
||||||
bit_cases = {
|
bit_cases = {
|
||||||
0: self.valid.eq(0b1111),
|
0: valid.eq(0b1111),
|
||||||
1: self.valid.eq(0b0001),
|
1: valid.eq(0b0001),
|
||||||
2: self.valid.eq(0b0011),
|
2: valid.eq(0b0011),
|
||||||
3: self.valid.eq(0b0111),
|
3: valid.eq(0b0111),
|
||||||
}
|
}
|
||||||
self.sync += Case(self.x_size[:2], bit_cases)
|
self.sync += Case(self.x_size[:2], bit_cases)
|
||||||
|
|
||||||
self.comb += [
|
self.comb += [
|
||||||
Case(self.r_cnt, source_cases),
|
Case(r_cnt, source_cases),
|
||||||
If(valid_stb,
|
If(stb_valid,
|
||||||
self.source.valid.eq(self.valid),
|
self.source.valid.eq(valid),
|
||||||
).Else(
|
).Else(
|
||||||
self.source.valid.eq(0b1111),
|
self.source.valid.eq(0b1111),
|
||||||
),
|
),
|
||||||
@ -592,17 +581,20 @@ class Frame_Deserializer(Module):
|
|||||||
self.submodules.eol_inserter = eol_inserter = End_Of_Line_Inserter()
|
self.submodules.eol_inserter = eol_inserter = End_Of_Line_Inserter()
|
||||||
self.sync += eol_inserter.l_size.eq(self.l_size),
|
self.sync += eol_inserter.l_size.eq(self.l_size),
|
||||||
|
|
||||||
|
for i in [8, 10, 12, 14, 16]:
|
||||||
|
gearbox = Custom_Pixel_Gearbox(i)
|
||||||
|
self.submodules += gearbox
|
||||||
|
self.sync += gearbox.x_size.eq(self.x_size),
|
||||||
|
self.comb += eol_inserter.source.connect(gearbox.sink)
|
||||||
|
self.comb += gearbox.source.ack.eq(1) # simulated a proper consumer, idk why but without this it will destory timing
|
||||||
|
|
||||||
# TODO: fix the edge case where 5 x_size is not working
|
# self.submodules.gearbox = gearbox = Custom_Pixel_Gearbox(8)
|
||||||
# the issue arises when the 4th pixel is sent with eop (e.g. 10bits & x_size = 5,6)
|
# self.sync += gearbox.x_size.eq(self.x_size),
|
||||||
# eating the rest of the bits
|
# self.comb += eol_inserter.source.connect(gearbox.sink)
|
||||||
self.submodules.gearbox = gearbox = Custom_Pixel_Gearbox(14)
|
|
||||||
self.sync += gearbox.x_size.eq(self.x_size),
|
|
||||||
|
|
||||||
self.comb += eol_inserter.source.connect(gearbox.sink)
|
|
||||||
|
|
||||||
self.sink = eol_inserter.sink
|
self.sink = eol_inserter.sink
|
||||||
self.source = gearbox.source
|
# self.source = gearbox.source
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -730,8 +722,8 @@ class ROI_Pipeline(Module):
|
|||||||
self.sink = self.pipeline[0].sink
|
self.sink = self.pipeline[0].sink
|
||||||
|
|
||||||
# DEBUG
|
# DEBUG
|
||||||
self.source = self.pipeline[-1].source
|
# self.source = self.pipeline[-1].source
|
||||||
self.comb += self.source.ack.eq(1) # simulated a proper consumer, idk why but without this it will destory timing
|
# self.comb += self.source.ack.eq(1) # simulated a proper consumer, idk why but without this it will destory timing
|
||||||
|
|
||||||
class Frame_Packet_Router(Module):
|
class Frame_Packet_Router(Module):
|
||||||
# packet size expressed in bits
|
# packet size expressed in bits
|
||||||
|
Loading…
Reference in New Issue
Block a user