def elaborate(self, platform): m = Module() # Synchronize the USB signals at our I/O boundary. # Despite the assumptions made in ValentyUSB, this line rate recovery FSM # isn't enough to properly synchronize these inputs. We'll explicitly synchronize. sync_dp = synchronize(m, self._usbp, o_domain="usb_io") sync_dn = synchronize(m, self._usbn, o_domain="usb_io") ####################################################################### # Line State Recovery State Machine # # The receive path doesn't use a differential receiver. Because of # this there is a chance that one of the differential pairs will appear # to have changed to the new state while the other is still in the old # state. The following state machine detects transitions and waits an # extra sampling clock before decoding the state on the differential # pair. This transition period will only ever last for one clock as # long as there is no noise on the line. If there is enough noise on # the line then the data may be corrupted and the packet will fail the # data integrity checks. # dpair = Cat(sync_dp, sync_dn) # output signals for use by the clock recovery stage line_state_in_transition = Signal() with m.FSM(domain="usb_io") as fsm: m.d.usb_io += [ self.line_state_se0.eq(fsm.ongoing("SE0")), self.line_state_se1.eq(fsm.ongoing("SE1")), self.line_state_dj.eq(fsm.ongoing("DJ")), self.line_state_dk.eq(fsm.ongoing("DK")), ] # If we are in a transition state, then we can sample the pair and # move to the next corresponding line state. with m.State("DT"): m.d.comb += line_state_in_transition.eq(1) with m.Switch(dpair): with m.Case(0b10): m.next = "DJ" with m.Case(0b01): m.next = "DK" with m.Case(0b00): m.next = "SE0" with m.Case(0b11): m.next = "SE1" # If we are in a valid line state and the value of the pair changes, # then we need to move to the transition state. with m.State("DJ"): with m.If(dpair != 0b10): m.next = "DT" with m.State("DK"): with m.If(dpair != 0b01): m.next = "DT" with m.State("SE0"): with m.If(dpair != 0b00): m.next = "DT" with m.State("SE1"): with m.If(dpair != 0b11): m.next = "DT" ####################################################################### # Clock and Data Recovery # # The DT state from the line state recovery state machine is used to align to # transmit clock. The line state is sampled in the middle of the bit time. # # Example of signal relationships # ------------------------------- # line_state DT DJ DJ DJ DT DK DK DK DK DK DK DT DJ DJ DJ # line_state_valid ________----____________----____________----________----____ # bit_phase 0 0 1 2 3 0 1 2 3 0 1 2 0 1 2 # # We 4x oversample, so make the line_state_phase have # 4 possible values. line_state_phase = Signal(2) m.d.usb_io += self.line_state_valid.eq(line_state_phase == 1) with m.If(line_state_in_transition): m.d.usb_io += [ # re-align the phase with the incoming transition line_state_phase.eq(0), # make sure we never assert valid on a transition self.line_state_valid.eq(0), ] with m.Else(): # keep tracking the clock by incrementing the phase m.d.usb_io += line_state_phase.eq(line_state_phase + 1) return m
def check(self, m: Module): with m.If(self.instr.matches(JSR_EXT)): self.assert_cycles(m, 9) addr_hi = self.assert_cycle_signals(m, 1, address=self.data.pre_pc + 1, vma=1, rw=1, ba=0) addr_lo = self.assert_cycle_signals(m, 2, address=self.data.pre_pc + 2, vma=1, rw=1, ba=0) _ = self.assert_cycle_signals(m, 3, address=LCat(addr_hi, addr_lo), vma=1, rw=1, ba=0) retaddr_lo = self.assert_cycle_signals(m, 4, address=self.data.pre_sp, vma=1, rw=0, ba=0) retaddr_hi = self.assert_cycle_signals(m, 5, address=self.data.pre_sp - 1, vma=1, rw=0, ba=0) self.assert_cycle_signals(m, 6, vma=0, ba=0) self.assert_cycle_signals(m, 7, vma=0, ba=0) # I am not convinced the datasheet is correct for this cycle. # It claims there is a read of the pc+2 here. self.assert_cycle_signals(m, 8, vma=0, ba=0) self.assert_registers(m, SP=self.data.pre_sp - 2, PC=LCat(addr_hi, addr_lo)) m.d.comb += Assert( LCat(retaddr_hi, retaddr_lo) == (self.data.pre_pc + 3)[:16]) with m.Else(): self.assert_cycles(m, 8) offset = self.assert_cycle_signals(m, 1, address=self.data.pre_pc + 1, vma=1, rw=1, ba=0) self.assert_cycle_signals(m, 2, vma=0, ba=0) retaddr_lo = self.assert_cycle_signals(m, 3, address=self.data.pre_sp, vma=1, rw=0, ba=0) retaddr_hi = self.assert_cycle_signals(m, 4, address=self.data.pre_sp - 1, vma=1, rw=0, ba=0) self.assert_cycle_signals(m, 5, vma=0, ba=0) self.assert_cycle_signals(m, 6, vma=0, ba=0) self.assert_cycle_signals(m, 7, vma=0, ba=0) self.assert_registers(m, SP=self.data.pre_sp - 2, PC=self.data.pre_x + offset) m.d.comb += Assert( LCat(retaddr_hi, retaddr_lo) == (self.data.pre_pc + 2)[:16]) self.assert_flags(m)
def elaborate(self, platform): m = Module() usbp = Signal() usbn = Signal() oe = Signal() # wait for new packet to start with m.FSM(domain="usb_io"): with m.State("IDLE"): m.d.comb += [ usbp.eq(1), usbn.eq(0), oe.eq(0), ] with m.If(self.i_valid & self.i_oe): # first bit of sync always forces a transition, we idle # in J so the first output bit is K. m.next = "DK" # the output line is in state J with m.State("DJ"): m.d.comb += [ usbp.eq(1), usbn.eq(0), oe.eq(1), ] with m.If(self.i_valid): with m.If(~self.i_oe): m.next = "SE0A" with m.Elif(self.i_data): m.next = "DJ" with m.Else(): m.next = "DK" # the output line is in state K with m.State("DK"): m.d.comb += [ usbp.eq(0), usbn.eq(1), oe.eq(1), ] with m.If(self.i_valid): with m.If(~self.i_oe): m.next = "SE0A" with m.Elif(self.i_data): m.next = "DK" with m.Else(): m.next = "DJ" # first bit of the SE0 state with m.State("SE0A"): m.d.comb += [ usbp.eq(0), usbn.eq(0), oe.eq(1), ] with m.If(self.i_valid): m.next = "SE0B" # second bit of the SE0 state with m.State("SE0B"): m.d.comb += [ usbp.eq(0), usbn.eq(0), oe.eq(1), ] with m.If(self.i_valid): m.next = "EOPJ" # drive the bus back to J before relinquishing control with m.State("EOPJ"): m.d.comb += [ usbp.eq(1), usbn.eq(0), oe.eq(1), ] with m.If(self.i_valid): m.next = "IDLE" m.d.usb_io += [ self.o_oe.eq(oe), self.o_usbp.eq(usbp), self.o_usbn.eq(usbn), ] return m
def elaborate(self, platform: Platform) -> Module: m = Module() m.d.sync += self.PSW.eq(self._psw) m.d.comb += self._psw.eq(self.PSW) with m.Switch(self.oper): with m.Case(Operation.NOP): if self.verification is Operation.NOP: with m.If(~Initial()): m.d.comb += [ Assert(self._psw.N == self.PSW.N), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == self.PSW.Z), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.ADC): low = Cat(self.result[:4], self._psw.H) high = Cat(self.result[4:], self._psw.C) m.d.comb += [ low.eq(self.inputa[:4] + self.inputb[:4] + self.PSW.C), high.eq(self.inputa[4:] + self.inputb[4:] + self._psw.H), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), self._psw.V.eq(self.result[7] != self._psw.C), ] if self.verification is Operation.ADC: r = Signal(8) f = Signal(9) h = Signal(5) m.d.comb += [ r.eq(self.inputa.as_signed() + self.inputb.as_signed() + self.PSW.C), f.eq(self.inputa + self.inputb + self.PSW.C), h.eq(self.inputa[:4] + self.inputb[:4] + self.PSW.C), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self.result == f[:8]), Assert(self._psw.N == f[7]), Assert(self._psw.V == (f[7] ^ f[8])), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == h[4]), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(f[:8].bool())), Assert(self._psw.C == f[8]), ] with m.Case(Operation.SBC): low = Cat(self.result[:4], self._psw.H) high = Cat(self.result[4:], self._psw.C) m.d.comb += [ low.eq(self.inputa[:4] - self.inputb[:4] - self.PSW.C), high.eq(self.inputa[4:] - self.inputb[4:] - self._psw.H), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), self._psw.V.eq(self.result[7] != self._psw.C), ] if self.verification is Operation.SBC: r = Signal(8) f = Signal(9) h = Signal(5) m.d.comb += [ r.eq(self.inputa.as_signed() - self.inputb.as_signed() - self.PSW.C), f.eq(self.inputa - self.inputb - self.PSW.C), h.eq(self.inputa[:4] - self.inputb[:4] - self.PSW.C), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self.result == f[:8]), Assert(self._psw.N == f[7]), Assert(self._psw.V == (f[7] ^ f[8])), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == h[4]), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(f[:8].bool())), Assert(self._psw.C == f[8]), ] with m.Case(Operation.CMP): full = Cat(self.result, self._psw.C) m.d.comb += [ full.eq(self.inputa.as_signed() - self.inputb.as_signed()), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), self._psw.V.eq(self.result[7] != self._psw.C), ] if self.verification is Operation.CMP: r = Signal(9) m.d.comb += r.eq(self.inputa.as_signed() - self.inputb.as_signed()) with m.If(~Initial()): m.d.comb += [ Assert(self.result == r[:8]), Assert(self._psw.N == r[7]), Assert(self._psw.V == (r[7] ^ r[8])), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r[:8].bool())), Assert(self._psw.C == r[8]), ] with m.Case(Operation.AND): m.d.comb += [ self.result.eq(self.inputa & self.inputb), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.AND: r = Signal(8) m.d.comb += r.eq(self.inputa & self.inputb) with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == r[7]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.OOR): m.d.comb += [ self.result.eq(self.inputa | self.inputb), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.OOR: r = Signal(8) m.d.comb += [ r.eq(self.inputa | self.inputb), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == r[7]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.EOR): m.d.comb += [ self.result.eq(self.inputa ^ self.inputb), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.EOR: r = Signal(8) m.d.comb += [ r.eq(self.inputa ^ self.inputb), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == r[7]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.INC): m.d.comb += [ self.result.eq(self.inputa + 1), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.INC: r = Signal(8) m.d.comb += [ r.eq(self.inputa + 1), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == r[7]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.DEC): m.d.comb += [ self.result.eq(self.inputa - 1), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.DEC: r = Signal(8) m.d.comb += [ r.eq(self.inputa - 1), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == r[7]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.ASL): m.d.comb += [ Cat(self.result, self._psw.C).eq(Cat(Const(0), self.inputa)), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.ASL: r = Signal(8) m.d.comb += [ r.eq(self.inputa * 2), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == self.inputa[6]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.inputa[7]), ] with m.Case(Operation.LSR): m.d.comb += [ Cat(self._psw.C, self.result).eq(Cat(self.inputa, Const(0))), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.LSR: r = Signal(8) m.d.comb += [ r.eq(self.inputa // 2), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == 0), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.inputa[0]), ] with m.Case(Operation.ROL): m.d.comb += [ Cat(self.result, self._psw.C).eq(Cat(self.PSW.C, self.inputa)), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.ROL: r = Signal(8) m.d.comb += [ r.eq(self.inputa * 2 + self.PSW.C), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == self.inputa[6]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.inputa[7]), ] with m.Case(Operation.ROR): m.d.comb += [ Cat(self._psw.C, self.result).eq(Cat(self.inputa, self.PSW.C)), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.ROR: r = Signal(8) m.d.comb += [ r.eq(self.inputa // 2 + Cat(Signal(7), self.PSW.C)), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == self.PSW.C), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r.bool())), Assert(self._psw.C == self.inputa[0]), ] with m.Case(Operation.XCN): m.d.comb += [ self.result.eq(Cat(self.inputa[4:], self.inputa[:4])), self._psw.N.eq(self.result.as_signed() < 0), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.XCN: r = Signal(8) m.d.comb += [ r.eq(self.inputa * 16 + self.inputa // 16), ] with m.If(~Initial()): m.d.comb += [ Assert(self.result == r), Assert(self._psw.N == self.inputa[3]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(self.inputa.bool())), Assert(self._psw.C == self.PSW.C), ] with m.Case(Operation.DAA): temp = Signal().like(self.inputa) with m.If(self.PSW.C | (self.inputa > 0x99)): m.d.comb += self._psw.C.eq(1) m.d.comb += temp.eq(self.inputa + 0x60) with m.Else(): m.d.comb += temp.eq(self.inputa) with m.If(self.PSW.H | (temp[:4] > 0x09)): m.d.comb += self.result.eq(temp + 0x06) m.d.comb += [ self._psw.N.eq(self.result & 0x80), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.DAA: with m.If(~Initial()): m.d.comb += [Assert(False)] with m.Case(Operation.DAS): temp = Signal().like(self.inputa) with m.If(~self.PSW.C | (self.inputa > 0x99)): m.d.comb += self._psw.C.eq(0) m.d.comb += temp.eq(self.inputa - 0x60) with m.Else(): m.d.comb += temp.eq(self.inputa) with m.If(~self.PSW.H | (temp[:4] > 0x09)): m.d.comb += self.result.eq(temp - 0x06) m.d.comb += [ self._psw.N.eq(self.result & 0x80), self._psw.Z.eq(self.result == 0), ] if self.verification is Operation.DAS: with m.If(~Initial()): m.d.comb += [Assert(False)] # could be optimized with shift to right with m.Case(Operation.MUL): with m.Switch(self.count): for i in range(0, 8): with m.Case(i): prod = self.inputa * self.inputb[i] if i == 0: prod = Cat(prod[0:7], ~prod[7], Const(1)) elif i == 7: prod = Cat(~prod[0:7], prod[7], Const(1)) else: prod = Cat(prod[0:7], ~prod[7]) m.d.sync += self.partial.eq(self.partial + (prod << i)) m.d.sync += self.count.eq(i + 1) with m.Case(8): m.d.sync += self.partial_hi.eq(self.partial_lo) m.d.sync += self.count.eq(9) m.d.comb += [ self.result.eq(self.partial_hi), self._psw.N.eq(self.partial_hi.as_signed() < 0), self._psw.Z.eq(self.partial_hi == 0), ] with m.Case(9): m.d.sync += self.partial.eq(0) m.d.sync += self.count.eq(0) m.d.comb += [ self.result.eq(self.partial_hi), ] if self.verification is Operation.MUL: r = Signal(16) m.d.comb += [ r.eq(self.inputa.as_signed() * self.inputb.as_signed()), Cover(self.count == 9), ] with m.If(self.count == 9): m.d.comb += [ Assert(Past(self.result) == r[8:16]), Assert(self.result == r[0:8]), Assert(self._psw.N == r[15]), Assert(self._psw.V == self.PSW.V), Assert(self._psw.P == self.PSW.P), Assert(self._psw.B == self.PSW.B), Assert(self._psw.H == self.PSW.H), Assert(self._psw.I == self.PSW.I), Assert(self._psw.Z == ~(r[8:16].bool())), Assert(self._psw.C == self.PSW.C), ] with m.If(~Initial() & (self.count == 0)): m.d.comb += [ Assert(self.partial == 0), Assert((Past(self.count) == 0) | (Past(self.count) == 9)), ] with m.If(~Initial() & (self.count != 0)): m.d.comb += [ Assert(self.count == Past(self.count) + 1), Assume(self.inputa == Past(self.inputa)), Assume(self.inputb == Past(self.inputb)), ] with m.Case(Operation.DIV): over = Signal(reset=0) with m.Switch(self.count): with m.Case(0): m.d.sync += self.partial_hi.eq(self.inputa) # Y m.d.sync += self.count.eq(1) with m.Case(1): m.d.sync += self.partial_lo.eq(self.inputa) # A m.d.sync += self.count.eq(2) m.d.comb += self._psw.H.eq( Mux(self.partial_hi[0:4] >= self.inputb[0:4], 1, 0)) for i in range(2, 11): with m.Case(i): tmp1_w = Cat(self.partial << 1, over) tmp1_x = Signal(17) tmp1_y = Signal(17) tmp1_z = Signal(17) tmp2 = self.inputb << 9 m.d.comb += tmp1_x.eq(tmp1_w) with m.If(tmp1_w & 0x20000): m.d.comb += tmp1_x.eq((tmp1_w & 0x1FFFF) | 1) m.d.comb += tmp1_y.eq(tmp1_x) with m.If(tmp1_x >= tmp2): m.d.comb += tmp1_y.eq(tmp1_x ^ 1) m.d.comb += tmp1_z.eq(tmp1_y) with m.If(tmp1_y & 1): m.d.comb += tmp1_z.eq((tmp1_y - tmp2) & 0x1FFFF) m.d.sync += Cat(self.partial, over).eq(tmp1_z) m.d.sync += self.count.eq(i + 1) with m.Case(11): m.d.sync += self.count.eq(12) m.d.comb += [ self.result.eq( (Cat(self.partial, over) >> 9)), # Y % ] with m.Case(12): m.d.sync += self.partial.eq(0) m.d.sync += over.eq(0) m.d.sync += self.count.eq(0) m.d.comb += [ self.result.eq(self.partial_lo), # A / self._psw.N.eq(self.partial_lo.as_signed() < 0), self._psw.V.eq(over), self._psw.Z.eq(self.partial_lo == 0), ] if self.verification is Operation.DIV: m.d.comb += [ Cover(self.count == 12), ] with m.If(self.count == 12): m.d.comb += [Assert(False)] return m
def elaborate(self, platform): m = Module() wbuffer_layout = [("addr", 32), ("data", 32), ("sel", 4)] wbuffer_din = Record(wbuffer_layout) wbuffer_dout = Record(wbuffer_layout) dcache = m.submodules.dcache = Cache(nlines=self.nlines, nwords=self.nwords, nways=self.nways, start_addr=self.start_addr, end_addr=self.end_addr, enable_write=True) arbiter = m.submodules.arbiter = Arbiter() wbuffer = m.submodules.wbuffer = SyncFIFOBuffered( width=len(wbuffer_din), depth=self.nwords) wbuffer_port = arbiter.add_port(priority=0) cache_port = arbiter.add_port(priority=1) bare_port = arbiter.add_port(priority=2) x_use_cache = Signal() m_use_cache = Signal() m_data_w = Signal(32) m_byte_sel = Signal(4) bits_range = log2_int(self.end_addr - self.start_addr, need_pow2=False) m.d.comb += x_use_cache.eq( (self.x_addr[bits_range:] == (self.start_addr >> bits_range))) with m.If(~self.x_stall): m.d.sync += [ m_use_cache.eq(x_use_cache), m_data_w.eq(self.x_data_w), m_byte_sel.eq(self.x_byte_sel) ] m.d.comb += arbiter.bus.connect(self.dport) # -------------------------------------------------- # write buffer IO m.d.comb += [ # input wbuffer.w_data.eq(wbuffer_din), wbuffer.w_en.eq(x_use_cache & self.x_store & self.x_valid & ~self.x_stall), wbuffer_din.addr.eq(self.x_addr), wbuffer_din.data.eq(self.x_data_w), wbuffer_din.sel.eq(self.x_byte_sel), # output wbuffer_dout.eq(wbuffer.r_data), ] # drive the arbiter port with m.If(wbuffer_port.cyc): with m.If(wbuffer_port.ack | wbuffer_port.err): m.d.comb += wbuffer.r_en.eq(1) m.d.sync += wbuffer_port.stb.eq(0) with m.If(wbuffer.level == 1): # Buffer is empty m.d.sync += [wbuffer_port.cyc.eq(0), wbuffer_port.we.eq(0)] with m.Elif(~wbuffer_port.stb): m.d.sync += [ wbuffer_port.stb.eq(1), wbuffer_port.addr.eq(wbuffer_dout.addr), wbuffer_port.dat_w.eq(wbuffer_dout.data), wbuffer_port.sel.eq(wbuffer_dout.sel) ] with m.Elif(wbuffer.r_rdy): m.d.sync += [ wbuffer_port.cyc.eq(1), wbuffer_port.stb.eq(1), wbuffer_port.we.eq(1), wbuffer_port.addr.eq(wbuffer_dout.addr), wbuffer_port.dat_w.eq(wbuffer_dout.data), wbuffer_port.sel.eq(wbuffer_dout.sel) ] m.d.comb += wbuffer.r_en.eq(0) m.d.comb += [ wbuffer_port.cti.eq(CycleType.CLASSIC), wbuffer_port.bte.eq(0) ] # -------------------------------------------------- # connect IO: cache m.d.comb += [ dcache.s1_address.eq(self.x_addr), dcache.s1_flush.eq(0), dcache.s1_valid.eq(self.x_valid), dcache.s1_stall.eq(self.x_stall), dcache.s2_address.eq(self.m_addr), dcache.s2_evict.eq(0), # Evict is not used. Remove maybe? dcache.s2_valid.eq(self.m_valid), dcache.s2_re.eq(self.m_load), dcache.s2_wdata.eq(m_data_w), dcache.s2_sel.eq(m_byte_sel), dcache.s2_we.eq(self.m_store) ] # connect cache to arbiter m.d.comb += [ cache_port.addr.eq(dcache.bus_addr), cache_port.dat_w.eq(0), cache_port.sel.eq(0), cache_port.we.eq(0), cache_port.cyc.eq(dcache.bus_valid), cache_port.stb.eq(dcache.bus_valid), cache_port.cti.eq( Mux(dcache.bus_last, CycleType.END, CycleType.INCREMENT)), cache_port.bte.eq(log2_int(self.nwords) - 1), dcache.bus_data.eq(cache_port.dat_r), dcache.bus_ack.eq(cache_port.ack), dcache.bus_err.eq(cache_port.err) ] # -------------------------------------------------- # bare port rdata = Signal.like(bare_port.dat_r) op = Signal() m.d.comb += op.eq(self.x_load | self.x_store) # transaction logic with m.If(bare_port.cyc): with m.If(bare_port.ack | bare_port.err | ~self.m_valid): m.d.sync += [ rdata.eq(bare_port.dat_r), bare_port.we.eq(0), bare_port.cyc.eq(0), bare_port.stb.eq(0) ] with m.Elif(op & self.x_valid & ~self.x_stall & ~x_use_cache): m.d.sync += [ bare_port.addr.eq(self.x_addr), bare_port.dat_w.eq(self.x_data_w), bare_port.sel.eq(self.x_byte_sel), bare_port.we.eq(self.x_store), bare_port.cyc.eq(1), bare_port.stb.eq(1) ] m.d.comb += [bare_port.cti.eq(CycleType.CLASSIC), bare_port.bte.eq(0)] # -------------------------------------------------- # extra logic with m.If(self.x_fence_i): m.d.comb += self.x_busy.eq(wbuffer.r_rdy) with m.Elif(x_use_cache): m.d.comb += self.x_busy.eq(self.x_store & ~wbuffer.w_rdy) with m.Else(): m.d.comb += self.x_busy.eq(bare_port.cyc) with m.If(m_use_cache): m.d.comb += [ self.m_busy.eq(dcache.s2_re & dcache.s2_miss), self.m_load_data.eq(dcache.s2_rdata) ] with m.Elif(self.m_load_error | self.m_store_error): m.d.comb += [self.m_busy.eq(0), self.m_load_data.eq(0)] with m.Else(): m.d.comb += [ self.m_busy.eq(bare_port.cyc), self.m_load_data.eq(rdata) ] # -------------------------------------------------- # exceptions with m.If(self.dport.cyc & self.dport.err): m.d.sync += [ self.m_load_error.eq(~self.dport.we), self.m_store_error.eq(self.dport.we), self.m_badaddr.eq(self.dport.addr) ] with m.Elif(~self.m_stall): m.d.sync += [self.m_load_error.eq(0), self.m_store_error.eq(0)] return m
if args.instr is not None: instr = getattr(modules["instruction.implemented"], args.instr.split(".")[0]) instr = getattr(instr, args.instr.split(".")[1]) if instr not in implemented.implemented: raise AttributeError() m = Module() m.submodules.core = core = Core(instr) if instr is not None: time = Signal(6, reset_less=True) m.d.sync += time.eq(time + 1) with m.If(Initial()): m.d.sync += Assume(ResetSignal()) with m.Else(): m.d.sync += Assume(~ResetSignal()) # A time slot delayed because PC and addr need to sync with m.If(time == 2): m.d.sync += Assume(~core.snapshot.taken) with m.If(time == 3): m.d.sync += Cover(core.snapshot.taken) m.d.sync += Assume(core.snapshot.taken) m.d.sync += Cover(Fell(core.snapshot.taken)) main_runner( parser, args, m, ports=core.ports() + [ClockSignal(), ResetSignal()] ) else:
def elaborate(self, platform): m = Module() # Shortcuts. interface = self.interface out_stream = interface.tx new_frame = interface.tokenizer.new_frame targeting_ep_num = ( interface.tokenizer.endpoint == self._endpoint_number) targeting_us = targeting_ep_num & interface.tokenizer.is_in data_requested = targeting_us & interface.tokenizer.ready_for_response m.d.comb += [ self.data_requested.eq(data_requested), ] # Track our state in our transmission. bytes_left_in_frame = Signal.like(self.bytes_in_frame) bytes_left_in_packet = Signal(range(0, self._max_packet_size + 1), reset=self._max_packet_size - 1) next_data_pid = Signal(2) # Reset our state at the start of each frame. with m.If(new_frame): m.d.usb += [ # Latch in how many bytes we'll be transmitting this frame. bytes_left_in_frame.eq(self.bytes_in_frame), # And start with a full packet to transmit. bytes_left_in_packet.eq(self._max_packet_size) ] # If it'll take more than two packets to send our data, start off with DATA2. # We'll follow with DATA1 and DATA0. with m.If(self.bytes_in_frame > (2 * self._max_packet_size)): m.d.usb += next_data_pid.eq(2) # Otherwise, if we need two, start with DATA1. with m.Elif(self.bytes_in_frame > self._max_packet_size): m.d.usb += next_data_pid.eq(1) # Otherwise, we'll start (and end) with DATA0. with m.Else(): m.d.usb += next_data_pid.eq(0) m.d.comb += [ # Always pass our ``value`` directly through to our transmitter. # We'll provide ``address``/``next_address`` to our user code to help # orchestrate this timing. out_stream.payload.eq(self.value), # Provide our data pid through to to the transmitter. interface.tx_pid_toggle.eq(next_data_pid) ] m.d.usb += [ self.data_packet_starting.eq(0), ] m.d.comb += [ self.byte_advance.eq(out_stream.ready), ] # # Core sequencing FSM. # with m.FSM(domain="usb"): # IDLE -- the host hasn't yet requested data from our endpoint. with m.State("IDLE"): m.d.usb += [ # Remain targeting the first byte in our frame. self.address.eq(0), out_stream.first.eq(0) ] m.d.comb += self.next_address.eq(0) # Once the host requests a packet from us... with m.If(data_requested): # If we have data to send, send it. with m.If(bytes_left_in_frame): m.d.usb += out_stream.first.eq(1) m.d.usb += self.data_packet_starting.eq(1) m.next = "SEND_DATA" # Otherwise, we'll send a ZLP. with m.Else(): m.next = "SEND_ZLP" # SEND_DATA -- our primary data-transmission state; handles packet transmission with m.State("SEND_DATA"): last_byte_in_packet = (bytes_left_in_packet <= 1) last_byte_in_frame = (bytes_left_in_frame <= 1) byte_terminates_send = last_byte_in_packet | last_byte_in_frame m.d.comb += [ # Our data is always valid in this state... out_stream.valid.eq(1), # ... and we're terminating our packet if we're on the last byte of it. out_stream.last.eq(byte_terminates_send), ] # ``address`` should always move to the value presented in # ``next_address`` on each clock edge. m.d.usb += self.address.eq(self.next_address) # By default, don't advance. m.d.comb += self.next_address.eq(self.address) # We'll advance each time our data is accepted. with m.If(out_stream.ready): m.d.usb += out_stream.first.eq(0) # Mark the relevant byte as sent... m.d.usb += [ bytes_left_in_frame.eq(bytes_left_in_frame - 1), bytes_left_in_packet.eq(bytes_left_in_packet - 1), ] # ... and advance to the next address. m.d.comb += self.next_address.eq(self.address + 1) # If we've just completed transmitting a packet, or we've # just transmitted a full frame, end our transmission. with m.If(byte_terminates_send): m.d.usb += [ # Move to the next DATA pid, which is always one DATA PID less. # [USB2.0: 5.9.2]. We'll reset this back to its maximum value when # the next frame starts. next_data_pid.eq(next_data_pid - 1), # Mark our next packet as being a full one. bytes_left_in_packet.eq(self._max_packet_size) ] m.next = "IDLE" # SEND_ZLP -- sends a zero-length packet, and then return to idle. with m.State("SEND_ZLP"): # We'll request a ZLP by strobing LAST and VALID without strobing FIRST. m.d.comb += [ out_stream.valid.eq(1), out_stream.last.eq(1), ] m.next = "IDLE" return m
def elaborate(self, platform): m = Module() # Event timer: keeps track of the timing of each of the individual event phases. timer = Signal(range(0, self._CYCLES_3_MILLISECONDS + 1)) # Line state timer: keeps track of how long we've seen a line-state of interest; # other than a reset SE0. Used to track chirp and idle times. line_state_time = Signal(range(0, self._CYCLES_3_MILLISECONDS + 1)) # Valid pairs: keeps track of how make Chirp K / Chirp J sequences we've # seen, thus far. valid_pairs = Signal(range(0, 4)) # Tracks whether we were at high speed when we entered a suspend state. was_hs_pre_suspend = Signal() # By default, always count forward in time. # We'll reset the timer below when appropriate. m.d.usb += timer.eq(_generate_wide_incrementer(m, platform, timer)) m.d.usb += line_state_time.eq( _generate_wide_incrementer(m, platform, line_state_time)) # Signal that indicates when the bus is idle. # Our bus's IDLE condition depends on our active speed. bus_idle = Signal() # High speed busses present SE0 (which we see as SQUELCH'd) when idle [USB2.0: 7.1.1.3]. with m.If(self.current_speed == USBSpeed.HIGH): m.d.comb += bus_idle.eq( self.line_state == self._LINE_STATE_SQUELCH) # Full and low-speed busses see a 'J' state when idle, due to the device pull-up restistors. # (The line_state values for these are flipped between speeds.) [USB2.0: 7.1.7.4.1; USB2.0: Table 7-2]. with m.Elif(self.current_speed == USBSpeed.FULL): m.d.comb += bus_idle.eq( self.line_state == self._LINE_STATE_FS_HS_J) with m.Else(): m.d.comb += bus_idle.eq(self.line_state == self._LINE_STATE_LS_J) # # Core reset sequences. # with m.FSM(domain='usb'): # INITIALIZE -- we're immediately post-reset; we'll perform some minor setup with m.State('INITIALIZE'): # If we're working in low-speed mode, configure the PHY accordingly. with m.If(self.low_speed_only): m.d.usb += self.current_speed.eq(USBSpeed.LOW) m.next = 'LS_FS_NON_RESET' m.d.usb += [timer.eq(0), line_state_time.eq(0)] # LS_FS_NON_RESET -- we're currently operating at LS/FS and waiting for a reset; # the device could be active or inactive, but we haven't yet seen a reset condition. with m.State('LS_FS_NON_RESET'): # If we're seeing a state other than SE0 (D+ / D- at zero), this isn't yet a # potential reset. Keep our timer at zero. with m.If(self.line_state != self._LINE_STATE_SE0): m.d.usb += timer.eq(0) # If VBUS isn't connected, don't go through the whole reset process; # but also consider ourselves permanently in reset. This ensures we # don't progress through the reset FSM; but also ensures the device # state starts fresh with each plug. with m.If(~self.vbus_connected): m.d.usb += timer.eq(0) m.d.comb += self.bus_reset.eq(1) # If we see an SE0 for >2.5uS; < 3ms, this a bus reset. # We'll trigger a reset after 5uS; providing a little bit of timing flexibility. # [USB2.0: 7.1.7.5; ULPI 3.8.5.1]. with m.If(timer == self._CYCLES_5_MICROSECONDS): m.d.comb += self.bus_reset.eq(1) # If we're okay to run in high speed, we'll try to perform a high-speed detect. with m.If(~self.low_speed_only & ~self.full_speed_only): m.next = 'START_HS_DETECTION' # If we're seeing a state other than IDLE, clear our suspend timer. with m.If(~bus_idle): m.d.usb += line_state_time.eq(0) # If we see 3ms of consecutive line idle, we're being put into USB suspend. # We'll enter our suspended state, directly. [USB2.0: 7.1.7.6] with m.If(line_state_time == self._CYCLES_3_MILLISECONDS): m.d.usb += was_hs_pre_suspend.eq(0) m.next = 'SUSPENDED' # HS_NON_RESET -- we're currently operating at high speed and waiting for a reset or # suspend; the device could be active or inactive. with m.State('HS_NON_RESET'): # If we're seeing a state other than SE0 (D+ / D- at zero), this isn't yet a # potential reset. Keep our timer at zero. with m.If(self.line_state != self._LINE_STATE_SE0): m.d.usb += timer.eq(0) # If VBUS isn't connected, our device/host relationship is effectively # a blank state. We'll want to present our detection pull-up to the host, # so we'll drop out of high speed. with m.If(~self.vbus_connected): m.d.comb += self.bus_reset.eq(1) m.next = 'IS_LOW_OR_FULL_SPEED' # High speed signaling presents IDLE and RESET the same way: with the host # driving SE0; and us seeing SQUELCH. [USB2.0: 7.1.1.3; USB2.0: 7.1.7.6]. # Either way, our next step is the same: we'll drop down to full-speed. [USB2.0: 7.1.7.6] # Afterwards, we'll take steps to differentiate a reset from a suspend. with m.If(timer == self._CYCLES_3_MILLISECONDS): m.d.usb += [ timer.eq(0), self.current_speed.eq(USBSpeed.FULL), self.operating_mode.eq(UTMIOperatingMode.NORMAL), self.termination_select.eq( UTMITerminationSelect.LS_FS_NORMAL), ] m.next = 'DETECT_HS_SUSPEND' # If we see full-speed-only or low-speed-only being driven, switch # back to our LS/FS mode. with m.If(self.full_speed_only | self.low_speed_only): m.next = 'IS_LOW_OR_FULL_SPEED' # START_HS_DETECTION -- entry state for high-speed detection with m.State('START_HS_DETECTION'): m.d.usb += [ timer.eq(0), # Switch into High-speed chirp mode. Note that we'll need to leave our # terminations set to '1' until we're sure this is a high-speed host; # or the host will see our pull-up removal as a disconnect. self.current_speed.eq(USBSpeed.HIGH), self.operating_mode.eq(UTMIOperatingMode.CHIRP), self.termination_select.eq(UTMITerminationSelect.HS_CHIRP) ] m.next = 'PREPARE_FOR_CHIRP_0' # PREPARE_FOR_CHIRP_0 / PREPARE_FOR_CHIRP_1-- wait states; in which we give the PHY # time to the mode we'll need to drive our high-speed chirp. with m.State('PREPARE_FOR_CHIRP_0'): with m.If(~self.bus_busy): m.next = 'PREPARE_FOR_CHIRP_1' with m.State('PREPARE_FOR_CHIRP_1'): with m.If(~self.bus_busy): m.next = 'DEVICE_CHIRP' # DEVICE_CHIRP -- the device produces a 'chirp' K, which advertises to the host that # we're high speed capable. We'll provide that chirp K for around ~2ms. [USB2.0: 7.1.7.5] with m.State('DEVICE_CHIRP'): # Transmit a constant stream of 0's, which in this mode is a Chirp K. # Note that we don't need to check 'ready', as we care about the length # of time, rather than the number of bits. m.d.comb += [self.tx.valid.eq(1), self.tx.data.eq(0)] # Once 2ms have passed, we can stop our chirp, and begin waiting for the # hosts's response. We'll wait for Ready to be asserted to do so, to ensure # we don't change our values in the middle of a bit. with m.If((timer == self._CYCLES_2_MILLISECONDS)): m.d.usb += [timer.eq(0), valid_pairs.eq(0)] m.next = 'AWAIT_HOST_K' # AWAIT_HOST_K -- we've now completed the device chirp; and are waiting to see if the host # will respond with an alternating sequence of K's and J's. with m.State('AWAIT_HOST_K'): # If we don't see our response within 2.5ms, this isn't a compliant HS host. [USB2.0: 7.1.7.5]. # This is thus a full-speed host, and we'll act as a full-speed device. with m.If(timer == self._CYCLES_2P5_MILLISECONDS): m.next = 'IS_LOW_OR_FULL_SPEED' # Once we've seen our K, we're good to start observing J/K toggles. with m.If(self.line_state == self._LINE_STATE_FS_HS_K): m.next = 'IN_HOST_K' m.d.usb += line_state_time.eq(0) # IN_HOST_K: we're seeing a host Chirp K as part of our handshake; we'll # time it and see how long it lasts with m.State('IN_HOST_K'): # If we've exceeded our minimum chirp time, consider this a valid pattern # bit, # and advance in the pattern. with m.If(line_state_time == self._CYCLES_2P5_MICROSECONDS): m.next = 'AWAIT_HOST_J' # If our input has become something other than a K, then # we haven't finished our sequence. We'll go back to expecting a K. with m.If(self.line_state != self._LINE_STATE_FS_HS_K): m.next = 'AWAIT_HOST_K' # Time out if we exceed our maximum allowed duration. with m.If(timer == self._CYCLES_2P5_MILLISECONDS): m.next = 'IS_LOW_OR_FULL_SPEED' # AWAIT_HOST_J -- we're waiting for the next Chirp J in the host chirp sequence with m.State('AWAIT_HOST_J'): # If we've exceeded our maximum wait, this isn't a high speed host. with m.If(timer == self._CYCLES_2P5_MILLISECONDS): m.next = 'IS_LOW_OR_FULL_SPEED' # Once we've seen our J, start timing its duration. with m.If(self.line_state == self._LINE_STATE_FS_HS_J): m.next = 'IN_HOST_J' m.d.usb += line_state_time.eq(0) # IN_HOST_J: we're seeing a host Chirp K as part of our handshake; we'll # time it and see how long it lasts with m.State('IN_HOST_J'): # If we've exceeded our minimum chirp time, consider this a valid pattern # bit, and advance in the pattern. with m.If(line_state_time == self._CYCLES_2P5_MICROSECONDS): # If this would complete our third pair, this completes a handshake, # and we've identified a high speed host! with m.If(valid_pairs == 2): m.next = 'IS_HIGH_SPEED' # Otherwise, count the pair as valid, and wait for the next K. with m.Else(): m.d.usb += valid_pairs.eq(valid_pairs + 1) m.next = 'AWAIT_HOST_K' # If our input has become something other than a K, then # we haven't finished our sequence. We'll go back to expecting a K. with m.If(self.line_state != self._LINE_STATE_FS_HS_J): m.next = 'AWAIT_HOST_J' # Time out if we exceed our maximum allowed duration. with m.If(timer == self._CYCLES_2P5_MILLISECONDS): m.next = 'IS_LOW_OR_FULL_SPEED' # IS_HIGH_SPEED -- we've completed a high speed handshake, and are ready to # switch to high speed signaling with m.State('IS_HIGH_SPEED'): # Switch to high speed. m.d.usb += [ timer.eq(0), line_state_time.eq(0), self.current_speed.eq(USBSpeed.HIGH), self.operating_mode.eq(UTMIOperatingMode.NORMAL), self.termination_select.eq(UTMITerminationSelect.HS_NORMAL) ] m.next = 'HS_NON_RESET' # IS_LOW_OR_FULL_SPEED -- we've decided the device is low/full speed (typically # because it didn't) complete our high-speed handshake; set it up accordingly. with m.State('IS_LOW_OR_FULL_SPEED'): m.d.usb += [ self.operating_mode.eq(UTMIOperatingMode.NORMAL), self.termination_select.eq( UTMITerminationSelect.LS_FS_NORMAL) ] # If we're operating in low-speed only, drop down to low speed. with m.If(self.low_speed_only): m.d.usb += self.current_speed.eq(USBSpeed.LOW), # Otherwise, drop down to full speed. with m.Else(): m.d.usb += self.current_speed.eq(USBSpeed.FULL) # Once we know that our reset is complete, move back to our normal, non-reset state. with m.If(self.line_state != self._LINE_STATE_SE0): m.next = 'LS_FS_NON_RESET' m.d.usb += [timer.eq(0), line_state_time.eq(0)] # DETECT_HS_SUSPEND -- we were operating at high speed, and just detected an event # which is either a reset or a suspend event; we'll now detect which. with m.State('DETECT_HS_SUSPEND'): # We've just switch from HS signaling to FS signaling. # We'll wait a little while for the bus to settle, and then # check to see if it's settled to FS idle; or if we still see SE0. with m.If(timer == self._CYCLES_200_MICROSECONDS): m.d.usb += timer.eq(0) # If we've resume IDLE, this is suspend. Move to HS suspend. with m.If(self.line_state == self._LINE_STATE_FS_HS_J): m.d.usb += was_hs_pre_suspend.eq(1) m.next = 'SUSPENDED' # Otherwise, this is a reset (or, if K/SE1, we're very confused, and # should re-initialize anyway). Move to the HS reset detect sequence. with m.Else(): m.d.comb += self.bus_reset.eq(1) m.next = 'START_HS_DETECTION' # SUSPEND -- our device has entered USB suspend; we'll now wait for either a # resume or a reset with m.State('SUSPENDED'): m.d.comb += self.suspended.eq(1) # If we see a K state, then we're being resumed. is_ls_k = self.low_speed_only & (self.line_state == self._LINE_STATE_LS_K) is_fs_k = ~self.low_speed_only & (self.line_state == self._LINE_STATE_FS_HS_K) with m.If(is_ls_k | is_fs_k): m.d.usb += timer.eq(0) # If we were in high-speed pre-suspend, then resume being in HS. with m.If(was_hs_pre_suspend): m.next = 'IS_HIGH_SPEED' # Otherwise, just resume. with m.Else(): m.next = 'LS_FS_NON_RESET' m.d.usb += [timer.eq(0), line_state_time.eq(0)] # If this isn't an SE0, we're not receiving a reset request. # Keep our reset counter at zero. with m.If(self.line_state != self._LINE_STATE_SE0): m.d.usb += timer.eq(0) # If we see an SE0 for > 2.5uS, this is a reset request. [USB 2.0: 7.1.7.5] # We'll handle it directly from suspend. with m.If(timer == self._CYCLES_2P5_MICROSECONDS): m.d.comb += self.bus_reset.eq(1) m.d.usb += timer.eq(0) # If we're limited to LS or FS, move to the appropriate state. with m.If(self.low_speed_only | self.full_speed_only): m.next = 'LS_FS_NON_RESET' m.d.usb += [timer.eq(0), line_state_time.eq(0)] # Otherwise, this could be a high-speed device; enter its reset. with m.Else(): m.next = 'START_HS_DETECTION' return m
def elaborate(self, platform): m = Module() # add 1 MHZ clock domain cntr = Signal(range(self.divider)) # pos max_bits = (self.max_steps << BIT_SHIFT).bit_length() cntrs = Array( Signal(signed(max_bits + 1)) for _ in range(len(self.coeff))) assert max_bits <= 64 ticks = Signal(MOVE_TICKS.bit_length()) if self.top: steppers = [res for res in get_all_resources(platform, "stepper")] assert len(steppers) != 0 for idx, stepper in enumerate(steppers): m.d.comb += [ stepper.step.eq(self.step[idx]), stepper.dir.eq(self.dir[idx]) ] else: self.ticks = ticks self.cntrs = cntrs # steps for motor in range(self.motors): m.d.comb += [ self.step[motor].eq(cntrs[motor * self.order][BIT_SHIFT]), self.totalsteps[motor].eq(cntrs[motor * self.order] >> (BIT_SHIFT + 1)) ] # directions counter_d = Array( Signal(signed(max_bits + 1)) for _ in range(self.motors)) for motor in range(self.motors): m.d.sync += counter_d[motor].eq(cntrs[motor * self.order]) # negative case --> decreasing with m.If(counter_d[motor] > cntrs[motor * self.order]): m.d.sync += self.dir[motor].eq(0) # positive case --> increasing with m.Elif(counter_d[motor] < cntrs[motor * self.order]): m.d.sync += self.dir[motor].eq(1) with m.FSM(reset='RESET', name='polynomen'): with m.State('RESET'): m.next = 'WAIT_START' m.d.sync += self.busy.eq(0) with m.State('WAIT_START'): m.d.sync += self.busy.eq(0) with m.If(self.start): for motor in range(self.motors): coef0 = motor * self.order m.d.sync += [ cntrs[coef0 + 2].eq(0), cntrs[coef0 + 1].eq(0), cntrs[coef0].eq(0), counter_d[motor].eq(0) ] m.d.sync += self.busy.eq(1) m.next = 'RUNNING' with m.State('RUNNING'): with m.If((ticks < self.ticklimit) & (cntr >= self.divider - 1)): m.d.sync += [ticks.eq(ticks + 1), cntr.eq(0)] for motor in range(self.motors): idx = motor * self.order op3 = 3 * 2 * self.coeff[idx + 2] + cntrs[idx + 2] op2 = (cntrs[idx + 2] + 2 * self.coeff[idx + 1] + cntrs[idx + 1]) op1 = (self.coeff[idx + 2] + self.coeff[idx + 1] + self.coeff[idx] + cntrs[idx + 2] + cntrs[idx + 1] + cntrs[idx]) m.d.sync += [ cntrs[idx + 2].eq(op3), cntrs[idx + 1].eq(op2), cntrs[idx].eq(op1) ] with m.Elif(ticks < self.ticklimit): m.d.sync += cntr.eq(cntr + 1) with m.Else(): m.d.sync += ticks.eq(0) m.next = 'WAIT_START' return m
def elaborate(self, platform): m = Module() # Baud generator. baud_counter = Signal(range(0, self.divisor)) # Tx shift register; holds our data, a start, and a stop bit. bits_per_frame = len(self.data) + 2 data_shift = Signal(bits_per_frame) bits_to_send = Signal(range(0, len(data_shift))) # Create an internal signal equal to our input data framed with a start/stop bit. framed_data_in = Cat(self.START_BIT, self.data, self.STOP_BIT) # Idle our strobes low unless asserted. m.d.sync += [ self.accepted .eq(0) ] with m.FSM() as f: m.d.comb += self.idle.eq(f.ongoing('IDLE')) # IDLE: transmitter is waiting for input with m.State("IDLE"): m.d.comb += self.tx.eq(1) # idle high # Once we get a send request, fill in our shift register, and start shifting. with m.If(self.send): m.d.sync += [ baud_counter .eq(self.divisor - 1), bits_to_send .eq(len(data_shift) - 1), data_shift .eq(framed_data_in), self.accepted .eq(1) ] m.next = "TRANSMIT" # TRANSMIT: actively shift out start/data/stop with m.State("TRANSMIT"): m.d.comb += self.tx .eq(data_shift[0]) m.d.sync += baud_counter .eq(baud_counter - 1) # If we've finished a bit period... with m.If(baud_counter == 0): m.d.sync += baud_counter.eq(self.divisor - 1) # ... if we have bits left to send, move to the next one. with m.If(bits_to_send > 0): m.d.sync += [ bits_to_send .eq(bits_to_send - 1), data_shift .eq(data_shift[1:]) ] # Otherwise, complete the frame. with m.Else(): # If we still have data to send, move to the next byte... with m.If(self.send): m.d.sync += [ bits_to_send .eq(bits_per_frame - 1), data_shift .eq(framed_data_in), self.accepted .eq(1) ] # ... otherwise, move to our idle state. with m.Else(): m.next = "IDLE" return m
def elaborate(self, platform): m = Module() way_layout = [ ('data', 32 * self.nwords), ('tag', self.s1_address.tag.shape()), ('valid', 1), ('sel_lru', 1) ] if self.enable_write: way_layout.append(('sel_we', 1)) ways = Array(Record(way_layout) for _way in range(self.nways)) fill_cnt = Signal.like(self.s1_address.offset) # set the LRU if self.nways == 1: lru = Const(0) # self.nlines else: lru = Signal(self.nlines) with m.If(self.bus_valid & self.bus_ack & self.bus_last): # err ^ ack == 1 _lru = lru.bit_select(self.s2_address.line, 1) m.d.sync += lru.bit_select(self.s2_address.line, 1).eq(~_lru) # hit/miss way_hit = m.submodules.way_hit = Encoder(self.nways) for idx, way in enumerate(ways): m.d.comb += way_hit.i[idx].eq((way.tag == self.s2_address.tag) & way.valid) m.d.comb += self.s2_miss.eq(way_hit.n) if self.enable_write: m.d.comb += ways[way_hit.o].sel_we.eq(self.s2_we & self.s2_valid) # read data m.d.comb += self.s2_rdata.eq(ways[way_hit.o].data.word_select(self.s2_address.offset, 32)) with m.FSM(): with m.State('READ'): with m.If(self.s2_re & self.s2_miss & self.s2_valid): m.d.sync += [ self.bus_addr.eq(self.s2_address), # WARNING extra_bits self.bus_valid.eq(1), fill_cnt.eq(self.s2_address.offset - 1) ] m.next = 'REFILL' with m.State('REFILL'): m.d.comb += self.bus_last.eq(fill_cnt == self.bus_addr.offset) with m.If(self.bus_ack): m.d.sync += self.bus_addr.offset.eq(self.bus_addr.offset + 1) with m.If(self.bus_ack & self.bus_last | self.bus_err): m.d.sync += self.bus_valid.eq(0) with m.If(~self.bus_valid | self.s1_flush): # in case of flush, abort ongoing refill. m.next = 'READ' m.d.sync += self.bus_valid.eq(0) # mark the way to use (replace) m.d.comb += ways[lru.bit_select(self.s2_address.line, 1)].sel_lru.eq(self.bus_valid) # generate for N ways for way in ways: # create the memory structures for valid, tag and data. valid = Signal(self.nlines) tag_m = Memory(width=len(way.tag), depth=self.nlines) tag_rp = tag_m.read_port() tag_wp = tag_m.write_port() m.submodules += tag_rp, tag_wp data_m = Memory(width=len(way.data), depth=self.nlines) data_rp = data_m.read_port() data_wp = data_m.write_port(granularity=32) m.submodules += data_rp, data_wp # handle valid with m.If(self.s1_flush & self.s1_valid): # flush m.d.sync += valid.eq(0) with m.Elif(way.sel_lru & self.bus_last & self.bus_ack): # refill ok m.d.sync += valid.bit_select(self.bus_addr.line, 1).eq(1) with m.Elif(way.sel_lru & self.bus_err): # refill error m.d.sync += valid.bit_select(self.bus_addr.line, 1).eq(0) with m.Elif(self.s2_evict & self.s2_valid & (way.tag == self.s2_address.tag)): # evict m.d.sync += valid.bit_select(self.s2_address.line, 1).eq(0) # assignments m.d.comb += [ tag_rp.addr.eq(Mux(self.s1_stall, self.s2_address.line, self.s1_address.line)), tag_wp.addr.eq(self.bus_addr.line), tag_wp.data.eq(self.bus_addr.tag), tag_wp.en.eq(way.sel_lru & self.bus_ack & self.bus_last), data_rp.addr.eq(Mux(self.s1_stall, self.s2_address.line, self.s1_address.line)), way.data.eq(data_rp.data), way.tag.eq(tag_rp.data), way.valid.eq(valid.bit_select(self.s2_address.line, 1)) ] # update cache: CPU or Refill if self.enable_write: update_addr = Signal(len(data_wp.addr)) update_data = Signal(len(data_wp.data)) update_we = Signal(len(data_wp.en)) aux_wdata = Signal(32) with m.If(self.bus_valid): m.d.comb += [ update_addr.eq(self.bus_addr.line), update_data.eq(Repl(self.bus_data, self.nwords)), update_we.bit_select(self.bus_addr.offset, 1).eq(way.sel_lru & self.bus_ack), ] with m.Else(): m.d.comb += [ update_addr.eq(self.s2_address.line), update_data.eq(Repl(aux_wdata, self.nwords)), update_we.bit_select(self.s2_address.offset, 1).eq(way.sel_we & ~self.s2_miss) ] m.d.comb += [ aux_wdata.eq(Cat( Mux(self.s2_sel[0], self.s2_wdata.word_select(0, 8), self.s2_rdata.word_select(0, 8)), Mux(self.s2_sel[1], self.s2_wdata.word_select(1, 8), self.s2_rdata.word_select(1, 8)), Mux(self.s2_sel[2], self.s2_wdata.word_select(2, 8), self.s2_rdata.word_select(2, 8)), Mux(self.s2_sel[3], self.s2_wdata.word_select(3, 8), self.s2_rdata.word_select(3, 8)) )), # data_wp.addr.eq(update_addr), data_wp.data.eq(update_data), data_wp.en.eq(update_we), ] else: m.d.comb += [ data_wp.addr.eq(self.bus_addr.line), data_wp.data.eq(Repl(self.bus_data, self.nwords)), data_wp.en.bit_select(self.bus_addr.offset, 1).eq(way.sel_lru & self.bus_ack), ] return m
def elaborate(self, platform): m = Module() clk_60MHz = platform.request(platform.default_clk) # Drive the sync clock domain with our main clock. m.domains.sync = ClockDomain() m.d.comb += ClockSignal().eq(clk_60MHz) # Create a set of registers, and expose them over SPI. board_spi = platform.request("debug_spi") spi_registers = SPIRegisterInterface(default_read_value=-1) m.submodules.spi_registers = spi_registers # Identify ourselves as the SPI flash bridge. spi_registers.add_read_only_register(REGISTER_ID, read=0x53504946) # # For now, keep resources on our right-side I/O network used. # platform.request("target_phy") # # SPI flash passthrough connections. # flash_sdo = Signal() spi_flash_bus = platform.request('spi_flash') spi_flash_passthrough = ECP5ConfigurationFlashInterface( bus=spi_flash_bus) m.submodules += spi_flash_passthrough m.d.comb += [ spi_flash_passthrough.sck.eq(board_spi.sck), spi_flash_passthrough.sdi.eq(board_spi.sdi), flash_sdo.eq(spi_flash_passthrough.sdo), ] # # Structural connections. # sck = Signal() sdi = Signal() cs = Signal() gateware_sdo = Signal() # # Synchronize each of our I/O SPI signals, where necessary. # m.submodules += FFSynchronizer(board_spi.sck, sck) m.submodules += FFSynchronizer(board_spi.sdi, sdi) m.submodules += FFSynchronizer(board_spi.cs, cs) # Select the passthrough or gateware SPI based on our chip-select values. with m.If(spi_registers.cs): m.d.comb += board_spi.sdo.eq(gateware_sdo) with m.Else(): m.d.comb += board_spi.sdo.eq(flash_sdo) # Connect our register interface to our board SPI. m.d.comb += [ spi_registers.sck.eq(sck), spi_registers.sdi.eq(sdi), gateware_sdo.eq(spi_registers.sdo), spi_registers.cs.eq(cs) ] return m
def elaborate(self, platform): m = Module() # # Transciever state. # # Handle our PID-sequence reset. # Note that we store the _inverse_ of our data PID, as we'll toggle our DATA PID # before sending. with m.If(self.reset_sequence): m.d.usb += self.data_pid.eq(~self.start_with_data1) # # Transmit buffer. # # Our USB connection imposed a few requirements on our stream: # 1) we must be able to transmit packets at a full rate; i.e. # must be asserted from the start to the end of our transfer; and # 2) we must be able to re-transmit data if a given packet is not ACK'd. # # Accordingly, we'll buffer a full USB packet of data, and then transmit # it once either a) our buffer is full, or 2) the transfer ends (last=1). # # This implementation is double buffered; so a buffer fill can be pipelined # with a transmit. # # We'll create two buffers; so we can fill one as we empty the other. # Since each buffer will be used for every other transaction, and our PID toggle flips every other transcation, # we'll identify which buffer we're targeting by the current PID toggle. buffer = Array(Memory(width=8, depth=self._max_packet_size, name=f"transmit_buffer_{i}") for i in range(2)) buffer_write_ports = Array(buffer[i].write_port(domain="usb") for i in range(2)) buffer_read_ports = Array(buffer[i].read_port(domain="usb") for i in range(2)) m.submodules.read_port_0, m.submodules.read_port_1 = buffer_read_ports m.submodules.write_port_0, m.submodules.write_port_1 = buffer_write_ports # Create values equivalent to the buffer numbers for our read and write buffer; which switch # whenever we swap our two buffers. write_buffer_number = self.data_pid[0] read_buffer_number = ~self.data_pid[0] # Create a shorthand that refers to the buffer to be filled; and the buffer to send from. # We'll call these the Read and Write buffers. buffer_write = buffer_write_ports[write_buffer_number] buffer_read = buffer_read_ports[read_buffer_number] # Buffer state tracking: # - Our ``fill_count`` keeps track of how much data is stored in a given buffer. # - Our ``stream_ended`` bit keeps track of whether the stream ended while filling up # the given buffer. This indicates that the buffer cannot be filled further; and, when # ``generate_zlps`` is enabled, is used to determine if the given buffer should end in # a short packet; which determines whether ZLPs are emitted. buffer_fill_count = Array(Signal(range(0, self._max_packet_size + 1)) for _ in range(2)) buffer_stream_ended = Array(Signal(name=f"strem_ended_in_buffer{i}") for i in range(2)) # Create shortcuts to active fill_count / stream_ended signals for the buffer being written. write_fill_count = buffer_fill_count[write_buffer_number] write_stream_ended = buffer_stream_ended[write_buffer_number] # Create shortcuts to the fill_count / stream_ended signals for the packet being sent. read_fill_count = buffer_fill_count[read_buffer_number] read_stream_ended = buffer_stream_ended[read_buffer_number] # Keep track of our current send position; which determines where we are in the packet. send_position = Signal(range(0, self._max_packet_size + 1)) # Shortcut names. in_stream = self.transfer_stream out_stream = self.packet_stream # Use our memory's two ports to capture data from our transfer stream; and two emit packets # into our packet stream. Since we'll never receive to anywhere else, or transmit to anywhere else, # we can just unconditionally connect these. m.d.comb += [ # We'll only ever -write- data from our input stream... buffer_write_ports[0].data .eq(in_stream.payload), buffer_write_ports[0].addr .eq(write_fill_count), buffer_write_ports[1].data .eq(in_stream.payload), buffer_write_ports[1].addr .eq(write_fill_count), # ... and we'll only ever -send- data from the Read buffer. buffer_read.addr .eq(send_position), out_stream.payload .eq(buffer_read.data), # We're ready to receive data iff we have space in the buffer we're currently filling. in_stream.ready .eq((write_fill_count != self._max_packet_size) & ~write_stream_ended), buffer_write.en .eq(in_stream.valid & in_stream.ready) ] # Increment our fill count whenever we accept new data. with m.If(buffer_write.en): m.d.usb += write_fill_count.eq(write_fill_count + 1) # If the stream ends while we're adding data to the buffer, mark this as an ended stream. with m.If(in_stream.last & buffer_write.en): m.d.usb += write_stream_ended.eq(1) # Shortcut for when we need to deal with an in token. # Pulses high an interpacket delay after receiving an IN token. in_token_received = self.active & self.tokenizer.is_in & self.tokenizer.ready_for_response with m.FSM(domain='usb'): # WAIT_FOR_DATA -- We don't yet have a full packet to transmit, so we'll capture data # to fill the our buffer. At full throughput, this state will never be reached after # the initial post-reset fill. with m.State("WAIT_FOR_DATA"): # We can't yet send data; so NAK any packet requests. m.d.comb += self.handshakes_out.nak.eq(in_token_received) # If we have valid data that will end our packet, we're no longer waiting for data. # We'll now wait for the host to request data from us. packet_complete = (write_fill_count + 1 == self._max_packet_size) will_end_packet = packet_complete | in_stream.last with m.If(in_stream.valid & will_end_packet): # If we've just finished a packet, we now have data we can send! with m.If(packet_complete | in_stream.last): m.next = "WAIT_TO_SEND" m.d.usb += [ # We're now ready to take the data we've captured and _transmit_ it. # We'll swap our read and write buffers, and toggle our data PID. self.data_pid[0].eq(~self.data_pid[0]) ] # WAIT_TO_SEND -- we now have at least a buffer full of data to send; we'll # need to wait for an IN token to send it. with m.State("WAIT_TO_SEND"): m.d.usb += send_position .eq(0), # Once we get an IN token, move to sending a packet. with m.If(in_token_received): # If we have a packet to send, send it. with m.If(read_fill_count): m.next = "SEND_PACKET" m.d.usb += out_stream.first .eq(1) # Otherwise, we entered a transmit path without any data in the buffer. with m.Else(): m.d.comb += [ # Send a ZLP... out_stream.valid .eq(1), out_stream.last .eq(1), ] # ... and clear the need to follow up with one, since we've just sent a short packet. m.d.usb += read_stream_ended.eq(0) m.next = "WAIT_FOR_ACK" with m.State("SEND_PACKET"): last_packet = (send_position + 1 == read_fill_count) m.d.comb += [ # We're always going to be sending valid data, since data is always # available from our memory. out_stream.valid .eq(1), # Let our transmitter know when we've reached our last packet. out_stream.last .eq(last_packet) ] # Once our transmitter accepts our data... with m.If(out_stream.ready): m.d.usb += [ # ... move to the next byte in our packet ... send_position .eq(send_position + 1), # ... and mark our packet as no longer the first. out_stream.first .eq(0) ] # Move our memory pointer to its next position. m.d.comb += buffer_read.addr .eq(send_position + 1), # If we've just sent our last packet, we're now ready to wait for a # response from our host. with m.If(last_packet): m.next = 'WAIT_FOR_ACK' # WAIT_FOR_ACK -- We've just sent a packet; but don't know if the host has # received it correctly. We'll wait to see if the host ACKs. with m.State("WAIT_FOR_ACK"): # If the host does ACK... with m.If(self.handshakes_in.ack): # ... clear the data we've sent from our buffer. m.d.usb += read_fill_count.eq(0) # Figure out if we'll need to follow up with a ZLP. If we have ZLP generation enabled, # we'll make sure we end on a short packet. If this is max-packet-size packet _and_ our # transfer ended with this packet; we'll need to inject a ZLP. follow_up_with_zlp = \ self.generate_zlps & (read_fill_count == self._max_packet_size) & read_stream_ended # If we're following up with a ZLP, move back to our "wait to send" state. # Since we've now cleared our fill count; this next go-around will emit a ZLP. with m.If(follow_up_with_zlp): m.next = "WAIT_TO_SEND" # Otherwise, there's a possibility we already have a packet-worth of data waiting # for us in our "write buffer", which we've been filling in the background. # If this is the case, we'll flip which buffer we're working with, toggle our data pid, # and then ready ourselves for transmit. packet_completing = in_stream.valid & (write_fill_count + 1 == self._max_packet_size) with m.Elif(~in_stream.ready | packet_completing): m.next = "WAIT_TO_SEND" m.d.usb += self.data_pid[0].eq(~self.data_pid[0]) # If neither of the above conditions are true; we now don't have enough data to send. # We'll wait for enough data to transmit. with m.Else(): m.next = "WAIT_FOR_DATA" # If the host starts a new packet without ACK'ing, we'll need to retransmit. # We'll move back to our "wait for token" state without clearing our buffer. with m.If(self.tokenizer.new_token): m.next = 'WAIT_TO_SEND' return m
def elaborate(self, platform): m = Module() # Range shortcuts for internal signals. address_range = range(0, self.depth + 1) # # Core internal "backing store". # memory = Memory(width=self.width, depth=self.depth + 1, name=self.name) m.submodules.read_port = read_port = memory.read_port() m.submodules.write_port = write_port = memory.write_port() # Always connect up our memory's data/en ports to ours. m.d.comb += [ self.read_data .eq(read_port.data), write_port.data .eq(self.write_data), write_port.en .eq(self.write_en & ~self.full) ] # # Write port. # # We'll track two pieces of data: our _committed_ write position, and our current un-committed write one. # This will allow us to rapidly backtrack to our pre-commit position. committed_write_pointer = Signal(address_range) current_write_pointer = Signal(address_range) m.d.comb += write_port.addr.eq(current_write_pointer) # If we're writing to the fifo, update our current write position. with m.If(self.write_en & ~self.full): with m.If(current_write_pointer == self.depth): m.d.sync += current_write_pointer.eq(0) with m.Else(): m.d.sync += current_write_pointer.eq(current_write_pointer + 1) # If we're committing a FIFO write, update our committed position. with m.If(self.write_commit): m.d.sync += committed_write_pointer.eq(current_write_pointer) # If we're discarding our current write, reset our current position, with m.If(self.write_discard): m.d.sync += current_write_pointer.eq(committed_write_pointer) # # Read port. # # We'll track two pieces of data: our _committed_ read position, and our current un-committed read one. # This will allow us to rapidly backtrack to our pre-commit position. committed_read_pointer = Signal(address_range) current_read_pointer = Signal(address_range) # Compute the location for the next read, accounting for wraparound. We'll not assume a binary-sized # buffer; so we'll compute the wraparound manually. next_read_pointer = Signal.like(current_read_pointer) with m.If(current_read_pointer == self.depth): m.d.comb += next_read_pointer.eq(0) with m.Else(): m.d.comb += next_read_pointer.eq(current_read_pointer + 1) # Our memory always takes a single cycle to provide its read output; so we'll update its address # "one cycle in advance". Accordingly, if we're about to advance the FIFO, we'll use the next read # address as our input. If we're not, we'll use the current one. with m.If(self.read_en & ~self.empty): m.d.comb += read_port.addr.eq(next_read_pointer) with m.Else(): m.d.comb += read_port.addr.eq(current_read_pointer) # If we're reading from our the fifo, update our current read position. with m.If(self.read_en & ~self.empty): m.d.sync += current_read_pointer.eq(next_read_pointer) # If we're committing a FIFO write, update our committed position. with m.If(self.read_commit): m.d.sync += committed_read_pointer.eq(current_read_pointer) # If we're discarding our current write, reset our current position, with m.If(self.read_discard): m.d.sync += current_read_pointer.eq(committed_read_pointer) # # FIFO status. # # Our FIFO is empty if our read and write pointers are in the same. We'll use the current # read position (which leads ahead) and the committed write position (which lags behind). m.d.comb += self.empty.eq(current_read_pointer == committed_write_pointer) # For our space available, we'll use the current write position (which leads ahead) and our committed # read position (which lags behind). This yields two cases: one where the buffer isn't wrapped around, # and one where it is. with m.If(self.full): m.d.comb += self.space_available.eq(0) with m.Elif(committed_read_pointer <= current_write_pointer): m.d.comb += self.space_available.eq(self.depth - (current_write_pointer - committed_read_pointer)) with m.Else(): m.d.comb += self.space_available.eq(committed_read_pointer - current_write_pointer - 1) # Our FIFO is full if we don't have any space available. m.d.comb += self.full.eq(current_write_pointer + 1 == committed_read_pointer) # If we're not supposed to be in the sync domain, rename our sync domain to the target. if self.domain != "sync": m = DomainRenamer({"sync": self.domain})(m) return m
def elaborate(self, _: Platform) -> Module: """Implements the logic of the interrupt card.""" m = Module() m.d.comb += [ self._pend_mti.eq(0), self._pend_mei.eq(0), self._clear_pend_mti.eq(0), self._clear_pend_mei.eq(0), ] with m.If(~self.trap): with m.If(self._mstatus[MStatus.MIE]): with m.If(self._mie[MInterrupt.MTI]): with m.If(~self._mip[MInterrupt.MTI]): m.d.comb += self._pend_mti.eq(self.time_irq) with m.Else(): m.d.comb += self._clear_pend_mti.eq(1) with m.If(self._mie[MInterrupt.MEI]): with m.If(~self._mip[MInterrupt.MEI]): m.d.comb += self._pend_mei.eq(self.ext_irq) with m.Else(): m.d.comb += self._clear_pend_mei.eq(1) with m.Else(): m.d.comb += self._clear_pend_mti.eq(1) m.d.comb += self._clear_pend_mei.eq(1) m.d.comb += self.mei_pend.eq(self._mip[MInterrupt.MEI]) m.d.comb += self.mti_pend.eq(self._mip[MInterrupt.MTI]) enter_trap_mstatus = self._mstatus enter_trap_mstatus &= ~(1 << MStatus.MIE) # clear MIE enter_trap_mstatus &= ~(1 << MStatus.MPIE) # clear MPIE enter_trap_mstatus |= (self._mstatus[MStatus.MIE] << MStatus.MPIE ) # set MPIE exit_trap_mstatus = self._mstatus exit_trap_mstatus |= (1 << MStatus.MPIE) # set MPIE exit_trap_mstatus &= ~(1 << MStatus.MIE) # clear MIE exit_trap_mstatus |= (self._mstatus[MStatus.MPIE] << MStatus.MIE ) # set MIE self.multiplex_to_reg(m, clk="ph2w", reg=self._mstatus, sels=[ self.z_to_csr & (self.csr_num == CSRAddr.MSTATUS), self.enter_trap, self.exit_trap, ], sigs=[ self.data_z_in, enter_trap_mstatus, exit_trap_mstatus, ], ext_init=self._ext_init) self.multiplex_to_reg(m, clk="ph2w", reg=self._mie, sels=[ self.z_to_csr & (self.csr_num == CSRAddr.MIE), ], sigs=[self.data_z_in], ext_init=self._ext_init) mtip = Signal() meip = Signal() m.d.comb += mtip.eq(self._mip[MInterrupt.MTI]) m.d.comb += meip.eq(self._mip[MInterrupt.MEI]) with m.If(self._pend_mti): m.d.comb += mtip.eq(1) with m.Elif(self._clear_pend_mti | self.clear_pend_mti): m.d.comb += mtip.eq(0) with m.If(self._pend_mei): m.d.comb += meip.eq(1) with m.Elif(self._clear_pend_mei | self.clear_pend_mei): m.d.comb += meip.eq(0) # Pending machine interrupts are not writable. mip_load = Signal(32) m.d.comb += mip_load.eq(self.data_z_in) m.d.comb += mip_load[MInterrupt.MTI].eq(mtip) m.d.comb += mip_load[MInterrupt.MEI].eq(meip) m.d.comb += mip_load[MInterrupt.MSI].eq(self._mip[MInterrupt.MSI]) load_mip = self.z_to_csr & (self.csr_num == CSRAddr.MIP) mip_pend = Signal(32) m.d.comb += mip_pend.eq(self._mip) m.d.comb += mip_pend[MInterrupt.MTI].eq(mtip) m.d.comb += mip_pend[MInterrupt.MEI].eq(meip) # Either we load MIP, or set/clear/retain bits in MIP. We never have # to leave MIP alone. self.multiplex_to_reg(m, clk="ph2w", reg=self._mip, sels=[load_mip, ~load_mip], sigs=[mip_load, mip_pend]) self.multiplex_to_bus( m, bus=self.data_x_out, sels=[ self.csr_to_x & (self.csr_num == CSRAddr.MSTATUS), self.csr_to_x & (self.csr_num == CSRAddr.MIE), self.csr_to_x & (self.csr_num == CSRAddr.MIP), ], sigs=[ self._mstatus, self._mie, self._mip, ]) return m
def elaborate(self, platform): m = Module() cpu = Core() m.submodules += cpu m.domains.ph1 = ph1 = ClockDomain("ph1") m.domains.ph2 = ph2 = ClockDomain("ph2", clk_edge="neg") # Hook up clocks and reset to pins if SLOWCLK: clk_freq = platform.default_clk_frequency timer = Signal(range(0, int(clk_freq // 2)), reset=int(clk_freq // 2) - 1) tick = Signal() sync = ClockDomain() with m.If(timer == 0): m.d.sync += timer.eq(timer.reset) m.d.sync += tick.eq(~tick) with m.Else(): m.d.sync += timer.eq(timer - 1) m.d.comb += [ ph1.rst.eq(sync.rst), ph2.rst.eq(sync.rst), ph1.clk.eq(tick), ph2.clk.eq(~tick), ] # Hook up address lines to pins addr = [] for i in range(16): pin = platform.request("addr", i) m.d.comb += pin.o.eq(cpu.Addr[i]) addr.append(pin) data = [] if not FAKEMEM: # Hook up data in/out + direction to pins for i in range(8): pin = platform.request("data", i) m.d.comb += pin.o.eq(cpu.Dout[i]) m.d.ph2 += cpu.Din[i].eq(pin.i) data.append(pin) if FAKEMEM: with m.Switch(cpu.Addr): for a, d in mem.items(): with m.Case(a): m.d.comb += cpu.Din.eq(d) with m.Default(): m.d.comb += cpu.Din.eq(0x00) for i in range(8): pin = platform.request("led", i) m.d.comb += pin.o.eq(cpu.Addr[i]) rw = platform.request("rw") m.d.comb += rw.o.eq(cpu.RW) nIRQ = platform.request("n_irq") nNMI = platform.request("n_nmi") m.d.ph2 += cpu.IRQ.eq(~nIRQ) m.d.ph2 += cpu.NMI.eq(~nNMI) ba = platform.request("ba") m.d.comb += ba.o.eq(cpu.BA) m.d.comb += rw.oe.eq(~cpu.BA) for i in range(len(addr)): m.d.comb += addr[i].oe.eq(~cpu.BA) for i in range(len(data)): m.d.comb += data[i].oe.eq(~cpu.BA & ~cpu.RW) return m
def elaborate(self, platform: Platform) -> Module: m = Module() m.domains.ramg = ClockDomain(async_reset=True, local=True) m.domains.romb0 = ClockDomain(async_reset=True, local=True) m.domains.romb1 = ClockDomain(async_reset=True, local=True) m.domains.ramb = ClockDomain(async_reset=True, local=True) ramg_clk = ClockSignal("ramg") romb0_clk = ClockSignal("romb0") romb1_clk = ClockSignal("romb1") ramb_clk = ClockSignal("ramb") m.d.comb += [ ResetSignal("ramg").eq(self.cart_rst), ResetSignal("romb0").eq(self.cart_rst), ResetSignal("romb1").eq(self.cart_rst), ResetSignal("ramb").eq(self.cart_rst), ] ramg = Signal(8) romb0 = Signal(8, reset=0x01) romb1 = Signal(1) ramb = Signal(4) m.d.ramg += ramg.eq(self.cart_data) m.d.romb0 += romb0.eq(self.cart_data) m.d.romb1 += romb1.eq(self.cart_data) m.d.ramb += ramb.eq(self.cart_data) m.d.comb += [ ramg_clk.eq(1), romb0_clk.eq(1), romb1_clk.eq(1), ramb_clk.eq(1) ] with m.If(self.cart_wr): with m.Switch(self.cart_addr[12:]): with m.Case(0b0000): m.d.comb += ramg_clk.eq(0) with m.Case(0b0001): m.d.comb += ramg_clk.eq(0) with m.Case(0b0010): m.d.comb += romb0_clk.eq(0) with m.Case(0b0011): m.d.comb += romb1_clk.eq(0) with m.Case(0b0100): m.d.comb += ramb_clk.eq(0) with m.Case(0b0101): m.d.comb += ramb_clk.eq(0) m.d.comb += [ self.ram_en.eq(ramg == 0x0A), self.ram_bank.eq(ramb), ] with m.If(self.cart_addr[14]): m.d.comb += self.rom_bank.eq(Cat(romb0, romb1)) with m.Else(): m.d.comb += self.rom_bank.eq(0) return m
def elaborate(self, platform): """ """ m = Module() # pins ft_clkout_i = platform.request("ft_clkout_i") ft_wr_n_o = platform.request("ft_wr_n_o") ft_txe_n_i = platform.request("ft_txe_n_i") ft_suspend_n_i = platform.request("ft_suspend_n_i") ft_oe_n_o = platform.request("ft_oe_n_o") ft_rd_n_o = platform.request("ft_rd_n_o") ft_siwua_n_o = platform.request("ft_siwua_n_o") ft_data_io = platform.request("ft_data_io") adc_d_i = platform.request("adc_d_i") adc_oe_o = platform.request("adc_oe_o") adc_shdn_o = platform.request("adc_shdn_o") ext1 = platform.request("ext1") pa_en_n_o = platform.request("pa_en_n_o") mix_en_n_o = platform.request("mix_en_n_o") # signals clk80 = Signal() pll_fb = Signal() chan_a = Signal(self.ADC_WIDTH) chan_b = Signal(self.ADC_WIDTH) lsb = Signal() lock = Signal(RAW_STATE) sample_ctr = Signal(range(self.DECIMATE * self.FFT_LEN)) sample_ctr_max = Const(self.DECIMATE * self.FFT_LEN - 1) cons_done = Signal() cons_done_clk80_dom = Signal() send_start = Signal() send_stop = Signal() wait_prod = Signal() lock_ftclk_dom = Signal() lock_ftclk_dom_last = Signal() # clock domains clk40_neg = ClockDomain("clk40_neg", clk_edge="neg") m.domains.clk40_neg = clk40_neg m.d.comb += ClockSignal("clk40_neg").eq(ClockSignal("sync")) m.domains += ClockDomain("clk60") m.d.comb += ClockSignal("clk60").eq(ft_clkout_i.i) m.domains += ClockDomain("clk80") m.d.comb += ClockSignal("clk80").eq(clk80) # ======================== submodules ======================== # PLL m.submodules += Instance( "PLLE2_BASE", ("p", "CLKFBOUT_MULT", 24), ("p", "DIVCLK_DIVIDE", 1), ("p", "CLKOUT0_DIVIDE", 12), ("p", "CLKIN1_PERIOD", 25), ("o", "CLKOUT0", clk80), ("i", "CLKIN1", ClockSignal("sync")), ("i", "RST", 0), ("o", "CLKFBOUT", pll_fb), ("i", "CLKFBIN", pll_fb), ) # ADC m.submodules.ltc2292 = ltc2292 = LTC2292( posedge_domain="sync", negedge_domain="clk40_neg" ) m.d.comb += [ ltc2292.di.eq(adc_d_i.i), chan_a.eq(ltc2292.dao), chan_b.eq(ltc2292.dbo), ] # FIFO m.submodules.fifo = fifo = AsyncFIFO( width=self.USB_WIDTH, depth=self.DECIMATE * self.FFT_LEN * 2, r_domain="clk60", w_domain="clk80", ) with m.If(lsb): m.d.comb += fifo.w_data.eq(chan_a[: self.USB_WIDTH]) with m.Else(): m.d.comb += fifo.w_data.eq( Cat( chan_a[self.USB_WIDTH :], Const(0, 2 * self.USB_WIDTH - self.ADC_WIDTH), ) ) # consumption done sync m.submodules.cons_done_sync = cons_done_sync = FFSynchronizer( i=cons_done, o=cons_done_clk80_dom, o_domain="clk80" ) # lock synch m.submodules.lock_sync = lock_sync = FFSynchronizer( i=lock, o=lock_ftclk_dom, o_domain="clk60" ) # =========================== logic ========================== m.d.comb += [ pa_en_n_o.o.eq(1), mix_en_n_o.o.eq(1), adc_oe_o.o.eq(0b01), adc_shdn_o.o.eq(0b00), ext1.o[0].eq(0b0), ext1.o[3].eq(lock), ext1.o[1].eq(0b0), ext1.o[4].eq(fifo.r_en), ext1.o[2].eq(0b0), ext1.o[5].eq(fifo.w_en), ] # write clock domain with m.If(lock == RAW_STATE.PROD): with m.If(sample_ctr == sample_ctr_max): m.d.clk80 += [ lock.eq(RAW_STATE.CONS), sample_ctr.eq(0), lsb.eq(0), ] with m.Else(): with m.If(lsb): m.d.clk80 += sample_ctr.eq(sample_ctr + 1) m.d.clk80 += lsb.eq(~lsb) with m.Else(): with m.If(cons_done_clk80_dom): m.d.clk80 += [ lock.eq(RAW_STATE.PROD), sample_ctr.eq(0), lsb.eq(0), ] with m.Switch(lock): with m.Case(RAW_STATE.PROD): m.d.comb += fifo.w_en.eq(1) with m.Case(RAW_STATE.CONS): m.d.comb += fifo.w_en.eq(0) # read clock domain m.d.clk60 += lock_ftclk_dom_last.eq(lock_ftclk_dom) with m.If(lock_ftclk_dom == RAW_STATE.CONS & ~wait_prod): with m.If(~fifo.r_rdy): m.d.clk60 += wait_prod.eq(1) with m.Elif(lock_ftclk_dom == RAW_STATE.CONS): m.d.clk60 += wait_prod.eq(1) with m.Else(): m.d.clk60 += wait_prod.eq(0) m.d.comb += [ ft_oe_n_o.o.eq(1), ft_rd_n_o.o.eq(1), ft_siwua_n_o.o.eq(1), ] with m.Switch(lock_ftclk_dom): with m.Case(RAW_STATE.PROD): m.d.comb += [ send_start.eq(0), send_stop.eq(0), ft_data_io.o.eq(0), ft_wr_n_o.o.eq(1), fifo.r_en.eq(0), ] with m.Case(RAW_STATE.CONS): with m.If(lock_ftclk_dom_last == RAW_STATE.PROD): m.d.comb += send_start.eq(1) with m.Else(): m.d.comb += send_start.eq(0) with m.If(~fifo.r_rdy): m.d.comb += [send_stop.eq(1), cons_done.eq(1)] with m.Else(): m.d.comb += [send_stop.eq(0), cons_done.eq(0)] with m.If(send_start): m.d.comb += [ ft_data_io.o.eq(self.START_FLAG), ft_wr_n_o.o.eq(ft_txe_n_i.i), fifo.r_en.eq(1), ] with m.Elif(send_stop): m.d.comb += [ ft_data_io.o.eq(self.STOP_FLAG), ft_wr_n_o.o.eq(ft_txe_n_i.i), fifo.r_en.eq(0), ] with m.Else(): with m.If(wait_prod): m.d.comb += [ ft_data_io.o.eq(0), ft_wr_n_o.o.eq(1), fifo.r_en.eq(0), ] with m.Else(): m.d.comb += [ ft_data_io.o.eq(fifo.r_data), ft_wr_n_o.o.eq(~(~ft_txe_n_i.i & fifo.r_en)), fifo.r_en.eq(~ft_txe_n_i.i & fifo.r_rdy), ] return m
def elaborate(self, platform): m = Module() # Memory read and write ports. m.submodules.read = mem_read_port = self.mem.read_port(domain="usb") m.submodules.write = mem_write_port = self.mem.write_port(domain="usb") # Store the memory address of our active packet header, which will store # packet metadata like the packet size. header_location = Signal.like(mem_write_port.addr) write_location = Signal.like(mem_write_port.addr) # Read FIFO status. read_location = Signal.like(mem_read_port.addr) fifo_count = Signal.like(mem_read_port.addr, reset=0) fifo_new_data = Signal() # Current receive status. packet_size = Signal(16) # # Read FIFO logic. # m.d.comb += [ # We have data ready whenever there's not data in the FIFO. self.data_available.eq(fifo_count != 0), # Our data_out is always the output of our read port... self.data_out.eq(mem_read_port.data), # ... and our read port always reads from our read pointer. mem_read_port.addr.eq(read_location), self.sampling.eq(mem_write_port.en) ] # Once our consumer has accepted our current data, move to the next address. with m.If(self.next & self.data_available): m.d.usb += read_location.eq(read_location + 1) # # FIFO count handling. # fifo_full = (fifo_count == self.mem_size) data_pop = Signal() data_push = Signal() m.d.comb += [ data_pop.eq(self.next & self.data_available), data_push.eq(fifo_new_data & ~fifo_full) ] # If we have both a read and a write, don't update the count, # as we've both added one and subtracted one. with m.If(data_push & data_pop): pass # Otherwise, add when data's added, and subtract when data's removed. with m.Elif(data_push): m.d.usb += fifo_count.eq(fifo_count + 1) with m.Elif(data_pop): m.d.usb += fifo_count.eq(fifo_count - 1) # # Core analysis FSM. # with m.FSM(domain="usb") as f: m.d.comb += [ self.overrun.eq(f.ongoing("OVERRUN")), self.capturing.eq(f.ongoing("CAPTURE")), ] # IDLE: wait for an active receive. with m.State("IDLE"): # Wait until a transmission is active. # TODO: add triggering logic? with m.If(self.utmi.rx_active): m.next = "CAPTURE" m.d.usb += [ header_location.eq(write_location), write_location.eq(write_location + self.HEADER_SIZE_BYTES), packet_size.eq(0), ] # Capture data until the packet is complete. with m.State("CAPTURE"): # Capture data whenever RxValid is asserted. m.d.comb += [ mem_write_port.addr.eq(write_location), mem_write_port.data.eq(self.utmi.rx_data), mem_write_port.en.eq(self.utmi.rx_valid & self.utmi.rx_active), fifo_new_data.eq(self.utmi.rx_valid & self.utmi.rx_active) ] # Advance the write pointer each time we receive a bit. with m.If(self.utmi.rx_valid & self.utmi.rx_active): m.d.usb += [ write_location.eq(write_location + 1), packet_size.eq(packet_size + 1) ] # If this would be filling up our data memory, # move to the OVERRUN state. with m.If(fifo_count == self.mem_size - 1 - self.HEADER_SIZE_BYTES): m.next = "OVERRUN" # If we've stopped receiving, move to the "finalize" state. with m.If(~self.utmi.rx_active): # Optimization: if we didn't receive any data, there's no need # to create a packet. Clear our header from the FIFO and disarm. with m.If(packet_size == 0): m.next = "IDLE" m.d.usb += [write_location.eq(header_location)] with m.Else(): m.next = "EOP_1" # EOP: handle the end of the relevant packet. with m.State("EOP_1"): # Now that we're done, add the header to the start of our packet. # This will take two cycles, currently, as we're using a 2-byte header, # but we only have an 8-bit write port. m.d.comb += [ mem_write_port.addr.eq(header_location), mem_write_port.data.eq(packet_size[7:16]), #mem_write_port.data .eq(0xAA), mem_write_port.en.eq(1), fifo_new_data.eq(1) ] m.next = "EOP_2" with m.State("EOP_2"): # Add the second byte of our header. # Note that, if this is an adjacent read, we should have # just captured our packet header _during_ the stop turnaround. m.d.comb += [ mem_write_port.addr.eq(header_location + 1), mem_write_port.data.eq(packet_size[0:8]), mem_write_port.en.eq(1), fifo_new_data.eq(1) ] # Move to the next state, which will either be another capture, # or our idle state, depending on whether we have another rx. with m.If(self.utmi.rx_active): m.next = "CAPTURE" m.d.usb += [ header_location.eq(write_location), write_location.eq(write_location + self.HEADER_SIZE_BYTES), packet_size.eq(0), ] # FIXME: capture if rx_valid with m.Else(): m.next = "IDLE" # BABBLE -- handles the case in which we've received a packet beyond # the allowable size in the USB spec with m.State("BABBLE"): # Trap here, for now. pass with m.State("OVERRUN"): # TODO: we should probably set an overrun flag and then emit an EOP, here? pass return m
def elaborate(self, platform): m = Module() ####################################################################### # Line State Recovery State Machine # # The receive path doesn't use a differential receiver. Because of # this there is a chance that one of the differential pairs will appear # to have changed to the new state while the other is still in the old # state. The following state machine detects transitions and waits an # extra sampling clock before decoding the state on the differential # pair. This transition period # will only ever last for one clock as # long as there is no noise on the line. If there is enough noise on # the line then the data may be corrupted and the packet will fail the # data integrity checks. # dpair = Signal(2) m.d.comb += dpair.eq(Cat(self._usbn, self._usbp)) # output signals for use by the clock recovery stage line_state_dt = Signal() line_state_dj = Signal() line_state_dk = Signal() line_state_se0 = Signal() line_state_se1 = Signal() # If we are in a transition state, then we can sample the pair and # move to the next corresponding line state. with m.FSM(domain="usb_io"): with m.State("DT"): m.d.comb += line_state_dt.eq(1) with m.Switch(dpair): with m.Case(0b10): m.next = "DJ" with m.Case(0b01): m.next = "DK" with m.Case(0b00): m.next = "SE0" with m.Case(0b11): m.next = "SE1" # If we are in a valid line state and the value of the pair changes, # then we need to move to the transition state. with m.State("DJ"): m.d.comb += line_state_dj.eq(1) with m.If(dpair != 0b10): m.next = "DT" with m.State("DK"): m.d.comb += line_state_dk.eq(1) with m.If(dpair != 0b01): m.next = "DT" with m.State("SE0"): m.d.comb += line_state_se0.eq(1) with m.If(dpair != 0b00): m.next = "DT" with m.State("SE1"): m.d.comb += line_state_se1.eq(1) with m.If(dpair != 0b11): m.next = "DT" ####################################################################### # Clock and Data Recovery # # The DT state from the line state recovery state machine is used to align to # transmit clock. The line state is sampled in the middle of the bit time. # # Example of signal relationships # ------------------------------- # line_state DT DJ DJ DJ DT DK DK DK DK DK DK DT DJ DJ DJ # line_state_valid ________----____________----____________----________----____ # bit_phase 0 0 1 2 3 0 1 2 3 0 1 2 0 1 2 # # We 4x oversample, so make the line_state_phase have # 4 possible values. line_state_phase = Signal(2) m.d.usb_io += [ self.line_state_valid.eq(line_state_phase == 1), # flop all the outputs to help with timing self.line_state_dj.eq(line_state_dj), self.line_state_dk.eq(line_state_dk), self.line_state_se0.eq(line_state_se0), self.line_state_se1.eq(line_state_se1), ] with m.If(line_state_dt): m.d.usb_io += [ # re-align the phase with the incoming transition line_state_phase.eq(0), # make sure we never assert valid on a transition self.line_state_valid.eq(0), ] with m.Else(): # keep tracking the clock by incrementing the phase m.d.usb_io += line_state_phase.eq(line_state_phase + 1) return m
def elaborate(self, platform): m = Module() # Move the pipeline along m.d.sync += [ self.o_rgbrndr.eq(self.i_rgbrndr), self.o_arndr.eq(self.i_arndr), self.o_zrndr.eq(self.i_zrndr), self.o_x_coord.eq(self.i_x_coord), self.o_y_coord.eq(self.i_y_coord), self.o_z_coord.eq(self.i_z_coord), self.o_alpha.eq(self.i_alpha), ] a_red = Signal(8) a_green = Signal(8) a_blue = Signal(8) with m.Switch(self.i_blend_a): with m.Case(BlendRGB.SRC): m.d.comb += a_red.eq(self.i_red) m.d.comb += a_green.eq(self.i_green) m.d.comb += a_blue.eq(self.i_blue) with m.Case(BlendRGB.FB): m.d.comb += a_red.eq(self.i_fbred) m.d.comb += a_green.eq(self.i_fbgreen) m.d.comb += a_blue.eq(self.i_fbblue) with m.Case(BlendRGB.ZERO): m.d.comb += a_red.eq(0) m.d.comb += a_green.eq(0) m.d.comb += a_blue.eq(0) b_red = Signal(8) b_green = Signal(8) b_blue = Signal(8) with m.Switch(self.i_blend_b): with m.Case(BlendRGB.SRC): m.d.comb += b_red.eq(self.i_red) m.d.comb += b_green.eq(self.i_green) m.d.comb += b_blue.eq(self.i_blue) with m.Case(BlendRGB.FB): m.d.comb += b_red.eq(self.i_fbred) m.d.comb += b_green.eq(self.i_fbgreen) m.d.comb += b_blue.eq(self.i_fbblue) with m.Case(BlendRGB.ZERO): m.d.comb += b_red.eq(0) m.d.comb += b_green.eq(0) m.d.comb += b_blue.eq(0) c = Signal(8) with m.Switch(self.i_blend_c): with m.Case(BlendAlpha.SRC): m.d.comb += c.eq(self.i_alpha) with m.Case(BlendAlpha.FB): m.d.comb += c.eq(self.i_fbalpha) with m.Case(BlendAlpha.FIX): m.d.comb += c.eq(self.i_fix) d_red = Signal(8) d_green = Signal(8) d_blue = Signal(8) with m.Switch(self.i_blend_d): with m.Case(BlendRGB.SRC): m.d.comb += d_red.eq(self.i_red) m.d.comb += d_green.eq(self.i_green) m.d.comb += d_blue.eq(self.i_blue) with m.Case(BlendRGB.FB): m.d.comb += d_red.eq(self.i_fbred) m.d.comb += d_green.eq(self.i_fbgreen) m.d.comb += d_blue.eq(self.i_fbblue) with m.Case(BlendRGB.ZERO): m.d.comb += d_red.eq(0) m.d.comb += d_green.eq(0) m.d.comb += d_blue.eq(0) with m.If(self.i_enable & (~self.i_alphaen | self.i_alpha[7])): m.d.sync += [ self.o_red.eq((((a_red - b_red) * c) >> 7) + d_red), self.o_green.eq((((a_green - b_green) * c) >> 7) + d_green), self.o_blue.eq((((a_blue - b_blue) * c) >> 7) + d_blue) ] with m.Else(): m.d.sync += [ self.o_red.eq(self.i_red), self.o_green.eq(self.i_green), self.o_blue.eq(self.i_blue) ] return m
def elaborate(self, platform): m = Module() # Create the component parts of our ULPI interfacing hardware. m.submodules.register_window = register_window = ULPIRegisterWindow() m.submodules.control_translator = control_translator = ULPIControlTranslator( register_window=register_window) m.submodules.rxevent_decoder = rxevent_decoder = ULPIRxEventDecoder( ulpi_bus=self.ulpi) m.submodules.transmit_translator = transmit_translator = ULPITransmitTranslator( ) # If we're choosing to honor any registers defined in the platform file, apply those # before continuing with elaboration. if self.use_platform_registers and hasattr(platform, 'ulpi_extra_registers'): for address, value in platform.ulpi_extra_registers.items(): self.add_extra_register(address, value) # Add any extra registers provided by the user to our control translator. for address, values in self._extra_registers.items(): control_translator.add_composite_register( m, address, values['value'], reset_value=values['default']) # Keep track of when any of our components are busy any_busy = \ register_window.busy | \ transmit_translator.busy | \ control_translator.busy | \ self.ulpi.dir # If we're handling ULPI clocking, do so. if self.handle_clocking: # We can't currently handle bidirectional clock lines, as we don't know if they # should be used in input or output modes. if hasattr(self.ulpi.clk, 'oe'): raise TypeError( "ULPI records with bidirectional clock lines require manual handling." ) # Just Input (TM) and Just Output (TM) clocks are simpler: we know how to drive them. elif hasattr(self.ulpi.clk, 'o'): m.d.comb += self.ulpi.clk.eq(ClockSignal('usb')) elif hasattr(self.ulpi.clk, 'i'): m.d.comb += ClockSignal('usb').eq(self.ulpi.clk) # Clocks that don't seem to be I/O pins aren't what we're expecting; fail out. else: raise TypeError(f"ULPI `clk` was an unexpected type {type(self.ulpi.clk)}." \ " You may need to handle clocking manually.") # Connect our ULPI control signals to each of our subcomponents. m.d.comb += [ # Drive the bus whenever the target PHY isn't. self.ulpi.data.oe.eq(~self.ulpi.dir), # Generate our busy signal. self.busy.eq(any_busy), # Connect up our clock and reset signals. self.ulpi.rst.eq(ResetSignal("usb")), # Connect our data inputs to the event decoder. # Note that the event decoder is purely passive. rxevent_decoder.register_operation_in_progress.eq( register_window.busy), self.last_rx_command.eq(rxevent_decoder.last_rx_command), # Connect our inputs to our transmit translator. transmit_translator.ulpi_nxt.eq(self.ulpi.nxt), transmit_translator.op_mode.eq(self.op_mode), transmit_translator.bus_idle.eq(~control_translator.busy & ~self.ulpi.dir), transmit_translator.tx_data.eq(self.tx_data), transmit_translator.tx_valid.eq(self.tx_valid), self.tx_ready.eq(transmit_translator.tx_ready), # Connect our inputs to our control translator / register window. control_translator.bus_idle.eq(~transmit_translator.busy), register_window.ulpi_data_in.eq(self.ulpi.data.i), register_window.ulpi_dir.eq(self.ulpi.dir), register_window.ulpi_next.eq(self.ulpi.nxt), ] # Control our the source of our ULPI data output. # If a transmit request is active, prioritize that over # any register reads/writes. with m.If(transmit_translator.ulpi_out_req): m.d.comb += [ self.ulpi.data.o.eq(transmit_translator.ulpi_data_out), self.ulpi.stp.eq(transmit_translator.ulpi_stp) ] # Otherwise, yield control to the register handler. # This is a slight optimization: since it properly generates NOPs # while not in use, we can let it handle idle, as well, saving a mux. with m.Else(): m.d.comb += [ self.ulpi.data.o.eq(register_window.ulpi_data_out), self.ulpi.stp.eq(register_window.ulpi_stop) ] # Connect our RxEvent status signals from our RxEvent decoder. for signal_name, _ in self.RXEVENT_STATUS_SIGNALS: signal = getattr(rxevent_decoder, signal_name) m.d.comb += self.__dict__[signal_name].eq(signal) # Connect our control signals through the control translator. for signal_name, _ in self.CONTROL_SIGNALS: signal = getattr(control_translator, signal_name) m.d.comb += signal.eq(self.__dict__[signal_name]) # RxActive handler: # A transmission starts when DIR goes high with NXT, or when an RxEvent indicates # a switch from RxActive = 0 to RxActive = 1. A transmission stops when DIR drops low, # or when the RxEvent RxActive bit drops from 1 to 0, or an error occurs.A dir_rising_edge = Rose(self.ulpi.dir.i, domain="usb") dir_based_start = dir_rising_edge & self.ulpi.nxt with m.If(~self.ulpi.dir | rxevent_decoder.rx_stop): # TODO: this should probably also trigger if RxError m.d.usb += self.rx_active.eq(0) with m.Elif(dir_based_start | rxevent_decoder.rx_start): m.d.usb += self.rx_active.eq(1) # Data-out: we'll connect this almost direct through from our ULPI # interface, as it's essentially the same as in the UTMI spec. We'll # add a one cycle processing delay so it matches the rest of our signals. # RxValid: equivalent to NXT whenever a Rx is active. m.d.usb += [ self.rx_data.eq(self.ulpi.data.i), self.rx_valid.eq(self.ulpi.nxt & self.rx_active) ] return m
def elaborate(self, platform): m = Module() # # General state signals. # # Our line state is always taken directly from D- and D+. m.d.comb += self.line_state.eq(Cat(self._io.d_n.i, self._io.d_p.i)) # If we have a ``vbus_valid`` indication, use it to drive our ``vbus_valid`` # signal. Otherwise, we'll pretend ``vbus_valid`` is always true, for compatibility. if hasattr(self._io, 'vbus_valid'): m.d.comb += [ self.vbus_valid.eq(self._io.vbus_valid), self.session_end.eq(~self._io.vbus_valid) ] else: m.d.comb += [self.vbus_valid.eq(1), self.session_end.eq(0)] # # General control signals. # # If we have a pullup signal, drive it based on ``term_select``. if hasattr(self._io, 'pullup'): m.d.comb += self._io.pullup.eq(self.term_select) # If we have a pulldown signal, drive it based on our pulldown controls. if hasattr(self._io, 'pulldown'): m.d.comb += self._io.pullup.eq(self.dm_pulldown | self.dp_pulldown) # # Transmitter # in_normal_mode = (self.op_mode == self.OP_MODE_NORMAL) in_non_encoding_mode = (self.op_mode == self.OP_MODE_NO_ENCODING) m.submodules.transmitter = transmitter = TxPipeline() # When we're in normal mode, we'll drive the USB bus with our standard USB transmitter data. with m.If(in_normal_mode): m.d.comb += [ # UTMI Transmit data. transmitter.i_data_payload.eq(self.tx_data), transmitter.i_oe.eq(self.tx_valid), self.tx_ready.eq(transmitter.o_data_strobe), # USB output. self._io.d_p.o.eq(transmitter.o_usbp), self._io.d_n.o.eq(transmitter.o_usbn), # USB tri-state control. self._io.d_p.oe.eq(transmitter.o_oe), self._io.d_n.oe.eq(transmitter.o_oe), ] # When we're in non-encoding mode ("disable bitstuff and NRZI"), # we'll output to D+ and D- directly when tx_valid is true. with m.Elif(in_non_encoding_mode): m.d.comb += [ # USB output. self._io.d_p.o.eq(self.tx_data), self._io.d_n.o.eq(~self.tx_data), # USB tri-state control. self._io.d_p.oe.eq(self.tx_valid), self._io.d_n.oe.eq(self.tx_valid) ] # When we're in other modes (non-driving or invalid), we'll not output at all. # This block does nothing, as signals default to zero, but it makes the intention obvious. with m.Else(): m.d.comb += [ self._io.d_p.oe.eq(0), self._io.d_n.oe.eq(0), ] # Generate our USB clock strobe, which should pulse at 12MHz. m.d.usb_io += transmitter.i_bit_strobe.eq(Rose(ClockSignal("usb"))) # # Receiver # m.submodules.receiver = receiver = RxPipeline() m.d.comb += [ # We'll listen for packets on D+ and D- _whenever we're not transmitting._. # (If we listen while we're transmitting, we'll hear our own packets.) receiver.i_usbp.eq(self._io.d_p & ~transmitter.o_oe), receiver.i_usbn.eq(self._io.d_n & ~transmitter.o_oe), self.rx_data.eq(receiver.o_data_payload), self.rx_valid.eq(receiver.o_data_strobe & receiver.o_pkt_in_progress), self.rx_active.eq(receiver.o_pkt_in_progress), self.rx_error.eq(receiver.o_receive_error) ] m.d.usb += self.rx_complete.eq(receiver.o_pkt_end) return m
def elaborate(self, platform): m = Module() bit_stuffing_disabled = (self.op_mode == self.OP_MODE_NO_BIT_STUFFING) with m.FSM(domain="usb") as fsm: # Mark ourselves as busy whenever we're not in idle. m.d.comb += self.busy.eq(~fsm.ongoing('IDLE')) # IDLE: our transmitter is ready and with m.State('IDLE'): m.d.comb += self.ulpi_stp.eq(0) # Start once a transmit is started, and we can access the bus. with m.If(self.tx_valid & self.bus_idle): # If bit-stuffing is disabled, we'll need to prefix our transmission with a NOPID command. # In this case, we'll never accept the first byte (as we're not ready to transmit it, yet), # and thus TxReady will always be 0. with m.If(bit_stuffing_disabled): m.d.usb += self.ulpi_out_req.eq(1), m.d.comb += [ self.ulpi_data_out.eq(self.TRANSMIT_COMMAND), self.tx_ready.eq(0) ] # Otherwise, this transmission starts with a PID. Extract the PID from the first data byte # and present it as part of the Transmit Command. In this case, the NXT signal is # has the same meaning as the UTMI TxReady signal; and can be passed along directly. with m.Else(): m.d.usb += self.ulpi_out_req.eq(1), m.d.comb += [ self.ulpi_data_out.eq(self.TRANSMIT_COMMAND | self.tx_data[0:4]), self.tx_ready.eq(self.ulpi_nxt) ] # Once the PHY has accepted the command byte, we're ready to move into our main transmit state. with m.If(self.ulpi_nxt): m.next = 'TRANSMIT' # TRANSMIT: we're in the body of a transmit; the UTMI and ULPI interface signals # are roughly equivalent; we'll just pass them through. with m.State('TRANSMIT'): m.d.comb += [ self.ulpi_data_out.eq(self.tx_data), self.tx_ready.eq(self.ulpi_nxt), self.ulpi_stp.eq(0), ] # Once the transmission has ended, we'll need to issue a ULPI stop. with m.If(~self.tx_valid): m.d.usb += self.ulpi_out_req.eq(0), m.next = 'IDLE' # STOP: our packet has just terminated; we'll generate a ULPI stop event for a single cycle. # [ULPI: 3.8.2.2] m.d.comb += self.ulpi_stp.eq(1) # If we've disabled bit stuffing, we'll want to termainate by generating a bit-stuff error. with m.If(bit_stuffing_disabled): # Drive 0xFF as we stop, to generate a bit-stuff error. [ULPI: 3.8.2.3] m.d.comb += self.ulpi_data_out.eq(0xFF) # Otherwise, we'll generate a normal stop. with m.Else(): m.d.comb += self.ulpi_data_out.eq(0) return m
def elaborate(self, platform): m = Module() interface = self.interface # # Test scaffolding. # if self._standalone: # Create our timer... m.submodules.timer = timer = USBInterpacketTimer() timer.add_interface(interface.timer) # ... our CRC generator ... m.submodules.crc = crc = USBDataPacketCRC() crc.add_interface(interface.data_crc) m.d.comb += [ crc.rx_data .eq(self.utmi.rx_data), crc.rx_valid .eq(self.utmi.rx_valid), crc.tx_valid .eq(0) ] # ... and our tokenizer. m.submodules.token_detector = tokenizer = USBTokenDetector(utmi=self.utmi) m.d.comb += tokenizer.interface.connect(interface.tokenizer) # # Convenience feature: # # If we have -only- a standard request handler, automatically add a handler that will # stall all other requests. # single_handler = (len(self._request_handlers) == 1) if (single_handler and isinstance(self._request_handlers[0], StandardRequestHandler)): # Add a handler that will stall any non-standard request. stall_condition = lambda setup : setup.type != USBRequestType.STANDARD self.add_request_handler(StallOnlyRequestHandler(stall_condition)) # # Submodules # # Create our SETUP packet decoder. m.submodules.setup_decoder = setup_decoder = USBSetupDecoder(utmi=self.utmi) m.d.comb += [ interface.data_crc .connect(setup_decoder.data_crc), interface.tokenizer .connect(setup_decoder.tokenizer), setup_decoder.speed .eq(interface.speed), # And attach our timer interface to both our local users and # to our setup decoder. interface.timer .attach(setup_decoder.timer) ] # # Request handler logic. # # Multiplex the output of each of our request handlers. m.submodules.request_mux = request_mux = USBRequestHandlerMultiplexer() request_handler = request_mux.shared # Add each of our handlers to the endpoint; and add it to our mux. for handler in self._request_handlers: # Create a display name for the handler... name = handler.__class__.__name__ if hasattr(m.submodules, name): name = f"{name}_{id(handler)}" # ... and add it. m.submodules[name] = handler request_mux.add_interface(handler.interface) # ... and hook it up. m.d.comb += [ setup_decoder.packet .connect(request_handler.setup), interface.tokenizer .connect(request_handler.tokenizer), request_handler.tx .attach(interface.tx), interface.handshakes_out.ack .eq(setup_decoder.ack | request_handler.handshakes_out.ack), interface.handshakes_out.nak .eq(request_handler.handshakes_out.nak), interface.handshakes_out.stall .eq(request_handler.handshakes_out.stall), interface.handshakes_in .connect(request_handler.handshakes_in), interface.address_changed .eq(request_handler.address_changed), interface.new_address .eq(request_handler.new_address), request_handler.active_config .eq(interface.active_config), interface.config_changed .eq(request_handler.config_changed), interface.new_config .eq(request_handler.new_config), # Fix our data PIDs to DATA1, for now, as we don't support multi-packet responses, yet. # Per [USB2.0: 8.5.3], the first packet of the DATA or STATUS phase always carries a DATA1 PID. interface.tx_pid_toggle .eq(request_handler.tx_data_pid) ] # # Core control request handler. # Behavior dictated by [USB2, 8.5.3]. # endpoint_targeted = (self.interface.tokenizer.endpoint == self._endpoint_number) with m.FSM(domain="usb"): # SETUP -- The "SETUP" phase of a control request. We'll wait here # until the SetupDetector detects a valid setup packet for us. with m.State('SETUP'): # We won't do anything until we receive a SETUP token. with m.If(setup_decoder.packet.received & endpoint_targeted): # If our SETUP packet indicates we'll have a data stage (wLength > 0) # move to the DATA stage. Otherwise, move directly to the status stage [8.5.3]. with m.If(setup_decoder.packet.length): # If this is an device -> host request, expect an IN packet. with m.If(setup_decoder.packet.is_in_request): m.next = 'DATA_IN' # Otherwise, expect an OUT one. with m.Else(): m.next = 'DATA_OUT' with m.Else(): # If we don't have a data phase, our status phase is always an IN [USB2.0: 8.5.3] m.next = 'STATUS_IN' with m.State('DATA_IN'): self._handle_setup_reset(m) # Wait until we have an IN token, and are allowed to respond to it. allowed_to_respond = interface.tokenizer.ready_for_response & endpoint_targeted with m.If(allowed_to_respond & interface.tokenizer.is_in): # Notify the request handler to prepare a response. m.d.comb += request_handler.data_requested.eq(1) # Once we get an OUT token, we should move on to the STATUS stage. [USB2, 8.5.3] with m.If(endpoint_targeted & interface.tokenizer.new_token & interface.tokenizer.is_out): m.next = 'STATUS_OUT' with m.State('DATA_OUT'): self._handle_setup_reset(m) # Pass through our Rx related signals iff we're in the DATA_OUT stage, # and the most recent token pointed to our endpoint. This ensures the # request handler only ever sees data events related to it; this simplifies # the request handler logic significantly. with m.If(endpoint_targeted & interface.tokenizer.is_out): m.d.comb += [ interface.rx .connect(request_handler.rx), request_handler.rx_ready_for_response .eq(interface.rx_ready_for_response) ] # Once we get an IN token, we should move on to the STATUS stage. [USB2, 8.5.3] with m.If(interface.tokenizer.new_token & interface.tokenizer.is_in): m.next = 'STATUS_IN' # STATUS_IN -- We're currently in the status stage, and we're expecting an IN token. # We'll wait for that token. with m.State('STATUS_IN'): self._handle_setup_reset(m) # If we respond to a status-phase IN token, we'll always use a DATA1 PID [USB2.0: 8.5.3] # When we get an IN token, the host is looking for a status-stage ZLP. # Notify the target handler. allowed_to_respond = interface.tokenizer.ready_for_response & endpoint_targeted with m.If(allowed_to_respond & interface.tokenizer.is_in): m.d.comb += request_handler.status_requested.eq(1) # STATUS_OUT -- We're currently in the status stage, and we're expecting the DATA packet for # an OUT request. with m.State('STATUS_OUT'): self._handle_setup_reset(m) # Once we've received a new DATA packet, we're ready to handle a status request. allowed_to_respond = interface.rx_ready_for_response & endpoint_targeted with m.If(allowed_to_respond & interface.tokenizer.is_out): m.d.comb += request_handler.status_requested.eq(1) return m
def elaborate(self, platform): m = Module() # Move the pipeline along m.d.sync += [ self.o_x_coord.eq(self.i_x_coord), self.o_y_coord.eq(self.i_y_coord), self.o_z_coord.eq(self.i_z_coord), self.o_red.eq(self.i_red), self.o_green.eq(self.i_green), self.o_blue.eq(self.i_blue), self.o_alpha.eq(self.i_alpha) ] # Alpha Test, relative to AREF. test = Signal() with m.If(self.i_enable): with m.Switch(self.i_test): with m.Case(AlphaTestMode.NEVER): m.d.comb += test.eq(0) with m.Case(AlphaTestMode.ALWAYS): m.d.comb += test.eq(1) with m.Case(AlphaTestMode.LESS): m.d.comb += test.eq(self.i_alpha < self.i_aref) with m.Case(AlphaTestMode.LEQUAL): m.d.comb += test.eq(self.i_alpha <= self.i_aref) with m.Case(AlphaTestMode.EQUAL): m.d.comb += test.eq(self.i_alpha == self.i_aref) with m.Case(AlphaTestMode.GEQUAL): m.d.comb += test.eq(self.i_alpha >= self.i_aref) with m.Case(AlphaTestMode.GREATER): m.d.comb += test.eq(self.i_alpha > self.i_aref) with m.Case(AlphaTestMode.NOTEQUAL): m.d.comb += test.eq(self.i_alpha != self.i_aref) with m.Else(): m.d.comb += test.eq(1) with m.Switch(self.i_failmod): with m.Case(AlphaFailMode.KEEP): m.d.sync += [ self.o_rgbrndr.eq(self.i_rgbrndr & test), self.o_arndr.eq(self.i_arndr & test), self.o_zrndr.eq(self.i_zrndr & test) ] with m.Case(AlphaFailMode.FB_ONLY): m.d.sync += [ self.o_rgbrndr.eq(self.i_rgbrndr), self.o_arndr.eq(self.i_arndr), self.o_zrndr.eq(self.i_zrndr & test) ] with m.Case(AlphaFailMode.ZB_ONLY): m.d.sync += [ self.o_rgbrndr.eq(self.i_rgbrndr & test), self.o_arndr.eq(self.i_arndr & test), self.o_zrndr.eq(self.i_zrndr) ] with m.Case(AlphaFailMode.RGB_ONLY): # "RGB_ONLY is effective only when the color format is RGBA32. # In other formats, operation is made with FB_ONLY." - GS User's Manual with m.If(self.i_fbpxfmt == PixelFormat.PSMCT32): m.d.sync += [ self.o_rgbrndr.eq(self.i_rgbrndr), self.o_arndr.eq(self.i_arndr & test), self.o_zrndr.eq(self.i_zrndr & test) ] with m.Else(): m.d.sync += [ self.o_rgbrndr.eq(self.i_rgbrndr), self.o_arndr.eq(self.i_arndr), self.o_zrndr.eq(self.i_zrndr & test) ] return m
def elaborate(self, platform): m = Module() # Generate our clock domains. clocking = LunaECP5DomainGenerator(clock_frequencies=CLOCK_FREQUENCIES) m.submodules.clocking = clocking # Create a set of registers, and expose them over SPI. board_spi = platform.request("debug_spi") spi_registers = SPIRegisterInterface(default_read_value=-1) m.submodules.spi_registers = spi_registers # Simple applet ID register. spi_registers.add_read_only_register(REGISTER_ID, read=0x54455354) # LED test register. led_reg = spi_registers.add_register(REGISTER_LEDS, size=6, name="leds", reset=0b10) led_out = Cat( [platform.request("led", i, dir="o") for i in range(0, 6)]) m.d.comb += led_out.eq(led_reg) # # Target power test register. # Note: these values assume you've populated the correct AP22814 for # your revision (AP22814As for rev0.2+, and AP22814Bs for rev0.1). # bits [1:0]: 0 = power off # 1 = provide A-port VBUS # 2 = pass through target VBUS # power_test_reg = Signal(3) power_test_write_strobe = Signal() power_test_write_value = Signal(2) spi_registers.add_sfr(REGISTER_TARGET_POWER, read=power_test_reg, write_strobe=power_test_write_strobe, write_signal=power_test_write_value) # Store the values for our enable bits. with m.If(power_test_write_strobe): m.d.sync += power_test_reg[0:2].eq(power_test_write_value) # Decode the enable bits and control the two power supplies. power_a_port = platform.request("power_a_port") power_passthrough = platform.request("pass_through_vbus") with m.If(power_test_reg[0:2] == 1): m.d.comb += [power_a_port.eq(1), power_passthrough.eq(0)] with m.Elif(power_test_reg[0:2] == 2): m.d.comb += [power_a_port.eq(0), power_passthrough.eq(1)] with m.Else(): m.d.comb += [power_a_port.eq(0), power_passthrough.eq(0)] # # User IO GPIO registers. # # Data direction register. user_io_dir = spi_registers.add_register(REGISTER_USER_IO_DIR, size=4) # Pin (input) state register. user_io_in = Signal(4) spi_registers.add_sfr(REGISTER_USER_IO_IN, read=user_io_in) # Output value register. user_io_out = spi_registers.add_register(REGISTER_USER_IO_OUT, size=4) # Grab and connect each of our user-I/O ports our GPIO registers. for i in range(4): pin = platform.request("user_io", i) m.d.comb += [ pin.oe.eq(user_io_dir[i]), user_io_in[i].eq(pin.i), pin.o.eq(user_io_out[i]) ] # # ULPI PHY windows # self.add_ulpi_registers(m, platform, clock=clocking.clk_ulpi, ulpi_bus="target_phy", register_base=REGISTER_TARGET_ADDR) self.add_ulpi_registers(m, platform, clock=clocking.clk_ulpi, ulpi_bus="host_phy", register_base=REGISTER_HOST_ADDR) self.add_ulpi_registers(m, platform, clock=clocking.clk_ulpi, ulpi_bus="sideband_phy", register_base=REGISTER_SIDEBAND_ADDR) # # HyperRAM test connections. # ram_bus = platform.request('ram') psram = HyperRAMInterface(bus=ram_bus) m.submodules += psram psram_address_changed = Signal() psram_address = spi_registers.add_register( REGISTER_RAM_REG_ADDR, write_strobe=psram_address_changed) spi_registers.add_sfr(REGISTER_RAM_VALUE, read=psram.read_data) # Hook up our PSRAM. m.d.comb += [ ram_bus.reset.eq(0), psram.single_page.eq(0), psram.perform_write.eq(0), psram.register_space.eq(1), psram.final_word.eq(1), psram.start_transfer.eq(psram_address_changed), psram.address.eq(psram_address), ] # # SPI flash passthrough connections. # flash_sdo = Signal() spi_flash_bus = platform.request('spi_flash') spi_flash_passthrough = ECP5ConfigurationFlashInterface( bus=spi_flash_bus) m.submodules += spi_flash_passthrough m.d.comb += [ spi_flash_passthrough.sck.eq(board_spi.sck), spi_flash_passthrough.sdi.eq(board_spi.sdi), flash_sdo.eq(spi_flash_passthrough.sdo), ] # # Synchronize each of our I/O SPI signals, where necessary. # spi = synchronize(m, board_spi) # Select the passthrough or gateware SPI based on our chip-select values. gateware_sdo = Signal() with m.If(spi_registers.spi.cs): m.d.comb += board_spi.sdo.eq(gateware_sdo) with m.Else(): m.d.comb += board_spi.sdo.eq(flash_sdo) # Connect our register interface to our board SPI. m.d.comb += [ spi_registers.spi.sck.eq(spi.sck), spi_registers.spi.sdi.eq(spi.sdi), gateware_sdo.eq(spi_registers.spi.sdo), spi_registers.spi.cs.eq(spi.cs) ] return m
def elaborate(self, platform): m = Module() # If we're standalone, generate the things we need. if self.standalone: # Create our tokenizer... m.submodules.tokenizer = tokenizer = USBTokenDetector(utmi=self.utmi) m.d.comb += tokenizer.interface.connect(self.tokenizer) # ... and our timer. m.submodules.timer = timer = USBInterpacketTimer() timer.add_interface(self.timer) m.d.comb += timer.speed.eq(self.speed) # Create a data-packet-deserializer, which we'll use to capture the # contents of the setup data packets. m.submodules.data_handler = data_handler = \ USBDataPacketDeserializer(utmi=self.utmi, max_packet_size=8, create_crc_generator=self.standalone) m.d.comb += self.data_crc.connect(data_handler.data_crc) # Instruct our interpacket timer to begin counting when we complete receiving # our setup packet. This will allow us to track interpacket delays. m.d.comb += self.timer.start.eq(data_handler.new_packet) # Keep our output signals de-asserted unless specified. m.d.usb += [ self.packet.received .eq(0), ] with m.FSM(domain="usb"): # IDLE -- we haven't yet detected a SETUP transaction directed at us with m.State('IDLE'): pid_matches = (self.tokenizer.pid == self.SETUP_PID) # If we're just received a new SETUP token addressed to us, # the next data packet is going to be for us. with m.If(pid_matches & self.tokenizer.new_token): m.next = 'READ_DATA' # READ_DATA -- we've just seen a SETUP token, and are waiting for the # data payload of the transaction, which contains the setup packet. with m.State('READ_DATA'): # If we receive a token packet before we receive a DATA packet, # this is a PID mismatch. Bail out and start over. with m.If(self.tokenizer.new_token): m.next = 'IDLE' # If we have a new packet, parse it as setup data. with m.If(data_handler.new_packet): # If we got exactly eight bytes, this is a valid setup packet. with m.If(data_handler.length == 8): # Collect the signals that make up our bmRequestType [USB2, 9.3]. request_type = Cat(self.packet.recipient, self.packet.type, self.packet.is_in_request) m.d.usb += [ # Parse the setup data itself... request_type .eq(data_handler.packet[0]), self.packet.request .eq(data_handler.packet[1]), self.packet.value .eq(Cat(data_handler.packet[2], data_handler.packet[3])), self.packet.index .eq(Cat(data_handler.packet[4], data_handler.packet[5])), self.packet.length .eq(Cat(data_handler.packet[6], data_handler.packet[7])), # ... and indicate that we have new data. self.packet.received .eq(1), ] # We'll now need to wait a receive-transmit delay before initiating our ACK. # Per the USB 2.0 and ULPI 1.1 specifications: # - A HS device needs to wait 8 HS bit periods before transmitting [USB2, 7.1.18.2]. # Each ULPI cycle is 8 HS bit periods, so we'll only need to wait one cycle. # - We'll use our interpacket delay timer for everything else. with m.If(self.timer.tx_allowed | (self.speed == USBSpeed.HIGH)): # If we're a high speed device, we only need to wait for a single ULPI cycle. # Processing delays mean we've already met our interpacket delay; and we can ACK # immediately. m.d.comb += self.ack.eq(1) m.next = "IDLE" # For other cases, handle the interpacket delay by waiting. with m.Else(): m.next = "INTERPACKET_DELAY" # Otherwise, this isn't; and we should ignore it. [USB2, 8.5.3] with m.Else(): m.next = "IDLE" # INTERPACKET -- wait for an inter-packet delay before responding with m.State('INTERPACKET_DELAY'): # ... and once it equals zero, ACK and return to idle. with m.If(self.timer.tx_allowed): m.d.comb += self.ack.eq(1) m.next = "IDLE" return m
def elaborate(self, platform): m = Module() sync_pulse = Signal(8) da_reset_shifter = Signal() da_reset_bitstuff = Signal( ) # Need to reset the bit stuffer 1 cycle after the shifter. stall = Signal() # These signals are set during the sync pulse sp_reset_bitstuff = Signal() sp_reset_shifter = Signal() sp_bit = Signal() sp_o_data_strobe = Signal() # 12MHz domain bitstuff_valid_data = Signal() # Keep a Gray counter around to smoothly transition between states state_gray = Signal(2) state_data = Signal() state_sync = Signal() # # Transmit gearing. # m.submodules.shifter = shifter = TxShifter(width=8) m.d.comb += [ shifter.i_data.eq(self.i_data_payload), shifter.i_enable.eq(~stall), shifter.i_clear.eq(da_reset_shifter | sp_reset_shifter) ] # # Bit-stuffing and NRZI. # bitstuff = ResetInserter(da_reset_bitstuff)(TxBitstuffer()) m.submodules.bitstuff = bitstuff m.submodules.nrzi = nrzi = TxNRZIEncoder() # # Transmit controller. # m.d.comb += [ # Send a data strobe when we're two bits from the end of the sync pulse. # This is because the pipeline takes two bit times, and we want to ensure the pipeline # has spooled up enough by the time we're there. bitstuff.i_data.eq(shifter.o_data), stall.eq(bitstuff.o_stall), sp_bit.eq(sync_pulse[0]), sp_reset_bitstuff.eq(sync_pulse[0]), # The shifter has one clock cycle of latency, so reset it # one cycle before the end of the sync byte. sp_reset_shifter.eq(sync_pulse[1]), sp_o_data_strobe.eq(sync_pulse[5]), state_data.eq(state_gray[0] & state_gray[1]), state_sync.eq(state_gray[0] & ~state_gray[1]), self.fit_oe.eq(state_data | state_sync), self.fit_dat.eq((state_data & shifter.o_data & ~bitstuff.o_stall) | sp_bit), self.o_data_strobe.eq(state_data & shifter.o_get & ~stall & self.i_oe), ] # If we reset the shifter, then o_empty will go high on the next cycle. # m.d.usb += [ # If the shifter runs out of data, percolate the "reset" signal to the # shifter, and then down to the bitstuffer. # da_reset_shifter.eq(~stall & shifter.o_empty & ~da_stalled_reset), # da_stalled_reset.eq(da_reset_shifter), # da_reset_bitstuff.eq(~stall & da_reset_shifter), bitstuff_valid_data.eq(~stall & shifter.o_get & self.i_oe), ] with m.FSM(domain="usb"): with m.State('IDLE'): with m.If(self.i_oe): m.d.usb += [sync_pulse.eq(1 << 7), state_gray.eq(0b01)] m.next = "SEND_SYNC" with m.Else(): m.d.usb += state_gray.eq(0b00) with m.State('SEND_SYNC'): m.d.usb += sync_pulse.eq(sync_pulse >> 1) with m.If(sync_pulse[0]): m.d.usb += state_gray.eq(0b11) m.next = "SEND_DATA" with m.Else(): m.d.usb += state_gray.eq(0b01) with m.State('SEND_DATA'): with m.If(~self.i_oe & shifter.o_empty & ~bitstuff.o_stall): with m.If(bitstuff.o_will_stall): m.next = 'STUFF_LAST_BIT' with m.Else(): m.d.usb += state_gray.eq(0b10) m.next = 'IDLE' with m.Else(): m.d.usb += state_gray.eq(0b11) with m.State('STUFF_LAST_BIT'): m.d.usb += state_gray.eq(0b10) m.next = 'IDLE' # 48MHz domain # NRZI encoding nrzi_dat = Signal() nrzi_oe = Signal() # Cross the data from the 12MHz domain to the 48MHz domain cdc_dat = FFSynchronizer(self.fit_dat, nrzi_dat, o_domain="usb_io", stages=3) cdc_oe = FFSynchronizer(self.fit_oe, nrzi_oe, o_domain="usb_io", stages=3) m.submodules += [cdc_dat, cdc_oe] m.d.comb += [ nrzi.i_valid.eq(self.i_bit_strobe), nrzi.i_data.eq(nrzi_dat), nrzi.i_oe.eq(nrzi_oe), self.o_usbp.eq(nrzi.o_usbp), self.o_usbn.eq(nrzi.o_usbn), self.o_oe.eq(nrzi.o_oe), ] return m
def elaborate(self, platform): m = Module() # # Delayed input and output. # if self.in_skew is not None: data_in = delay(m, self.bus.dq.i, self.in_skew) else: data_in = self.bus.dq.i if self.out_skew is not None: data_out = Signal.like(self.bus.dq.o) delay(m, data_out, self.out_skew, out=self.bus.dq.o) else: data_out = self.bus.dq.o # # Transaction clock generator. # advance_clock = Signal() reset_clock = Signal() if self.clock_skew is not None: out_clock = Signal() delay(m, out_clock, self.clock_skew, out=self.bus.clk) else: out_clock = self.bus.clk with m.If(reset_clock): m.d.sync += out_clock.eq(0) with m.Elif(advance_clock): m.d.sync += out_clock.eq(~out_clock) # # Latched control/addressing signals. # is_read = Signal() is_register = Signal() current_address = Signal(32) is_multipage = Signal() # # FSM datapath signals. # # Tracks whether we need to add an extra latency period between our # command and the data body. extra_latency = Signal() # Tracks how many cycles of latency we have remaining between a command # and the relevant data stages. latency_edges_remaining = Signal(range(0, self.HIGH_LATENCY_EDGES + 1)) # One cycle delayed version of RWDS. # This is used to detect edges in RWDS during reads, which semantically mean # we should accept new data. last_rwds = Signal.like(self.bus.rwds.i) m.d.sync += last_rwds.eq(self.bus.rwds.i) # Create a sync-domain version of our 'new data ready' signal. new_data_ready = self.new_data_ready # # Core operation FSM. # # Provide defaults for our control/status signals. m.d.sync += [ advance_clock.eq(1), reset_clock.eq(0), new_data_ready.eq(0), self.bus.cs.eq(1), self.bus.rwds.oe.eq(0), self.bus.dq.oe.eq(0), ] with m.FSM() as fsm: # IDLE state: waits for a transaction request with m.State('IDLE'): m.d.sync += reset_clock.eq(1) m.d.comb += self.idle.eq(1) # Once we have a transaction request, latch in our control # signals, and assert our chip-select. with m.If(self.start_transfer): m.next = 'LATCH_RWDS' m.d.sync += [ is_read.eq(~self.perform_write), is_register.eq(self.register_space), is_multipage.eq(~self.single_page), current_address.eq(self.address), ] with m.Else(): m.d.sync += self.bus.cs.eq(0) # LATCH_RWDS -- latch in the value of the RWDS signal, which determines # our read/write latency. Note that we advance the clock in this state, # as our out-of-phase clock signal will output the relevant data before # the next edge can occur. with m.State("LATCH_RWDS"): m.d.sync += extra_latency.eq(self.bus.rwds.i), m.next = "SHIFT_COMMAND0" # Commands, in order of bytes sent: # - WRBAAAAA # W => selects read or write; 1 = read, 0 = write # R => selects register or memory; 1 = register, 0 = memory # B => selects burst behavior; 0 = wrapped, 1 = linear # AAAAA => address bits [27:32] # # - AAAAAAAA => address bits [19:27] # - AAAAAAAA => address bits [11:19] # - AAAAAAAA => address bits [ 3:16] # - 00000000 => [reserved] # - 00000AAA => address bits [ 0: 3] # SHIFT_COMMANDx -- shift each of our command bytes out with m.State('SHIFT_COMMAND0'): m.next = 'SHIFT_COMMAND1' # Build our composite command byte. command_byte = Cat(current_address[27:32], is_multipage, is_register, is_read) # Output our first byte of our command. m.d.sync += [data_out.eq(command_byte), self.bus.dq.oe.eq(1)] # Note: it's felt that this is more readable with each of these # states defined explicitly. If you strongly disagree, feel free # to PR a for-loop, here.~ with m.State('SHIFT_COMMAND1'): m.d.sync += [ data_out.eq(current_address[19:27]), self.bus.dq.oe.eq(1) ] m.next = 'SHIFT_COMMAND2' with m.State('SHIFT_COMMAND2'): m.d.sync += [ data_out.eq(current_address[11:19]), self.bus.dq.oe.eq(1) ] m.next = 'SHIFT_COMMAND3' with m.State('SHIFT_COMMAND3'): m.d.sync += [ data_out.eq(current_address[3:16]), self.bus.dq.oe.eq(1) ] m.next = 'SHIFT_COMMAND4' with m.State('SHIFT_COMMAND4'): m.d.sync += [data_out.eq(0), self.bus.dq.oe.eq(1)] m.next = 'SHIFT_COMMAND5' with m.State('SHIFT_COMMAND5'): m.d.sync += [ data_out.eq(current_address[0:3]), self.bus.dq.oe.eq(1) ] # If we have a register write, we don't need to handle # any latency. Move directly to our SHIFT_DATA state. with m.If(is_register & ~is_read): m.next = 'WRITE_DATA_MSB' # Otherwise, react with either a short period of latency # or a longer one, depending on what the RAM requested via # RWDS. with m.Else(): m.next = "HANDLE_LATENCY" with m.If(extra_latency): m.d.sync += latency_edges_remaining.eq( self.HIGH_LATENCY_EDGES) with m.Else(): m.d.sync += latency_edges_remaining.eq( self.LOW_LATENCY_EDGES) # HANDLE_LATENCY -- applies clock edges until our latency period is over. with m.State('HANDLE_LATENCY'): m.d.sync += latency_edges_remaining.eq( latency_edges_remaining - 1) with m.If(latency_edges_remaining == 0): with m.If(is_read): m.next = 'READ_DATA_MSB' with m.Else(): m.next = 'WRITE_DATA_MSB' # STREAM_DATA_MSB -- scans in or out the first byte of data with m.State('READ_DATA_MSB'): # If RWDS has changed, the host has just sent us new data. with m.If(self.bus.rwds.i != last_rwds): m.d.sync += self.read_data[8:16].eq(data_in) m.next = 'READ_DATA_LSB' # STREAM_DATA_LSB -- scans in or out the second byte of data with m.State('READ_DATA_LSB'): # If RWDS has changed, the host has just sent us new data. # Sample it, and indicate that we now have a valid piece of new data. with m.If(self.bus.rwds.i != last_rwds): m.d.sync += [ self.read_data[0:8].eq(data_in), new_data_ready.eq(1) ] # If our controller is done with the transcation, end it. with m.If(self.final_word): m.next = 'RECOVERY' m.d.sync += advance_clock.eq(0) with m.Else(): #m.next = 'READ_DATA_MSB' m.next = 'RECOVERY' # RECOVERY state: wait for the required period of time before a new transaction with m.State('RECOVERY'): m.d.sync += [self.bus.cs.eq(0), advance_clock.eq(0)] # TODO: implement recovery m.next = 'IDLE' # TODO: implement write shift states with m.State("WRITE_DATA_MSB"): pass return m