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