320 lines
7.5 KiB
Python
320 lines
7.5 KiB
Python
from migen.fhdl.structure import *
|
|
from migen.fhdl.specials import Memory
|
|
from migen.bus import csr
|
|
from migen.bank import description, csrgen
|
|
from migen.bank.description import *
|
|
from migen.genlib.misc import optree
|
|
from migen.genlib.fsm import *
|
|
|
|
from miscope.tools.misc import RisingEdge
|
|
|
|
class Storage:
|
|
#
|
|
# Definition
|
|
#
|
|
def __init__(self, width, depth):
|
|
self.width = width
|
|
self.depth = depth
|
|
self.depth_width = bits_for(self.depth)
|
|
|
|
# Control
|
|
self.rst = Signal()
|
|
self.start = Signal()
|
|
self.offset = Signal(self.depth_width)
|
|
self.size = Signal(self.depth_width)
|
|
self.done = Signal()
|
|
|
|
# Push Path
|
|
self.push_stb = Signal()
|
|
self.push_dat = Signal(self.width)
|
|
self._push_ptr = Signal(self.depth_width)
|
|
self._push_ptr_stop = Signal(self.depth_width)
|
|
|
|
# Pull Path
|
|
self.pull_stb = Signal()
|
|
self.pull_dat = Signal(self.width)
|
|
self._pull_ptr = Signal(self.depth_width)
|
|
|
|
# Memory
|
|
self._mem = Memory(self.width, self.depth)
|
|
self._push_port = self._mem.get_port(write_capable=True)
|
|
self._pull_port = self._mem.get_port(has_re=True)
|
|
|
|
def get_fragment(self):
|
|
comb = [
|
|
self._push_port.adr.eq(self._push_ptr),
|
|
self._push_port.we.eq(self.push_stb),
|
|
self._push_port.dat_w.eq(self.push_dat),
|
|
|
|
self._pull_port.adr.eq(self._pull_ptr),
|
|
self._pull_port.re.eq(self.pull_stb),
|
|
self.pull_dat.eq(self._pull_port.dat_r)
|
|
]
|
|
|
|
# FSM
|
|
fsm = FSM("IDLE", "ACTIVE")
|
|
|
|
# Idle
|
|
fsm.act(fsm.IDLE,
|
|
If(self.start,
|
|
fsm.next_state(fsm.ACTIVE),
|
|
)
|
|
)
|
|
|
|
# Active
|
|
fsm.act(fsm.ACTIVE,
|
|
If(self.done | self.rst,
|
|
fsm.next_state(fsm.IDLE),
|
|
)
|
|
)
|
|
|
|
sync =[
|
|
If(fsm.entering(fsm.ACTIVE),
|
|
self._push_ptr_stop.eq(self._push_ptr + self.size - self.offset),
|
|
self._pull_ptr.eq(self._push_ptr - self.offset - 1)
|
|
).Else(
|
|
If(self.pull_stb, self._pull_ptr.eq(self._pull_ptr + 1))
|
|
),
|
|
If(self.push_stb, self._push_ptr.eq(self._push_ptr + 1)),
|
|
]
|
|
comb +=[self.done.eq((self._push_ptr == self._push_ptr_stop) & fsm.ongoing(fsm.ACTIVE))]
|
|
|
|
return Fragment(comb, sync, specials={self._mem}) + fsm.get_fragment()
|
|
|
|
class RLE:
|
|
|
|
#
|
|
# Definition
|
|
#
|
|
def __init__(self, width, length):
|
|
self.width = width
|
|
self.length = length
|
|
|
|
# Control
|
|
self.enable = Signal()
|
|
|
|
# Input
|
|
self.dat_i = Signal(width)
|
|
|
|
# Output
|
|
self.stb_o = Signal()
|
|
self.dat_o = Signal(width)
|
|
|
|
def get_fragment(self):
|
|
|
|
# Register Input
|
|
dat_i_d = Signal(self.width)
|
|
|
|
sync =[dat_i_d.eq(self.dat_i)]
|
|
|
|
# Detect diff
|
|
diff = Signal()
|
|
comb = [diff.eq(~self.enable | (dat_i_d != self.dat_i))]
|
|
|
|
diff_rising = RisingEdge(diff)
|
|
diff_d = Signal()
|
|
sync +=[diff_d.eq(diff)]
|
|
|
|
# Generate RLE word
|
|
rle_cnt = Signal(max=self.length)
|
|
rle_max = Signal()
|
|
|
|
comb +=[If(rle_cnt == self.length, rle_max.eq(self.enable))]
|
|
|
|
sync +=[
|
|
If(diff | rle_max,
|
|
rle_cnt.eq(0)
|
|
).Else(
|
|
rle_cnt.eq(rle_cnt + 1)
|
|
)
|
|
]
|
|
|
|
# Mux RLE word and data
|
|
comb +=[
|
|
If(diff_rising.o & (~rle_max),
|
|
self.stb_o.eq(1),
|
|
self.dat_o[self.width-1].eq(1),
|
|
self.dat_o[:len(rle_cnt)].eq(rle_cnt)
|
|
).Elif(diff_d | rle_max,
|
|
self.stb_o.eq(1),
|
|
self.dat_o.eq(dat_i_d)
|
|
).Else(
|
|
self.stb_o.eq(0),
|
|
)
|
|
]
|
|
|
|
return Fragment(comb, sync) + diff_rising.get_fragment()
|
|
|
|
class Sequencer:
|
|
#
|
|
# Definition
|
|
#
|
|
def __init__(self):
|
|
|
|
# Control
|
|
self.rst = Signal()
|
|
self.arm = Signal()
|
|
|
|
# Trigger
|
|
self.hit = Signal()
|
|
|
|
# Recorder
|
|
self.start = Signal()
|
|
self.done = Signal()
|
|
|
|
# Internal
|
|
self.enable = Signal()
|
|
|
|
def get_fragment(self):
|
|
|
|
# FSM
|
|
fsm = FSM("IDLE", "ACTIVE")
|
|
|
|
# Idle
|
|
fsm.act(fsm.IDLE,
|
|
If(self.arm,
|
|
fsm.next_state(fsm.ACTIVE),
|
|
)
|
|
)
|
|
|
|
# Active
|
|
fsm.act(fsm.ACTIVE,
|
|
If(self.done | self.rst,
|
|
fsm.next_state(fsm.IDLE),
|
|
),
|
|
self.enable.eq(1)
|
|
)
|
|
|
|
# Start
|
|
hit_rising = RisingEdge(self.hit)
|
|
comb =[self.start.eq(self.enable & hit_rising.o)]
|
|
|
|
return Fragment(comb) + fsm.get_fragment() + hit_rising.get_fragment()
|
|
|
|
|
|
REC_RST_BASE = 0x00
|
|
REC_RLE_BASE = 0x01
|
|
REC_ARM_BASE = 0x02
|
|
REC_DONE_BASE = 0x03
|
|
REC_SIZE_BASE = 0x04
|
|
REC_OFFSET_BASE = 0x06
|
|
REC_READ_BASE = 0x08
|
|
REC_READ_DATA_BASE = 0x09
|
|
|
|
class Recorder:
|
|
#
|
|
# Definition
|
|
#
|
|
def __init__(self, width, depth, address=0x0000, interface=None):
|
|
self.width = width
|
|
self.depth = depth
|
|
self.depth_width = bits_for(self.depth-1)
|
|
|
|
self.storage = Storage(self.width, self.depth)
|
|
self.sequencer = Sequencer()
|
|
self.rle = RLE(self.width, (2**(width-2)))
|
|
|
|
# csr interface
|
|
self._rst = RegisterField("rst", reset=1)
|
|
self._rle = RegisterField("rle", reset=0)
|
|
self._arm = RegisterField("arm", reset=0)
|
|
self._done = RegisterField("done", reset=0, access_bus=READ_ONLY,
|
|
access_dev=WRITE_ONLY)
|
|
|
|
self._size = RegisterField("size", self.depth_width, reset=1)
|
|
self._offset = RegisterField("offset", self.depth_width, reset=1)
|
|
|
|
self._pull_stb = RegisterField("pull_stb", reset=0)
|
|
self._pull_dat = RegisterField("pull_dat", self.width, reset=1,
|
|
access_bus=READ_ONLY, access_dev=WRITE_ONLY)
|
|
|
|
self.regs = [self._rst, self._rle, self._arm, self._done, self._size, self._offset,
|
|
self._pull_stb, self._pull_dat]
|
|
|
|
# set address / interface
|
|
self.set_address(address)
|
|
self.set_interface(interface)
|
|
|
|
# trigger Interface
|
|
self.hit = Signal()
|
|
self.dat = Signal(self.width)
|
|
|
|
def set_address(self, address):
|
|
self.address = address
|
|
self.bank = csrgen.Bank(self.regs, address=self.address)
|
|
|
|
def set_interface(self, interface):
|
|
self.interface = interface
|
|
|
|
def get_fragment(self):
|
|
|
|
_pull_stb_rising = RisingEdge(self._pull_stb.field.r)
|
|
|
|
# Bank <--> Storage / Sequencer
|
|
comb = [
|
|
self.sequencer.rst.eq(self._rst.field.r),
|
|
self.storage.rst.eq(self._rst.field.r),
|
|
|
|
self.rle.enable.eq(self._rle.field.r),
|
|
self.sequencer.arm.eq(self._arm.field.r),
|
|
self.storage.offset.eq(self._offset.field.r),
|
|
self.storage.size.eq(self._size.field.r),
|
|
|
|
self._done.field.w.eq(~self.sequencer.enable),
|
|
|
|
self.storage.pull_stb.eq(_pull_stb_rising.o),
|
|
self._pull_dat.field.w.eq(self.storage.pull_dat)
|
|
]
|
|
|
|
# Storage <--> Sequencer <--> Trigger
|
|
comb += [
|
|
self.storage.start.eq(self.sequencer.start),
|
|
self.sequencer.done.eq(self.storage.done),
|
|
self.sequencer.hit.eq(self.hit),
|
|
|
|
self.rle.dat_i.eq(self.dat),
|
|
|
|
self.storage.push_stb.eq(self.sequencer.enable & self.rle.stb_o),
|
|
self.storage.push_dat.eq(self.rle.dat_o)
|
|
]
|
|
|
|
return self.bank.get_fragment() + Fragment(comb) +\
|
|
self.storage.get_fragment() + self.sequencer.get_fragment() +\
|
|
_pull_stb_rising.get_fragment() + self.rle.get_fragment()
|
|
|
|
|
|
|
|
#
|
|
# Driver
|
|
#
|
|
def reset(self):
|
|
self.interface.write(self.bank.get_base() + REC_RST_BASE, 1)
|
|
self.interface.write(self.bank.get_base() + REC_RST_BASE, 0)
|
|
|
|
def enable_rle(self):
|
|
self.interface.write(self.bank.get_base() + REC_RLE_BASE, 1)
|
|
|
|
def disable_rle(self):
|
|
self.interface.write(self.bank.get_base() + REC_RLE_BASE, 0)
|
|
|
|
def arm(self):
|
|
self.interface.write(self.bank.get_base() + REC_ARM_BASE, 1)
|
|
self.interface.write(self.bank.get_base() + REC_ARM_BASE, 0)
|
|
|
|
def is_done(self):
|
|
return self.interface.read(self.bank.get_base() + REC_DONE_BASE) == 1
|
|
|
|
def set_size(self, dat):
|
|
self.interface.write_n(self.bank.get_base() + REC_SIZE_BASE, dat, 16)
|
|
|
|
def set_offset(self, dat):
|
|
self.interface.write_n(self.bank.get_base() + REC_OFFSET_BASE, dat, 16)
|
|
|
|
def pull(self, size):
|
|
r = []
|
|
for i in range(size):
|
|
self.interface.write(self.bank.get_base() + REC_READ_BASE, 1)
|
|
self.interface.write(self.bank.get_base() + REC_READ_BASE, 0)
|
|
r.append(self.interface.read_n(self.bank.get_base() + REC_READ_DATA_BASE, self.width))
|
|
return r
|