示例#1
0
    def __init__(self, platform, sys_clk_freq):
        self.clock_domains.cd_sys = ClockDomain()
        self.clock_domains.cd_por = ClockDomain()

        # # #

        # Clocking
        clk12 = platform.request("clk12")
        rst_n = platform.request("user_btn_n")
        if sys_clk_freq == 12e6:
            self.comb += self.cd_sys.clk.eq(clk12)
        else:
            self.submodules.pll = pll = iCE40PLL(primitive="SB_PLL40_PAD")
            pll.register_clkin(clk12, 12e6)
            pll.create_clkout(self.cd_sys, sys_clk_freq)
        platform.add_period_constraint(self.cd_sys.clk, 1e9/sys_clk_freq)

        # Power On Reset
        por_cycles  = 4096
        por_counter = Signal(log2_int(por_cycles), reset=por_cycles-1)
        self.comb += self.cd_por.clk.eq(self.cd_sys.clk)
        platform.add_period_constraint(self.cd_por.clk, 1e9/sys_clk_freq)
        self.sync.por += If(por_counter != 0, por_counter.eq(por_counter - 1))
        self.specials += AsyncResetSynchronizer(self.cd_por, ~rst_n)
        self.specials += AsyncResetSynchronizer(self.cd_sys, (por_counter != 0))
    def __init__(self, platform, sys_clk_freq):
        self.rst = Signal()
        self.clock_domains.cd_sys = ClockDomain()
        self.clock_domains.cd_por = ClockDomain(reset_less=True)

        # # #

        # Clk/Rst
        clk12 = platform.request("clk12")
        rst_n = platform.request("user_btn_n")

        # Power On Reset
        por_count = Signal(16, reset=2**16 - 1)
        por_done = Signal()
        self.comb += self.cd_por.clk.eq(ClockSignal())
        self.comb += por_done.eq(por_count == 0)
        self.sync.por += If(~por_done, por_count.eq(por_count - 1))

        # PLL
        self.submodules.pll = pll = iCE40PLL(primitive="SB_PLL40_PAD")
        self.comb += pll.reset.eq(~rst_n | self.rst)
        pll.register_clkin(clk12, 12e6)
        pll.create_clkout(self.cd_sys, sys_clk_freq, with_reset=False)
        self.specials += AsyncResetSynchronizer(self.cd_sys,
                                                ~por_done | ~pll.locked)
        platform.add_period_constraint(self.cd_sys.clk, 1e9 / sys_clk_freq)
示例#3
0
    def __init__(self, platform, sys_clk_freq):
        assert sys_clk_freq == 12e6
        self.rst = Signal()
        self.clock_domains.cd_sys    = ClockDomain()
        self.clock_domains.cd_por    = ClockDomain(reset_less=True)
        self.clock_domains.cd_usb_12 = ClockDomain()
        self.clock_domains.cd_usb_48 = ClockDomain()

        # # #

        # Clk/Rst
        clk48 = platform.request("clk48")
        platform.add_period_constraint(clk48, 1e9/48e6)

        # Power On Reset
        por_count = Signal(16, reset=2**16-1)
        por_done  = Signal()
        self.comb += self.cd_por.clk.eq(ClockSignal())
        self.comb += por_done.eq(por_count == 0)
        self.sync.por += If(~por_done, por_count.eq(por_count - 1))

        # USB PLL
        self.submodules.pll = pll = iCE40PLL()
        #self.comb += pll.reset.eq(self.rst) # FIXME: Add proper iCE40PLL reset support and add back | self.rst.
        pll.clko_freq_range = ( 12e6,  275e9) # FIXME: improve iCE40PLL to avoid lowering clko_freq_min.
        pll.register_clkin(clk48, 48e6)
        pll.create_clkout(self.cd_usb_12, 12e6, with_reset=False)
        self.comb += self.cd_usb_48.clk.eq(clk48)
        self.specials += AsyncResetSynchronizer(self.cd_usb_12, ~por_done | ~pll.locked)
        self.specials += AsyncResetSynchronizer(self.cd_usb_48, ~por_done | ~pll.locked)

        # Sys Clk
        self.comb += self.cd_sys.clk.eq(self.cd_usb_12.clk)
        self.specials += AsyncResetSynchronizer(self.cd_sys, ~por_done | ~pll.locked)
