storage: add run length encoder
[litex.git] / miscope / storage.py
1 from migen.fhdl.std import *
2 from migen.flow.actor import *
3 from migen.flow.network import *
4 from migen.fhdl.specials import Memory
5 from migen.bus import csr
6 from migen.bank import description, csrgen
7 from migen.bank.description import *
8 from migen.actorlib.fifo import SyncFIFO
9
10 class RunLenghEncoder(Module, AutoCSR):
11 def __init__(self, width, length):
12 self.width = width
13 self.length = length
14
15 self.sink = Sink([("d", width)])
16 self.source = Source([("d", width)])
17
18 self._r_enable = CSRStorage()
19
20 ###
21 enable = self._r_enable.storage
22 stb_i = self.sink.stb
23 dat_i = self.sink.payload.d
24 ack_i = self.sink.ack
25
26 # Register Input
27 stb_i_d = Signal()
28 dat_i_d = Signal(width)
29
30 self.sync += [
31 dat_i_d.eq(dat_i),
32 stb_i_d.eq(stb_i)
33 ]
34
35 # Detect change
36 change = Signal()
37 comb = [diff.eq(stb_i & (~enable | (dat_i_d != dat_i)))]
38
39 change_d = Signal()
40 change_rising = Signal()
41 self.sync += change_d.eq(change)
42 self.comb += change_rising.eq(change & ~change_d)
43
44 # Generate RLE word
45 rle_cnt = Signal(max=length)
46 rle_max = Signal()
47
48 comb +=[If(rle_cnt == length, rle_max.eq(enable))]
49
50 sync +=[
51 If(change | rle_max,
52 rle_cnt.eq(0)
53 ).Else(
54 rle_cnt.eq(rle_cnt + 1)
55 )
56 ]
57
58 # Mux RLE word and data
59 stb_o = self.source.stb
60 dat_o = self.source.payload.d
61 ack_o = self.source.ack
62
63 comb +=[
64 If(change_rising & ~rle_max,
65 stb_o.eq(1),
66 dat_o[width-1].eq(1),
67 dat_o[:flen(rle_cnt)].eq(rle_cnt)
68 ).Elif(change_d | rle_max,
69 stb_o.eq(stb_i_d),
70 dat_o.eq(dat_i_d)
71 ).Else(
72 stb_o.eq(0),
73 ),
74 ack_i.eq(1) #FIXME
75 ]
76
77
78
79 class Recorder(Module, AutoCSR):
80 def __init__(self, width, depth):
81 self.width = width
82
83 self.sink = Sink([("hit", 1), ("d", width)])
84
85 self._r_trigger = CSR()
86 self._r_length = CSRStorage(bits_for(depth))
87 self._r_offset = CSRStorage(bits_for(depth))
88 self._r_done = CSRStatus()
89
90 self._r_read_en = CSR()
91 self._r_read_empty = CSRStatus()
92 self._r_read_dat = CSRStatus(width)
93
94 ###
95
96 length = self._r_length.storage
97 offset = self._r_offset.storage
98 done = Signal(reset=1)
99 ongoing = Signal()
100
101 cnt = Signal(max=depth)
102
103 fifo = SyncFIFO([("d", width)], depth)
104 self.submodules += fifo
105
106 # Write fifo is done only when done = 0
107 # Fifo must always be pulled by software between
108 # acquisition (Todo: add a flush funtionnality)
109 self.comb +=[
110 fifo.sink.stb.eq(self.sink.stb & ~done),
111 fifo.sink.payload.d.eq(self.sink.payload.d),
112 self.sink.ack.eq(1)
113 ]
114
115 # Done, Ongoing:
116 # 0, 0 : Storage triggered but hit was not yet seen
117 # Data are recorded to fifo, if "offset" datas
118 # in the fifo, ack is set on fifo.source to
119 # store only "offset" datas.
120 #
121 # 0, 1 : Hit was seen, ack is no longer set on fifo.source
122 # we are storing "length"-"offset" data in this
123 # phase
124 #
125 # 1, 0 : We have stored "length" datas in fifo. Write to
126 # fifo is disabled.
127 # Software must now read data from the fifo until
128 # it is empty
129
130 # done & ongoing
131 self.sync += [
132 If(self._r_trigger.re & self._r_trigger.r, done.eq(0)
133 ).Elif(cnt==length, done.eq(1)),
134
135 If(self.sink.stb & self.sink.payload.hit & ~done, ongoing.eq(1)
136 ).Elif(done, ongoing.eq(0)),
137 ]
138
139 # fifo ack & csr connection
140 self.comb += [
141 If(~done & ~ongoing & (cnt >= offset), fifo.source.ack.eq(1)
142 ).Else(fifo.source.ack.eq(self._r_read_en.re & self._r_read_en.r)),
143 self._r_read_empty.status.eq(~fifo.source.stb),
144 self._r_read_dat.status.eq(fifo.source.payload.d),
145 self._r_done.status.eq(done)
146 ]
147
148 # cnt
149 self.sync += [
150 If(done == 1,
151 cnt.eq(0)
152 ).Elif(fifo.sink.stb & fifo.sink.ack & ~(fifo.source.stb & fifo.source.ack),
153 cnt.eq(cnt+1),
154 )
155 ]