示例#4
0
 def __init__(self, platform, test=False):
     clk100 = platform.request('clk100')
     self.clock_domains.cd_sys = ClockDomain(reset_less=True)
     self.sync += [self.cd_sys.clk.eq(clk100)]
     platform.add_period_constraint(self.cd_sys.clk, 10)
     #rst_n = platform.request("rst_n")
     self.clock_domains.cd_slow = ClockDomain(reset_less=True)
     platform.add_period_constraint(self.cd_slow.clk, 50)
     n = 4
     self.counter = Signal(max=n + 1)
     self.sync += If(self.counter == 0,
                     self.cd_slow.clk.eq(~self.cd_slow.clk),
                     self.counter.eq(n)).Else(
                         self.counter.eq(self.counter - 1))
     self.clock_domains.cd_slow_pll = ClockDomain(reset_less=True)
     platform.add_period_constraint(self.cd_slow_pll.clk, 50)
     if not test:
         self.submodules.pll = pll = iCE40PLL()
         #self.comb += pll.reset.eq(~rst_n)
         pll.register_clkin(clk100, 100e6)
         pll.create_clkout(self.cd_slow_pll, 20e6)
     self.test = platform.request('led2')
     self.sync.slow += self.test.eq(~self.test)
     self.testfsm = platform.request('led3')
     self.submodules.fsm = ClockDomainsRenamer('slow_pll')(
         FSM(reset_state="RESET"))
     self.fsm.act("RESET", NextState("IDLE"))
     self.fsm.act("IDLE", NextValue(self.testfsm, ~self.testfsm))
示例#5
0
    def __init__(self, platform, sys_clk_freq):
        self.rst = Signal()
        self.clock_domains.cd_sys = ClockDomain()
        self.clock_domains.cd_por = ClockDomain(reset_less=True)

        # # #

        # Clk/Rst
        clk100 = platform.request("clk100")
        rst_n = platform.request("user_btn_n")

        # Power On Reset
        por_count = Signal(16, reset=2**16 - 1)
        por_done = Signal()
        self.comb += self.cd_por.clk.eq(ClockSignal())
        self.comb += por_done.eq(por_count == 0)
        self.sync.por += If(~por_done, por_count.eq(por_count - 1))

        # PLL
        self.submodules.pll = pll = iCE40PLL()
        self.comb += pll.reset.eq(
            rst_n
        )  # FIXME: Add proper iCE40PLL reset support and add back | self.rst.
        pll.register_clkin(clk100, 100e6)
        pll.create_clkout(self.cd_sys, sys_clk_freq, with_reset=False)
        self.specials += AsyncResetSynchronizer(self.cd_sys,
                                                ~por_done | ~pll.locked)
        platform.add_period_constraint(self.cd_sys.clk, 1e9 / sys_clk_freq)

        # SDRAM clock
        self.specials += DDROutput(0, 1, platform.request("sdram_clock"),
                                   ClockSignal("sys"))
示例#6
0
    def __init__(self, platform, sys_clk_freq):
        self.clock_domains.cd_sys = ClockDomain()
        self.clock_domains.cd_por = ClockDomain()

        # # #

        # Clocks
        clk16 = platform.request("clk16")
        platform.add_period_constraint(clk16, 1e9 / 16e6)
        if sys_clk_freq == 16e6:
            self.comb += self.cd_sys.clk.eq(clk16)
        else:
            self.submodules.pll = pll = iCE40PLL()
            pll.register_clkin(clk16, 16e6)
            pll.create_clkout(self.cd_sys, sys_clk_freq, with_reset=False)
        platform.add_period_constraint(self.cd_sys.clk, 1e9 / sys_clk_freq)

        # Power On Reset
        self.reset = Signal()
        por_cycles = 8
        por_counter = Signal(log2_int(por_cycles), reset=por_cycles - 1)
        self.comb += self.cd_por.clk.eq(self.cd_sys.clk)
        platform.add_period_constraint(self.cd_por.clk, 1e9 / sys_clk_freq)
        self.sync.por += If(por_counter != 0, por_counter.eq(por_counter - 1))
        self.comb += self.cd_sys.rst.eq(por_counter != 0)
        self.specials += AsyncResetSynchronizer(self.cd_por, self.reset)
    def __init__(self, platform, sys_clk_freq):
        self.clock_domains.cd_sys = ClockDomain()
        self.clock_domains.cd_por = ClockDomain()
        self.clock_domains.cd_vga = ClockDomain()

        # # #

        # Clocks
        clk12 = platform.request("clk12")
        self.submodules.pll = pll = iCE40PLL(primitive="SB_PLL40_PAD")
        pll.register_clkin(clk12, 12e6)
        pll.create_clkout(self.cd_vga, 40e6)

        clk_counter = Signal()
        self.sync.vga += clk_counter.eq(clk_counter - 1)
        self.comb += self.cd_sys.clk.eq(clk_counter[0])

        platform.add_period_constraint(self.cd_sys.clk, 1e9 / sys_clk_freq)
        platform.add_period_constraint(self.cd_vga.clk, 1e9 / 40e6)

        # Power On Reset
        self.reset = Signal()
        por_cycles = 4096
        por_counter = Signal(log2_int(por_cycles), reset=por_cycles - 1)
        self.comb += self.cd_por.clk.eq(self.cd_vga.clk)
        platform.add_period_constraint(self.cd_por.clk, 1e9 / sys_clk_freq)
        self.sync.por += If(por_counter != 0, por_counter.eq(por_counter - 1))
        self.comb += self.cd_sys.rst.eq(por_counter != 0)
        self.specials += AsyncResetSynchronizer(self.cd_por, self.reset)
示例#8
0
    def __init__(self, vga, clk12):
        self.clock_domains.sys = ClockDomain()
        self.comb += self.sys.clk.eq(clk12)

        self.clock_domains.vga = ClockDomain()
        self.vga_clk_pll = iCE40PLL(primitive='SB_PLL40_PAD')
        self.submodules.vga_clk_pll = self.vga_clk_pll
        self.vga_clk_pll.register_clkin(self.sys.clk, 12e6)
        self.vga_clk_pll.create_clkout(self.vga, 25.175e6)

        self.vga_timing = VgaTiming()
        self.submodules.vga_timing = self.vga_timing

        self.submodules.test_pattern = TestPattern(vga)

        self.comb += [
            vga.hsync.eq(self.vga_timing.hsync),
            vga.vsync.eq(self.vga_timing.vsync),
        ]
示例#9
0
文件: core.py 项目: aa88kk/hexastorm
 def __init__(self, platform, test=False):
     # variables
     self.ticksinfacet = round(
         self.VARIABLES['CRYSTAL_HZ'] /
         (self.VARIABLES['RPM'] / 60 * self.VARIABLES['FACETS']))
     LASERTICKS = int(self.VARIABLES['CRYSTAL_HZ'] /
                      self.VARIABLES['LASER_HZ'])
     self.JITTERTICKS = round(0.5 * LASERTICKS)
     if self.VARIABLES['END%'] > round(1 - (self.JITTERTICKS + 1) /
                                       self.ticksinfacet):
         raise Exception("Invalid settings, END% too high")
     self.BITSINSCANLINE = round(
         (self.ticksinfacet *
          (self.VARIABLES['END%'] - self.VARIABLES['START%'])) / LASERTICKS)
     if self.BITSINSCANLINE <= 0:
         raise Exception("Bits in scanline invalid")
     self.bytesinline = math.ceil(
         self.BITSINSCANLINE / 8) + 1  # command byte is equal to 1
     if self.VARIABLES['SINGLE_LINE'] == 1:
         self.MEMDEPTH = self.bytesinline
     elif (self.MEMWIDTH * self.MEMDEPTH) // self.BITSINSCANLINE < 5:
         raise Exception("Memory too small for 5 lines")
     # clock; routing was not able to reach speed higher than 70 MHz
     #        for entire circuit on iCE40, so clock is contraint to 50 MHz
     clk100 = platform.request('clk100')
     self.clock_domains.cd_sys = ClockDomain(reset_less=True)
     platform.add_period_constraint(self.cd_sys.clk, 20)
     if not test:
         self.submodules.pll = pll = iCE40PLL()
         #self.comb += pll.reset.eq(~rst_n)
         pll.register_clkin(clk100, 100e6)
         pll.create_clkout(self.cd_sys, self.VARIABLES['CRYSTAL_HZ'])
     # three submodules; SPI receiver, memory and laser state machine
     # full byte state
     self.laserfsmstate = Signal(3)  # state laser module 5-8 bit
     self.error = Signal(5)  # error              0-5 bit
     debug = Signal(8)  # optional
     # Memory element
     # Rules:
     #        read cannot be set equal to write address  --> handled by laser ledfsm
     #        write cannot be set equal to read address  --> handled by receiver statemachne
     # readbit, current bit read
     # written to detect if already information is written to memory
     #         written can go zero after a write... if the memory is complety read --> it is no longer "written"
     # dat_r_temp , data is shifted after read. It is believed that this is not possible on the memory element
     # As a result data is copied to another element first.
     # sram memory is 32 blocks... each block has its own ports
     # one block 8*512 = 4096 bits currently used
     self.specials.mem = Memory(width=self.MEMWIDTH, depth=self.MEMDEPTH)
     writeport = self.mem.get_port(write_capable=True, mode=READ_FIRST)
     self.readport = self.mem.get_port(has_re=True)
     self.specials += writeport, self.readport
     self.ios = {
         writeport.adr, writeport.dat_w, writeport.we, self.readport.dat_r,
         self.readport.adr, self.readport.re
     }
     readbit = Signal(max=self.MEMWIDTH)
     written = Signal()
     self.dat_r_new = Signal(self.MEMWIDTH)
     dat_r_old = Signal(self.MEMWIDTH)
     # Receiver State Machine
     # TODO: tried to paralize receiver and parser but this gives errors
     #       I am not able achieve agreement between Linux host and FPGA on memfull
     #       Memfull is not correctly propagated every 10K operations which results in failure.
     # Consists out of component from litex and own custom component
     # Detects whether new command is available
     self.spi = platform.request("spi")
     spislave = SPISlave(self.spi, data_width=8)
     self.submodules.slave = spislave
     # Done detector
     done_d = Signal()
     done_rise = Signal()
     self.sync += done_d.eq(spislave.done)
     self.comb += done_rise.eq(spislave.done & ~done_d)
     # Start detector
     start_d = Signal()
     start_rise = Signal()
     self.sync += start_d.eq(spislave.start)
     self.comb += start_rise.eq(spislave.start & ~start_d)
     # Memfull trigger
     dummyf = Signal(max=2 * self.MEMDEPTH)
     dummyb = Signal(min=-self.MEMDEPTH, max=self.MEMDEPTH)
     self.sync += dummyf.eq(writeport.adr + self.CHUNKSIZE + 1)
     self.sync += dummyb.eq(writeport.adr + self.CHUNKSIZE + 1 -
                            (self.MEMDEPTH - 1))
     self.submodules.parser = FSM(reset_state="RESET")
     spi_mosi = Signal(self.MEMWIDTH)
     parsertrigger = Signal()
     memfulld = Signal()
     command = Signal()
     chunkcnt = Signal(max=self.CHUNKSIZE + 1)
     self.parser.act("RESET", NextValue(command, 1), NextValue(chunkcnt, 0),
                     NextState('WAITFORSTART'))
     self.parser.act(
         "WAITFORSTART",
         If(start_rise, NextState("WAITFORDONE")).Else(
             If((written == 1) & (self.VARIABLES['SINGLE_LINE'] == False),
                If((dummyf > self.readport.adr) &
                   (self.readport.adr > writeport.adr),
                   NextValue(self.error[self.ERRORS.MEMFULL], 1)).Elif(
                       (dummyb > self.readport.adr) &
                       (self.readport.adr < writeport.adr),
                       NextValue(self.error[self.ERRORS.MEMFULL], 1)).Else(
                           NextValue(
                               self.error[self.ERRORS.MEMFULL], 0))).Else(
                                   NextValue(
                                       self.error[self.ERRORS.MEMFULL], 0)),
             NextValue(spislave.miso, Cat([self.error,
                                           self.laserfsmstate])),
             NextValue(memfulld, self.error[self.ERRORS.MEMFULL])))
     self.parser.act(
         "WAITFORDONE",
         NextValue(spi_mosi, spislave.mosi),
         If(
             done_rise,
             #TODO: don't overfloat bug with not stable
             NextValue(spislave.miso, 4),
             If(command, NextState("PROCESSCOMMAND")).Else(
                 If(chunkcnt >= self.CHUNKSIZE - 1, NextValue(chunkcnt, 0),
                    NextValue(command,
                              1)).Else(NextValue(chunkcnt, chunkcnt + 1)),
                 NextValue(command, 1),
                 NextValue(writeport.dat_w, spislave.mosi),
                 NextValue(writeport.we, 1), NextState("WRITE"))))
     # command is now used as extra parameter for state and makes it more confusing
     self.parser.act(
         "PROCESSCOMMAND", NextState("WAITFORSTART"),
         If(spi_mosi == self.COMMANDS.STOP,
            NextValue(self.laserfsmstate, self.STATES.STOP)).Elif(
                spi_mosi == self.COMMANDS.START,
                NextValue(self.laserfsmstate, self.STATES.START)).Elif(
                    spi_mosi == self.COMMANDS.LASERTEST,
                    NextValue(self.laserfsmstate,
                              self.STATES.LASERTEST)).Elif(
                                  spi_mosi == self.COMMANDS.MOTORTEST,
                                  NextValue(self.laserfsmstate,
                                            self.STATES.MOTORTEST)).
         Elif(
             spi_mosi == self.COMMANDS.LINETEST,
             NextValue(self.laserfsmstate, self.STATES.LINETEST)).Elif(
                 spi_mosi == self.COMMANDS.PHOTODIODETEST,
                 NextValue(
                     self.laserfsmstate, self.STATES.PHOTODIODETEST)).Elif(
                         spi_mosi == self.COMMANDS.WRITE_L,
                         If(memfulld == 0, NextValue(command, 0))).Elif(
                             spi_mosi == self.COMMANDS.STATUS).Elif(
                                 spi_mosi != 0,
                                 NextValue(self.error[self.ERRORS.INVALID],
                                           1)))
     self.parser.act(
         "WRITE",
         NextState("WAITFORSTART"),
         NextValue(written, 1),
         # Write address position
         If(writeport.adr + 1 == self.MEMDEPTH, NextValue(writeport.adr,
                                                          0)).
         # wrap around is different in single line mode
         Elif((writeport.adr == math.ceil(
             self.BITSINSCANLINE / self.MEMWIDTH)) &
              (self.VARIABLES['SINGLE_LINE'] == True),
              NextValue(writeport.adr,
                        0)).Else(NextValue(writeport.adr,
                                           writeport.adr + 1)),
         NextValue(writeport.we, 0))
     # the original motor driver was designed for 6 facets and pulsed for eached facet
     polyperiod = int(self.VARIABLES['CRYSTAL_HZ'] /
                      (self.VARIABLES['RPM'] / 60) / (6 * 2))
     pwmcounter = Signal(max=polyperiod)
     self.poly_pwm = platform.request("poly_pwm")
     self.sync += If(pwmcounter == 0, self.poly_pwm.eq(~self.poly_pwm),
                     pwmcounter.eq(polyperiod - 1)).Else(
                         pwmcounter.eq(pwmcounter - 1))
     # Laser FSM
     # Laser FSM controls the laser, polygon anld output to motor
     self.facetcnt = Signal(max=self.VARIABLES['FACETS'])
     # stable counter used for both spinup and photo diode stable
     spinupticks = round(self.VARIABLES['SPINUP_TIME'] *
                         self.VARIABLES['CRYSTAL_HZ'])
     stableticks = round(self.VARIABLES['STABLE_TIME'] *
                         self.VARIABLES['CRYSTAL_HZ'])
     stablecounter = Signal(max=max(
         spinupticks, stableticks))  # counter is used twice, hence the max
     stablethresh = Signal(max=stableticks)
     self.lasercnt = Signal(max=LASERTICKS)
     self.scanbit = Signal(max=self.BITSINSCANLINE + 1)
     self.tickcounter = Signal(max=int(self.ticksinfacet * 2))
     self.submodules.laserfsm = FSM(reset_state="RESET")
     # leesfoutdetectie:
     #   in het begin zijn lees en schrijfadres gelijk
     #   als er geschreven is dan is het schrijf adres een groter dan lees adres
     #   als er geschreven is en het volgende adres waarvan je gaat lezen nog niet beschreven is --> lees fout
     # op het moment kost lezen een tick, dit zou voorkomen kunnen worden
     # tick counter; number of ticks in a facet for the oscillator
     # laser counter; laser operates at reduced speed this controlled by this counter
     # readbit counter; current bit position in memory
     # scanbit counter; current bit position along scanline
     readtrig = Signal()
     self.submodules.readmem = FSM(reset_state="RESET")
     self.readmem.act("RESET", NextValue(self.readport.adr, 0),
                      NextValue(self.readport.re, 1), NextValue(written, 0),
                      NextState("WAIT"))
     self.readmem.act(
         "WAIT",
         If(readtrig, NextValue(self.readport.re, 0), NextState("READ")))
     self.readmem.act(
         "READ",
         NextValue(readtrig, 0),
         NextValue(self.readport.re, 1),
         If(
             (self.readport.adr == writeport.adr) & (written == 0),
             NextValue(self.error[self.ERRORS.MEMREAD], 1),
         ).Else(
             #TODO: you could split into two signals --> one if ever memread occured, other memread signal
             NextValue(self.error[self.ERRORS.MEMREAD], 0),
             NextValue(self.dat_r_new, self.readport.dat_r)),
         # always increase address, if you move over the write set written to zero
         If(
             self.readport.adr + 1 == self.MEMDEPTH,
             NextValue(self.readport.adr, 0),
             If((writeport.adr == 0) &
                (self.VARIABLES['SINGLE_LINE'] == False),
                NextValue(written, 0))).Else(
                    NextValue(self.readport.adr, self.readport.adr + 1),
                    If((self.readport.adr + 1 == writeport.adr) &
                       (self.VARIABLES['SINGLE_LINE'] == False),
                       NextValue(written, 0))),
         NextState("WAIT"))
     self.laserfsm.act("RESET", NextValue(self.readport.adr, 0),
                       NextValue(writeport.adr, 0), NextState("STOP"))
     self.laser0 = platform.request("laser0")
     self.poly_en = platform.request("poly_en")
     self.photodiode = platform.request("photodiode")
     self.laserfsm.act(
         "STOP",
         NextValue(stablethresh, stableticks - 1),
         NextValue(stablecounter, 0),
         NextValue(self.facetcnt, 0),
         NextValue(self.tickcounter, 0),
         NextValue(self.scanbit, 0),
         NextValue(self.lasercnt, 0),
         NextValue(self.laser0, 0),
         NextValue(self.poly_en, 1),
         NextValue(readbit, 0),
         If(
             self.laserfsmstate == self.STATES.START,
             If(self.photodiode == 0,
                NextValue(self.laserfsmstate, self.STATES.STOP),
                NextState("STOP")).Else(
                    NextValue(readtrig, 1),
                    NextValue(self.error[self.ERRORS.NOTSTABLE], 0),
                    NextValue(self.error[self.ERRORS.MEMREAD], 0),
                    NextValue(self.poly_en, 0), NextState("SPINUP"))).Elif(
                        self.laserfsmstate == self.STATES.MOTORTEST,
                        NextValue(self.poly_en, 0),
                        NextState("MOTORTEST")).Elif(
                            self.laserfsmstate == self.STATES.LASERTEST,
                            NextValue(self.laser0, 1),
                            NextState("LASERTEST")).Elif(
                                self.laserfsmstate == self.STATES.LINETEST,
                                NextValue(self.laser0, 1),
                                NextValue(self.poly_en, 0),
                                NextState("LINETEST")).
         Elif(
             self.laserfsmstate == self.STATES.PHOTODIODETEST,
             # photodiode should be high with laser off
             # something is wrong, this makes sure error is produced
             If(self.photodiode == 0, NextValue(self.laser0, 1),
                NextValue(self.poly_en, 1)).Else(
                    NextValue(self.laser0, 1),
                    NextValue(self.poly_en, 0),
                ),
             NextState("PHOTODIODETEST")))
     self.laserfsm.act(
         "MOTORTEST",
         If(self.laserfsmstate != self.STATES.MOTORTEST, NextState("STOP")))
     self.laserfsm.act(
         "LASERTEST",
         If(self.laserfsmstate != self.STATES.LASERTEST, NextState("STOP")))
     self.laserfsm.act(
         "LINETEST",
         If(self.laserfsmstate != self.STATES.LINETEST, NextState("STOP")))
     # Photodiode rising edge detector
     photodiode_d = Signal()
     self.laserfsm.act(
         "PHOTODIODETEST",
         If((self.photodiode == 0) & (self.poly_en == 0),
            NextValue(self.laserfsmstate, self.STATES.STOP),
            NextState("STOP")).Elif(
                self.laserfsmstate != self.STATES.PHOTODIODETEST,
                NextState("STOP")))
     self.laserfsm.act(
         "SPINUP", NextValue(stablecounter, stablecounter + 1),
         If(
             stablecounter > spinupticks - 1,
             NextState("STATE_WAIT_STABLE"),
             NextValue(self.laser0, 1),
             NextValue(stablecounter, 0),
         ))
     self.laserfsm.act(
         "STATE_WAIT_STABLE",
         NextValue(stablecounter, stablecounter + 1),
         NextValue(photodiode_d, self.photodiode),
         If(stablecounter >= stablethresh,
            NextValue(self.error[self.ERRORS.NOTSTABLE], 1),
            NextValue(self.laserfsmstate, self.STATES.STOP),
            NextState('RESET')).
         Elif(
             ~self.photodiode & ~photodiode_d,
             NextValue(self.tickcounter, 0),
             NextValue(self.laser0, 0),
             If(
                 (self.tickcounter > self.ticksinfacet - self.JITTERTICKS) &
                 (self.tickcounter < self.ticksinfacet + self.JITTERTICKS),
                 If(self.facetcnt == self.VARIABLES['FACETS'] - 1,
                    NextValue(self.facetcnt, 0)).Else(
                        NextValue(self.facetcnt, self.facetcnt + 1)),
                 NextValue(stablecounter, 0),
                 If((self.VARIABLES['SINGLE_FACET'] == True) &
                    (self.facetcnt > 0), NextState('WAIT_END')).Else(
                        NextValue(
                            stablethresh,
                            min(round(10.1 * self.ticksinfacet),
                                stableticks)),  #TODO: lower!
                        NextState('READ_INSTRUCTION'))).Else(
                            NextState('WAIT_END'))).Elif(
                                self.laserfsmstate != self.STATES.START,
                                NextState("RESET")).Else(
                                    NextValue(self.tickcounter,
                                              self.tickcounter + 1)))
     self.laserfsm.act(
         'READ_INSTRUCTION',
         NextValue(self.tickcounter, self.tickcounter + 1),
         If(
             readtrig == 0,
             If(
                 self.error[self.ERRORS.MEMREAD] == 1,
                 # move back the address and read again
                 If(self.readport.adr == 0,
                    NextValue(self.readport.adr, self.MEMDEPTH - 1)).Else(
                        NextValue(self.readport.adr,
                                  self.readport.adr - 1)),
                 NextValue(readtrig, 1),
                 NextState("WAIT_END")).Elif(
                     self.dat_r_new == self.INSTRUCTIONS.STOP,
                     NextState("RESET"),
                     NextValue(self.laserfsmstate, self.STATES.STOP)).Elif(
                         self.dat_r_new == self.INSTRUCTIONS.SCAN,
                         NextState('WAIT_FOR_DATA_RUN'),
                         NextValue(readtrig, 1),
                     ).Else(
                         NextState("RESET"),
                         NextValue(self.laserfsmstate, self.STATES.STOP),
                         NextValue(self.error[self.ERRORS.INVALIDLINE], 1),
                     )))
     self.laserfsm.act(
         'WAIT_FOR_DATA_RUN',
         NextValue(self.tickcounter, self.tickcounter + 1),
         If(
             readtrig == 0,
             If(self.error[self.ERRORS.MEMREAD] == 1,
                NextValue(dat_r_old,
                          0)).Else(NextValue(dat_r_old, self.dat_r_new)),
             NextValue(readbit, 0),
             NextValue(self.scanbit, 0),
             NextValue(self.lasercnt, 0),
             If(
                 self.tickcounter == int(self.VARIABLES['START%'] *
                                         self.ticksinfacet - 2),
                 NextState('DATA_RUN')).
             Elif(
                 self.tickcounter
                 > int(self.VARIABLES['START%'] * self.ticksinfacet - 2),
                 #NextValue(self.error[self.ERRORS.INVALID], 1),   #TODO: replace with timeout
                 NextState('DATA_RUN'))))
     self.laserfsm.act(
         "DATA_RUN",
         NextValue(self.tickcounter, self.tickcounter + 1),
         If(
             self.lasercnt == 0,
             #NOTE: readbit and scanbit counters can be different
             #      readbit is your current position in memory and scanbit your current byte position in scanline
             If(
                 self.scanbit >= self.BITSINSCANLINE, NextState("WAIT_END")
             ).Else(
                 NextValue(self.lasercnt, LASERTICKS - 1),
                 NextValue(self.scanbit, self.scanbit + 1),
                 # read from memory before the spinup
                 # it is triggered here again, so fresh data is available once the end is reached
                 # if read bit is 0, trigger a read out unless the next byte is outside of line
                 If(readbit == 0, NextValue(readtrig, 1),
                    NextValue(readbit, readbit + 1),
                    NextValue(dat_r_old, dat_r_old >> 1)).
                 # final read bit copy memory
                 # move to next address, i.e. byte, if end is reached
                 Elif(
                     readbit == self.MEMWIDTH - 1,
                     If(self.error[self.ERRORS.MEMREAD] == 1,
                        NextValue(dat_r_old, 0)).Else(
                            NextValue(dat_r_old, self.dat_r_new)),
                     NextValue(readbit, 0)).Else(
                         NextValue(readbit, readbit + 1),
                         NextValue(dat_r_old, dat_r_old >> 1)),
                 NextValue(self.laser0, dat_r_old[0]))).Else(
                     NextValue(self.lasercnt, self.lasercnt - 1)))
     self.laserfsm.act(
         "WAIT_END", NextValue(stablecounter, stablecounter + 1),
         NextValue(self.tickcounter, self.tickcounter + 1),
         If(
             self.tickcounter >=
             round(self.ticksinfacet - self.JITTERTICKS - 1),
             NextState("STATE_WAIT_STABLE"),
             NextValue(self.laser0, 1),
         ))