Exemple #1
0
    def __init__(self, *, utmi_interface, mem_depth=8192):
        """
        Parameters:
            utmi_interface -- A record or elaboratable that presents a UTMI interface.
        """

        self.utmi = utmi_interface

        assert (mem_depth % 2) == 0, "mem_depth must be a power of 2"

        # Internal storage memory.
        self.mem = Memory(width=8, depth=mem_depth, name="analysis_ringbuffer")
        self.mem_size = mem_depth

        #
        # I/O port
        #
        self.stream = StreamInterface()

        self.idle = Signal()
        self.overrun = Signal()
        self.capturing = Signal()

        # Diagnostic I/O.
        self.sampling = Signal()
Exemple #2
0
    def build_param_store(self, m):
        # Create memory for post process params
        # Use SinglePortMemory here?
        param_mem = Memory(width=POST_PROCESS_PARAMS_WIDTH,
                           depth=Constants.MAX_CHANNEL_DEPTH)
        m.submodules['param_rp'] = rp = param_mem.read_port(transparent=False)
        m.submodules['param_wp'] = wp = param_mem.write_port()

        # Configure param writer
        m.submodules['param_writer'] = pw = ParamWriter()
        m.d.comb += connect(self.post_process_params, pw.input_data)
        m.d.comb += [
            pw.reset.eq(self.reset),
            wp.en.eq(pw.mem_we),
            wp.addr.eq(pw.mem_addr),
            wp.data.eq(pw.mem_data),
        ]

        # Configure param reader
        m.submodules['param_reader'] = reader = ReadingProducer()
        repeats = Mux(self.config.mode, Constants.SYS_ARRAY_HEIGHT,
                      Constants.SYS_ARRAY_HEIGHT // 2)
        m.d.comb += [
            # Reset reader whenever new parameters are written
            reader.reset.eq(pw.input_data.is_transferring()),
            reader.sizes.depth.eq(self.config.output_channel_depth),
            reader.sizes.repeats.eq(repeats),
            rp.addr.eq(reader.mem_addr),
            reader.mem_data.eq(rp.data),
        ]
        return reader.output_data
Exemple #3
0
    def __init__(self,
                 *,
                 signals,
                 sample_depth,
                 domain="sync",
                 sample_rate=60e6,
                 samples_pretrigger=1):
        self.domain = domain
        self.signals = signals
        self.inputs = Cat(*signals)
        self.sample_width = len(self.inputs)
        self.sample_depth = sample_depth
        self.samples_pretrigger = samples_pretrigger
        self.sample_rate = sample_rate
        self.sample_period = 1 / sample_rate

        #
        # Create a backing store for our samples.
        #
        self.mem = Memory(width=self.sample_width,
                          depth=sample_depth,
                          name="ila_buffer")

        #
        # I/O port
        #
        self.trigger = Signal()
        self.sampling = Signal()
        self.complete = Signal()

        self.captured_sample_number = Signal(range(0, self.sample_depth))
        self.captured_sample = Signal(self.sample_width)
Exemple #4
0
 def make_ram(n, init):
     mem = Memory(width=32, depth=4, init=init)
     self.m.submodules[f"rp{n}"] = rp = mem.read_port(transparent=False)
     self.m.d.comb += [
         ram_mux.lram_data[n].eq(rp.data),
         rp.addr.eq(ram_mux.lram_addr[n]),
         rp.en.eq(1),
         ram_mux.addr_in[n].eq(reader.ram_mux_addr[n]),
         reader.ram_mux_data[n].eq(ram_mux.data_out[n]),
     ]
Exemple #5
0
 def elab(self, m):
     mem = Memory(width=self.width, depth=self.depth, simulate=self.is_sim)
     m.submodules['wp'] = wp = mem.write_port()
     m.submodules['rp'] = rp = mem.read_port(transparent=False)
     m.d.comb += [
         wp.en.eq(self.w_en),
         wp.addr.eq(self.w_addr),
         wp.data.eq(self.w_data),
         rp.en.eq(1),
         rp.addr.eq(self.r_addr),
         self.r_data.eq(rp.data),
     ]
Exemple #6
0
    def __init__(self, fifo_depth=9*4):
        self._fifo_depth    = fifo_depth
        self.adat_out       = Signal()
        self.addr_in        = Signal(3)
        self.sample_in      = Signal(24)
        self.user_data_in   = Signal(4)
        self.valid_in       = Signal()
        self.ready_out      = Signal()
        self.last_in        = Signal()
        self.fifo_level_out = Signal(range(fifo_depth+1))
        self.underflow_out  = Signal()

        self.mem = Memory(width=24, depth=8, name="sample_buffer")
Exemple #7
0
    def elab(self, m):
        memory = Memory(width=self._data_width, depth=self._depth)
        m.submodules.rp = rp = memory.read_port(transparent=False)
        m.submodules.wp = wp = memory.write_port()

        m.d.comb += [
            wp.en.eq(self.write_enable),
            rp.en.eq(~self.write_enable),
            wp.addr.eq(self.write_addr),
            wp.data.eq(self.write_data),
            rp.addr.eq(self.read_addr),
            self.read_data.eq(rp.data)
        ]
Exemple #8
0
    def elab(self, m: Module):
        rp_data = []
        for i in range(4):
            mem = Memory(depth=self._depth // 4, width=32)
            # Set up read port
            m.submodules[f'rp_{i}'] = rp = mem.read_port(transparent=False)
            rp_data.append(rp.data)
            m.d.comb += rp.addr.eq(self.read_addr)
            m.d.comb += rp.en.eq(~self.write_enable)

            # Set up write port
            m.submodules[f'wp_{i}'] = wp = mem.write_port()
            m.d.comb += wp.addr.eq(self.write_addr[2:])
            m.d.comb += wp.data.eq(self.write_data)
            m.d.comb += wp.en.eq(self.write_enable
                                 & (i == self.write_addr[:2]))

        # Assemble all of the read port outputs into one, 128bit wide signal
        m.d.comb += self.read_data.eq(Cat(rp_data))
 def create_dut(self):
     fetcher = Mode1InputFetcher()
     # Connect RAM Mux with simulated LRAMs
     self.m.submodules["ram_mux"] = ram_mux = RamMux()
     self.m.d.comb += ram_mux.phase.eq(fetcher.ram_mux_phase)
     for i in range(4):
         padding = [0] * (self.BASE_ADDR // 16)
         init = (padding + self.data.input_data[i::4])[:1024]
         mem = Memory(width=32, depth=1024, init=init)
         rp = mem.read_port(transparent=False)
         self.m.d.comb += [
             ram_mux.lram_data[i].eq(rp.data),
             rp.addr.eq(ram_mux.lram_addr[i]),
             rp.en.eq(1),
             ram_mux.addr_in[i].eq(fetcher.ram_mux_addr[i]),
             fetcher.ram_mux_data[i].eq(ram_mux.data_out[i]),
         ]
         self.m.submodules[f"rp{i}"] = rp
     return fetcher
Exemple #10
0
    def elaborate(self, platform):
        m = Module()

        # Create our memory initializer from our initial value.
        initial_value = self._initialization_value(self.initial_value,
                                                   self.data_width,
                                                   self.granularity,
                                                   self.byteorder)

        # Create the the memory used to store our data.
        memory_depth = 2**self.local_addr_width
        memory = Memory(width=self.data_width,
                        depth=memory_depth,
                        init=initial_value,
                        name=self.name)

        # Grab a reference to the bits of our Wishbone bus that are relevant to us.
        local_address_bits = self.bus.adr[:self.local_addr_width]

        # Create a read port, and connect it to our Wishbone bus.
        m.submodules.rdport = read_port = memory.read_port()
        m.d.comb += [
            read_port.addr.eq(local_address_bits),
            self.bus.dat_r.eq(read_port.data)
        ]

        # If this is a read/write memory, create a write port, as well.
        if not self.read_only:
            m.submodules.wrport = write_port = memory.write_port(
                granularity=self.granularity)
            m.d.comb += [
                write_port.addr.eq(local_address_bits),
                write_port.data.eq(self.bus.dat_w)
            ]

            # Generate the write enables for each of our words.
            for i in range(self.bytes_per_word):
                m.d.comb += write_port.en[i].eq(
                    self.bus.cyc &  # Transaction is active.
                    self.bus.stb &  # Valid data is being provided.
                    self.bus.we &  # This is a write.
                    self.bus.sel[
                        i]  # The relevant setion of the datum is being targeted.
                )

        # We can handle any transaction request in a single cycle, when our RAM handles
        # the read or write. Accordingly, we'll ACK the cycle after any request.
        m.d.sync += self.bus.ack.eq(self.bus.cyc & self.bus.stb
                                    & ~self.bus.ack)

        return m
Exemple #11
0
class Memtest(Elaboratable):
    def __init__(self):
        self.mem = Memory(width=MEMWIDTH, depth=Firestarter.memdepth)

    def elaborate(self, platform):
        m = Module()
        led0 = platform.request("led", 0)
        led1 = platform.request("led", 1)
        m.submodules.rdport = rdport = self.mem.read_port()
        with m.If(rdport.data == 255):
            m.d.comb += led0.eq(0)
        with m.Else():
            m.d.comb += led0.eq(1)
        timer = Signal(range(round(100E6)))
        with m.If(timer > 100):
            m.d.comb += rdport.addr.eq(100)
        with m.Else():
            m.d.comb += rdport.addr.eq(0)
        m.d.sync += timer.eq(timer + 1)
        m.d.comb += led1.o.eq(timer[-1])
        return m
Exemple #12
0
class USBAnalyzer(Elaboratable):
    """ Core USB analyzer; backed by a small ringbuffer in FPGA block RAM.

    If you're looking to instantiate a full analyzer, you'll probably want to grab
    one of the DRAM-based ringbuffer variants (which are currently forthcoming).

    If you're looking to use this with a ULPI PHY, rather than the FPGA-convenient UTMI interface,
    grab the UTMITranslator from `luna.gateware.interface.ulpi`.

    Attributes
    ----------
    stream: StreamInterface(), output stream
        Stream that carries USB analyzer data.

    idle: Signal(), output
        Asserted iff the analyzer is not currently receiving data.
    overrun: Signal(), output
        Asserted iff the analyzer has received more data than it can store in its internal buffer.
        Occurs if :attr:``stream`` is not being read quickly enough.
    capturing: Signal(), output
        Asserted iff the analyzer is currently capturing a packet.


    Parameters
    ----------
    utmi_interface: UTMIInterface()
        The UTMI interface that carries the data to be analyzed.
    mem_depth: int, default=8192
        The depth of the analyzer's local ringbuffer, in bytes.
        Must be a power of 2.
    """

    # Current, we'll provide a packet header of 16 bits.
    HEADER_SIZE_BITS = 16
    HEADER_SIZE_BYTES = HEADER_SIZE_BITS // 8

    # Support a maximum payload size of 1024B, plus a 1-byte PID and a 2-byte CRC16.
    MAX_PACKET_SIZE_BYTES = 1024 + 1 + 2

    def __init__(self, *, utmi_interface, mem_depth=8192):
        """
        Parameters:
            utmi_interface -- A record or elaboratable that presents a UTMI interface.
        """

        self.utmi = utmi_interface

        assert (mem_depth % 2) == 0, "mem_depth must be a power of 2"

        # Internal storage memory.
        self.mem = Memory(width=8, depth=mem_depth, name="analysis_ringbuffer")
        self.mem_size = mem_depth

        #
        # I/O port
        #
        self.stream = StreamInterface()

        self.idle = Signal()
        self.overrun = Signal()
        self.capturing = Signal()

        # Diagnostic I/O.
        self.sampling = Signal()

    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 data in the FIFO.
            self.stream.valid.eq((fifo_count != 0) & self.idle),

            # Our data_out is always the output of our read port...
            self.stream.payload.eq(mem_read_port.data),
            self.sampling.eq(mem_write_port.en)
        ]

        # Once our consumer has accepted our current data, move to the next address.
        with m.If(self.stream.ready & self.stream.valid):
            m.d.usb += read_location.eq(read_location + 1)
            m.d.comb += mem_read_port.addr.eq(read_location + 1)

        with m.Else():
            m.d.comb += mem_read_port.addr.eq(read_location),

        #
        # FIFO count handling.
        #
        fifo_full = (fifo_count == self.mem_size)

        data_pop = Signal()
        data_push = Signal()
        m.d.comb += [
            data_pop.eq(self.stream.ready & self.stream.valid),
            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.idle.eq(f.ongoing("IDLE")),
                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"):

                byte_received = self.utmi.rx_valid & self.utmi.rx_active

                # 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(byte_received),
                    fifo_new_data.eq(byte_received),
                ]

                # Advance the write pointer each time we receive a bit.
                with m.If(byte_received):
                    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):
                    m.next = "EOP_1"

                    # 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[8:16]),
                    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)
                ]

                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
Exemple #13
0
    def elaborate(self, platform):
        self.m = m = Module()

        comb = m.d.comb
        sync = m.d.sync

        # CPU units used.
        logic = m.submodules.logic = LogicUnit()
        adder = m.submodules.adder = AdderUnit()
        shifter = m.submodules.shifter = ShifterUnit()
        compare = m.submodules.compare = CompareUnit()

        self.current_priv_mode = Signal(PrivModeBits,
                                        reset=PrivModeBits.MACHINE)

        csr_unit = self.csr_unit = m.submodules.csr_unit = CsrUnit(
            # TODO does '==' below produces the same synth result as .all()?
            in_machine_mode=self.current_priv_mode == PrivModeBits.MACHINE)
        exception_unit = self.exception_unit = m.submodules.exception_unit = ExceptionUnit(
            csr_unit=csr_unit, current_priv_mode=self.current_priv_mode)
        arbiter = self.arbiter = m.submodules.arbiter = MemoryArbiter(
            mem_config=self.mem_config,
            with_addr_translation=True,
            csr_unit=csr_unit,  # SATP register
            exception_unit=exception_unit,  # current privilege mode
        )
        mem_unit = m.submodules.mem_unit = MemoryUnit(mem_port=arbiter.port(
            priority=0))

        ibus = arbiter.port(priority=2)

        if self.with_debug:
            m.submodules.debug = self.debug = DebugUnit(self)
            self.debug_bus = arbiter.port(priority=1)

        # Current decoding state signals.
        instr = self.instr = Signal(32)
        funct3 = self.funct3 = Signal(3)
        funct7 = self.funct7 = Signal(7)
        rd = self.rd = Signal(5)
        rs1 = Signal(5)
        rs2 = Signal(5)
        rs1val = Signal(32)
        rs2val = Signal(32)
        rdval = Signal(32)  # calculated by unit, stored to register file
        imm = Signal(signed(12))
        csr_idx = Signal(12)
        uimm = Signal(20)
        opcode = self.opcode = Signal(InstrType)
        pc = self.pc = Signal(32, reset=CODE_START_ADDR)

        # at most one active_unit at any time
        active_unit = ActiveUnit()

        # Register file. Contains two read ports (for rs1, rs2) and one write port.
        regs = Memory(width=32, depth=32, init=self.reg_init)
        reg_read_port1 = m.submodules.reg_read_port1 = regs.read_port()
        reg_read_port2 = m.submodules.reg_read_port2 = regs.read_port()
        reg_write_port = (self.reg_write_port
                          ) = m.submodules.reg_write_port = regs.write_port()

        # Timer management.
        mtime = self.mtime = Signal(32)
        sync += mtime.eq(mtime + 1)
        comb += csr_unit.mtime.eq(mtime)

        self.halt = Signal()
        with m.If(csr_unit.mstatus.mie & csr_unit.mie.mtie):
            with m.If(mtime == csr_unit.mtimecmp):
                # 'halt' signal needs to be cleared when CPU jumps to trap handler.
                sync += [
                    self.halt.eq(1),
                ]

        comb += [
            exception_unit.m_instruction.eq(instr),
            exception_unit.m_pc.eq(pc),
            # TODO more
        ]

        # TODO
        # DebugModule is able to read and write GPR values.
        # if self.with_debug:
        #     comb += self.halt.eq(self.debug.HALT)
        # else:
        #     comb += self.halt.eq(0)

        # with m.If(self.halt):
        #     comb += [
        #         reg_read_port1.addr.eq(self.gprf_debug_addr),
        #         reg_write_port.addr.eq(self.gprf_debug_addr),
        #         reg_write_port.en.eq(self.gprf_debug_write_en)
        #     ]

        #     with m.If(self.gprf_debug_write_en):
        #         comb += reg_write_port.data.eq(self.gprf_debug_data)
        #     with m.Else():
        #         comb += self.gprf_debug_data.eq(reg_read_port1.data)
        with m.If(0):
            pass
        with m.Else():
            comb += [
                reg_read_port1.addr.eq(rs1),
                reg_read_port2.addr.eq(rs2),
                reg_write_port.addr.eq(rd),
                reg_write_port.data.eq(rdval),
                # reg_write_port.en set later
                rs1val.eq(reg_read_port1.data),
                rs2val.eq(reg_read_port2.data),
            ]

        comb += [
            # following is not true for all instrutions, but in specific cases will be overwritten later
            imm.eq(instr[20:32]),
            csr_idx.eq(instr[20:32]),
            uimm.eq(instr[12:]),
        ]

        # drive input signals of actually used unit.
        with m.If(active_unit.logic):
            comb += [
                logic.funct3.eq(funct3),
                logic.src1.eq(rs1val),
                logic.src2.eq(Mux(opcode == InstrType.OP_IMM, imm, rs2val)),
            ]
        with m.Elif(active_unit.adder):
            comb += [
                adder.src1.eq(rs1val),
                adder.src2.eq(Mux(opcode == InstrType.OP_IMM, imm, rs2val)),
            ]
        with m.Elif(active_unit.shifter):
            comb += [
                shifter.funct3.eq(funct3),
                shifter.funct7.eq(funct7),
                shifter.src1.eq(rs1val),
                shifter.shift.eq(
                    Mux(opcode == InstrType.OP_IMM, imm[0:5].as_unsigned(),
                        rs2val[0:5])),
            ]
        with m.Elif(active_unit.mem_unit):
            comb += [
                mem_unit.en.eq(1),
                mem_unit.funct3.eq(funct3),
                mem_unit.src1.eq(rs1val),
                mem_unit.src2.eq(rs2val),
                mem_unit.store.eq(opcode == InstrType.STORE),
                mem_unit.offset.eq(
                    Mux(opcode == InstrType.LOAD, imm, Cat(rd, imm[5:12]))),
            ]
        with m.Elif(active_unit.compare):
            comb += [
                compare.funct3.eq(funct3),
                # Compare Unit uses Adder for carry and overflow flags.
                adder.src1.eq(rs1val),
                adder.src2.eq(Mux(opcode == InstrType.OP_IMM, imm, rs2val)),
                # adder.sub set somewhere below
            ]
        with m.Elif(active_unit.branch):
            comb += [
                compare.funct3.eq(funct3),
                # Compare Unit uses Adder for carry and overflow flags.
                adder.src1.eq(rs1val),
                adder.src2.eq(rs2val),
                # adder.sub set somewhere below
            ]
        with m.Elif(active_unit.csr):
            comb += [
                csr_unit.func3.eq(funct3),
                csr_unit.csr_idx.eq(csr_idx),
                csr_unit.rs1.eq(rs1),
                csr_unit.rs1val.eq(rs1val),
                csr_unit.rd.eq(rd),
                csr_unit.en.eq(1),
            ]

        comb += [
            compare.negative.eq(adder.res[-1]),
            compare.overflow.eq(adder.overflow),
            compare.carry.eq(adder.carry),
            compare.zero.eq(adder.res == 0),
        ]

        # Decoding state (with redundancy - instr. type not known yet).
        # We use 'ibus.read_data' instead of 'instr' (that is driven by sync domain)
        # for getting registers to save 1 cycle.
        comb += [
            opcode.eq(instr[0:7]),
            rd.eq(instr[7:12]),
            funct3.eq(instr[12:15]),
            rs1.eq(instr[15:20]),
            rs2.eq(instr[20:25]),
            funct7.eq(instr[25:32]),
        ]

        def fetch_with_new_pc(pc: Signal):
            m.next = "FETCH"
            m.d.sync += active_unit.eq(0)
            m.d.sync += self.pc.eq(pc)

        def trap(cause: Optional[Union[TrapCause, IrqCause]], interrupt=False):
            fetch_with_new_pc(Cat(Const(0, 2), self.csr_unit.mtvec.base))
            if cause is None:
                return
            assert isinstance(cause, TrapCause) or isinstance(cause, IrqCause)
            e = exception_unit
            notifiers = e.irq_cause_map if interrupt else e.trap_cause_map
            m.d.comb += notifiers[cause].eq(1)

        self.fetch = Signal()
        interconnect_error = Signal()
        comb += interconnect_error.eq(exception_unit.m_store_error
                                      | exception_unit.m_fetch_error
                                      | exception_unit.m_load_error)
        with m.FSM():
            with m.State("FETCH"):
                with m.If(self.halt):
                    sync += self.halt.eq(0)
                    trap(IrqCause.M_TIMER_INTERRUPT, interrupt=True)
                with m.Else():
                    with m.If(pc & 0b11):
                        trap(TrapCause.FETCH_MISALIGNED)
                    with m.Else():
                        comb += [
                            ibus.en.eq(1),
                            ibus.store.eq(0),
                            ibus.addr.eq(pc),
                            ibus.mask.eq(0b1111),
                            ibus.is_fetch.eq(1),
                        ]
                    with m.If(interconnect_error):
                        trap(cause=None)
                    with m.If(ibus.ack):
                        sync += [
                            instr.eq(ibus.read_data),
                        ]
                        m.next = "DECODE"
            with m.State("DECODE"):
                comb += self.fetch.eq(
                    1
                )  # only for simulation, notify that 'instr' ready to use.
                m.next = "EXECUTE"
                # here, we have registers already fetched into rs1val, rs2val.
                with m.If(instr & 0b11 != 0b11):
                    trap(TrapCause.ILLEGAL_INSTRUCTION)
                with m.If(match_logic_unit(opcode, funct3, funct7)):
                    sync += [
                        active_unit.logic.eq(1),
                    ]
                with m.Elif(match_adder_unit(opcode, funct3, funct7)):
                    sync += [
                        active_unit.adder.eq(1),
                        adder.sub.eq((opcode == InstrType.ALU)
                                     & (funct7 == Funct7.SUB)),
                    ]
                with m.Elif(match_shifter_unit(opcode, funct3, funct7)):
                    sync += [
                        active_unit.shifter.eq(1),
                    ]
                with m.Elif(match_loadstore_unit(opcode, funct3, funct7)):
                    sync += [
                        active_unit.mem_unit.eq(1),
                    ]
                with m.Elif(match_compare_unit(opcode, funct3, funct7)):
                    sync += [
                        active_unit.compare.eq(1),
                        adder.sub.eq(1),
                    ]
                with m.Elif(match_lui(opcode, funct3, funct7)):
                    sync += [
                        active_unit.lui.eq(1),
                    ]
                    comb += [
                        reg_read_port1.addr.eq(rd),
                        # rd will be available in next cycle in rs1val
                    ]
                with m.Elif(match_auipc(opcode, funct3, funct7)):
                    sync += [
                        active_unit.auipc.eq(1),
                    ]
                with m.Elif(match_jal(opcode, funct3, funct7)):
                    sync += [
                        active_unit.jal.eq(1),
                    ]
                with m.Elif(match_jalr(opcode, funct3, funct7)):
                    sync += [
                        active_unit.jalr.eq(1),
                    ]
                with m.Elif(match_branch(opcode, funct3, funct7)):
                    sync += [
                        active_unit.branch.eq(1),
                        adder.sub.eq(1),
                    ]
                with m.Elif(match_csr(opcode, funct3, funct7)):
                    sync += [active_unit.csr.eq(1)]
                with m.Elif(match_mret(opcode, funct3, funct7)):
                    sync += [active_unit.mret.eq(1)]
                with m.Elif(match_sfence_vma(opcode, funct3, funct7)):
                    pass  # sfence.vma
                with m.Elif(opcode == 0b0001111):
                    pass  # fence
                with m.Else():
                    trap(TrapCause.ILLEGAL_INSTRUCTION)
            with m.State("EXECUTE"):
                with m.If(active_unit.logic):
                    sync += [
                        rdval.eq(logic.res),
                    ]
                with m.Elif(active_unit.adder):
                    sync += [
                        rdval.eq(adder.res),
                    ]
                with m.Elif(active_unit.shifter):
                    sync += [
                        rdval.eq(shifter.res),
                    ]
                with m.Elif(active_unit.mem_unit):
                    sync += [
                        rdval.eq(mem_unit.res),
                    ]
                with m.Elif(active_unit.compare):
                    sync += [
                        rdval.eq(compare.condition_met),
                    ]
                with m.Elif(active_unit.lui):
                    sync += [
                        rdval.eq(Cat(Const(0, 12), uimm)),
                    ]
                with m.Elif(active_unit.auipc):
                    sync += [
                        rdval.eq(pc + Cat(Const(0, 12), uimm)),
                    ]
                with m.Elif(active_unit.jal | active_unit.jalr):
                    sync += [
                        rdval.eq(pc + 4),
                    ]
                with m.Elif(active_unit.csr):
                    sync += [rdval.eq(csr_unit.rd_val)]

                # control flow mux - all traps need to be here, otherwise it will overwrite m.next statement.
                with m.If(active_unit.mem_unit):
                    with m.If(mem_unit.ack):
                        m.next = "WRITEBACK"
                        sync += active_unit.eq(0)
                    with m.Else():
                        m.next = "EXECUTE"
                    with m.If(interconnect_error):
                        # NOTE:
                        # the order of that 'If' is important.
                        # In case of error overwrite m.next above.
                        trap(cause=None)
                with m.Elif(active_unit.csr):
                    with m.If(csr_unit.illegal_insn):
                        trap(TrapCause.ILLEGAL_INSTRUCTION)
                    with m.Else():
                        with m.If(csr_unit.vld):
                            m.next = "WRITEBACK"
                            sync += active_unit.eq(0)
                        with m.Else():
                            m.next = "EXECUTE"
                with m.Elif(active_unit.mret):
                    comb += exception_unit.m_mret.eq(1)
                    fetch_with_new_pc(exception_unit.mepc)
                with m.Else():
                    # all units not specified by default take 1 cycle
                    m.next = "WRITEBACK"
                    sync += active_unit.eq(0)

                jal_offset = Signal(signed(21))
                comb += jal_offset.eq(
                    Cat(
                        Const(0, 1),
                        instr[21:31],
                        instr[20],
                        instr[12:20],
                        instr[31],
                    ).as_signed())

                pc_addend = Signal(signed(32))
                sync += pc_addend.eq(Mux(active_unit.jal, jal_offset, 4))

                branch_addend = Signal(signed(13))
                comb += branch_addend.eq(
                    Cat(
                        Const(0, 1),
                        instr[8:12],
                        instr[25:31],
                        instr[7],
                        instr[31],
                    ).as_signed()  # TODO is it ok that it's signed?
                )

                with m.If(active_unit.branch):
                    with m.If(compare.condition_met):
                        sync += pc_addend.eq(branch_addend)

                new_pc = Signal(32)
                is_jalr_latch = Signal()  # that's bad workaround
                with m.If(active_unit.jalr):
                    sync += is_jalr_latch.eq(1)
                    sync += new_pc.eq(rs1val.as_signed() + imm)

            with m.State("WRITEBACK"):
                with m.If(is_jalr_latch):
                    sync += pc.eq(new_pc)
                with m.Else():
                    sync += pc.eq(pc + pc_addend)
                sync += is_jalr_latch.eq(0)

                # Here, rdval is already calculated. If neccessary, put it into register file.
                should_write_rd = self.should_write_rd = Signal()
                writeback = self.writeback = Signal()
                # for riscv-dv simulation:
                # detect that instruction does not perform register write to avoid infinite loop
                # by checking writeback & should_write_rd
                # TODO it will break for trap-causing instructions.
                comb += writeback.eq(1)
                comb += should_write_rd.eq(
                    reduce(
                        or_,
                        [
                            match_shifter_unit(opcode, funct3, funct7),
                            match_adder_unit(opcode, funct3, funct7),
                            match_logic_unit(opcode, funct3, funct7),
                            match_load(opcode, funct3, funct7),
                            match_compare_unit(opcode, funct3, funct7),
                            match_lui(opcode, funct3, funct7),
                            match_auipc(opcode, funct3, funct7),
                            match_jal(opcode, funct3, funct7),
                            match_jalr(opcode, funct3, funct7),
                            match_csr(opcode, funct3, funct7),
                        ],
                    )
                    & (rd != 0))

                with m.If(should_write_rd):
                    comb += reg_write_port.en.eq(True)
                m.next = "FETCH"

        return m
Exemple #14
0
class IntegratedLogicAnalyzer(Elaboratable):
    """ Super-simple integrated-logic-analyzer generator class for LUNA.

    Attributes
    ----------
    trigger: Signal(), input
        A strobe that determines when we should start sampling.
    sampling: Signal(), output
        Indicates when sampling is in progress.

    complete: Signal(), output
        Indicates when sampling is complete and ready to be read.

    captured_sample_number: Signal(), input
        Selects which sample the ILA will output. Effectively the address for the ILA's
        sample buffer.
    captured_sample: Signal(), output
        The sample corresponding to the relevant sample number.
        Can be broken apart by using Cat(*signals).

    Parameters
    ----------
    signals: iterable of Signals
        An iterable of signals that should be captured by the ILA.
    sample_depth: int
        The depth of the desired buffer, in samples.

    domain: string
        The clock domain in which the ILA should operate.
    sample_rate: float
        Cosmetic indication of the sample rate. Used to format output.
    samples_pretrigger: int
        The number of our samples which should be captured _before_ the trigger.
        This also can act like an implicit synchronizer; so asynchronous inputs
        are allowed if this number is >= 2. Note that the trigger strobe is read
        on the rising edge of the clock.
    """
    def __init__(self,
                 *,
                 signals,
                 sample_depth,
                 domain="sync",
                 sample_rate=60e6,
                 samples_pretrigger=1):
        self.domain = domain
        self.signals = signals
        self.inputs = Cat(*signals)
        self.sample_width = len(self.inputs)
        self.sample_depth = sample_depth
        self.samples_pretrigger = samples_pretrigger
        self.sample_rate = sample_rate
        self.sample_period = 1 / sample_rate

        #
        # Create a backing store for our samples.
        #
        self.mem = Memory(width=self.sample_width,
                          depth=sample_depth,
                          name="ila_buffer")

        #
        # I/O port
        #
        self.trigger = Signal()
        self.sampling = Signal()
        self.complete = Signal()

        self.captured_sample_number = Signal(range(0, self.sample_depth))
        self.captured_sample = Signal(self.sample_width)

    def elaborate(self, platform):
        m = Module()

        # Memory ports.
        write_port = self.mem.write_port()
        read_port = self.mem.read_port(domain="sync")
        m.submodules += [write_port, read_port]

        # If necessary, create synchronized versions of the relevant signals.
        if self.samples_pretrigger >= 2:
            delayed_inputs = Signal.like(self.inputs)
            m.submodules += FFSynchronizer(self.inputs,
                                           delayed_inputs,
                                           stages=self.samples_pretrigger)
        elif self.samples_pretrigger == 1:
            delayed_inputs = Signal.like(self.inputs)
            m.d.sync += delayed_inputs.eq(self.inputs)
        else:
            delayed_inputs = self.inputs

        # Counter that keeps track of our write position.
        write_position = Signal(range(0, self.sample_depth))

        # Set up our write port to capture the input signals,
        # and our read port to provide the output.
        m.d.comb += [
            write_port.data.eq(delayed_inputs),
            write_port.addr.eq(write_position),
            self.captured_sample.eq(read_port.data),
            read_port.addr.eq(self.captured_sample_number)
        ]

        # Don't sample unless our FSM asserts our sample signal explicitly.
        m.d.sync += write_port.en.eq(0)

        with m.FSM(name="ila_state") as fsm:

            m.d.comb += self.sampling.eq(~fsm.ongoing("IDLE"))

            # IDLE: wait for the trigger strobe
            with m.State('IDLE'):

                with m.If(self.trigger):
                    m.next = 'SAMPLE'

                    # Grab a sample as our trigger is asserted.
                    m.d.sync += [
                        write_port.en.eq(1),
                        write_position.eq(0),
                        self.complete.eq(0),
                    ]

            # SAMPLE: do our sampling
            with m.State('SAMPLE'):

                # Sample until we run out of samples.
                m.d.sync += [
                    write_port.en.eq(1),
                    write_position.eq(write_position + 1),
                ]

                # If this is the last sample, we're done. Finish up.
                with m.If(write_position + 1 == self.sample_depth):
                    m.next = "IDLE"

                    m.d.sync += [self.complete.eq(1), write_port.en.eq(0)]

        # Convert our sync domain to the domain requested by the user, if necessary.
        if self.domain != "sync":
            m = DomainRenamer(self.domain)(m)

        return m
Exemple #15
0
    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)

        # Compute the location for the next write, accounting for wraparound. We'll not assume a binary-sized
        # buffer; so we'll compute the wraparound manually.
        next_write_pointer = Signal.like(current_write_pointer)
        with m.If(current_write_pointer == self.depth):
            m.d.comb += next_write_pointer.eq(0)
        with m.Else():
            m.d.comb += next_write_pointer.eq(current_write_pointer + 1)

        # If we're writing to the fifo, update our current write position.
        with m.If(self.write_en & ~self.full):
            m.d.sync += current_write_pointer.eq(next_write_pointer)

        # 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(next_write_pointer == 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
Exemple #16
0
class ADATTransmitter(Elaboratable):
    """transmit ADAT from a multiplexed stream of eight audio channels

    Parameters
    ----------
    fifo_depth: capacity of the FIFO containing the ADAT frames to be transmitted

    Attributes
    ----------
    adat_out: Signal
        the ADAT signal to be transmitted by the optical transmitter
        This input is unused at the moment. Instead the caller needs to ensure
    addr_in: Signal
        contains the ADAT channel number (0-7) of the current sample to be written
        into the currently assembled ADAT frame
    sample_in: Signal
        the 24 bit sample to be written into the channel slot given by addr_in
        in the currently assembled ADAT frame. The samples need to be committed
        in order of channel number (0-7)
    user_data_in: Signal
        the user data bits of the currently assembled frame. Will be committed,
        when ``last_in`` is strobed high
    valid_in: Signal
        commits the data at sample_in into the currently assembled frame,
        but only if ``ready_out`` is high
    ready_out: Signal
        outputs if there is space left in the transmit FIFO. It also will
        prevent any samples to be committed into the currently assembled ADAT frame
    last_in: Signal
        needs to be strobed when the last sample has been committed into the currently
        assembled ADAT frame. This will commit the user bits to the current ADAT frame
    fifo_level_out: Signal
        outputs the number of entries in the transmit FIFO
    underflow_out: Signal
        this underflow indicator will be strobed, when a new ADAT frame needs to be
        transmitted but the transmit FIFO is empty. In this case, the last
        ADAT frame will be transmitted again.
    """

    def __init__(self, fifo_depth=9*4):
        self._fifo_depth    = fifo_depth
        self.adat_out       = Signal()
        self.addr_in        = Signal(3)
        self.sample_in      = Signal(24)
        self.user_data_in   = Signal(4)
        self.valid_in       = Signal()
        self.ready_out      = Signal()
        self.last_in        = Signal()
        self.fifo_level_out = Signal(range(fifo_depth+1))
        self.underflow_out  = Signal()

        self.mem = Memory(width=24, depth=8, name="sample_buffer")

    @staticmethod
    def chunks(lst: list, n: int):
        """Yield successive n-sized chunks from lst."""
        for i in range(0, len(lst), n):
            yield lst[i:i + n]

    def elaborate(self, platform) -> Module:
        m = Module()
        sync = m.d.sync
        adat = m.d.adat
        comb = m.d.comb

        samples_write_port = self.mem.write_port()
        samples_read_port  = self.mem.read_port(domain='comb')
        m.submodules += [samples_write_port, samples_read_port]

        # the highest bit in the FIFO marks a frame border
        frame_border_flag = 24
        m.submodules.transmit_fifo = transmit_fifo = AsyncFIFO(width=25, depth=self._fifo_depth, w_domain="sync", r_domain="adat")

        # needed for output processing
        m.submodules.nrzi_encoder = nrzi_encoder = NRZIEncoder()

        transmitted_frame      = Signal(30)
        transmit_counter       = Signal(5)

        comb += [
            self.ready_out       .eq(transmit_fifo.w_rdy),
            self.fifo_level_out  .eq(transmit_fifo.w_level),
            self.adat_out        .eq(nrzi_encoder.nrzi_out),
            nrzi_encoder.data_in .eq(transmitted_frame.bit_select(transmit_counter, 1)),
            self.underflow_out   .eq(0)
        ]

        #
        # Fill the transmit FIFO in the sync domain
        #
        channel_counter = Signal(3)

        # make sure, en is only asserted when explicitly strobed
        comb += samples_write_port.en.eq(0)

        write_frame_border = [
            transmit_fifo.w_data .eq((1 << frame_border_flag) | self.user_data_in),
            transmit_fifo.w_en   .eq(1)
        ]

        with m.FSM():
            with m.State("DATA"):
                with m.If(self.ready_out):
                    with m.If(self.valid_in):
                        comb += [
                            samples_write_port.data.eq(self.sample_in),
                            samples_write_port.addr.eq(self.addr_in),
                            samples_write_port.en.eq(1)
                        ]

                        with m.If(self.last_in):
                            sync += channel_counter.eq(0)
                            comb += write_frame_border
                            m.next = "COMMIT"

                    # underflow: repeat last frame
                    with m.Elif(transmit_fifo.w_level == 0):
                        sync += channel_counter.eq(0)
                        comb += self.underflow_out.eq(1)
                        comb += write_frame_border
                        m.next = "COMMIT"

            with m.State("COMMIT"):
                with m.If(transmit_fifo.w_rdy):
                    comb += [
                        self.ready_out.eq(0),
                        samples_read_port.addr .eq(channel_counter),
                        transmit_fifo.w_data   .eq(samples_read_port.data),
                        transmit_fifo.w_en     .eq(1)
                    ]
                    sync += channel_counter.eq(channel_counter + 1)

                    with m.If(channel_counter == 7):
                        m.next = "DATA"

        #
        # Read the FIFO and send data in the adat domain
        #
        # 4b/5b coding: Every 24 bit channel has 6 nibbles.
        # 1 bit before the sync pad and one bit before the user data nibble
        filler_bits = [Const(1, 1) for _ in range(7)]

        adat += transmit_counter.eq(transmit_counter - 1)
        comb += transmit_fifo.r_en.eq(0)

        with m.If(transmit_counter == 0):
            with m.If(transmit_fifo.r_rdy):
                comb += transmit_fifo.r_en.eq(1)

                with m.If(transmit_fifo.r_data[frame_border_flag] == 0):
                    adat += [
                        transmit_counter.eq(29),
                        # generate the adat data for one channel 0b1dddd1dddd1dddd1dddd1dddd1dddd where d is the PCM audio data
                        transmitted_frame.eq(Cat(zip(list(self.chunks(transmit_fifo.r_data[:25], 4)), filler_bits)))
                    ]
                with m.Else():
                    adat += [
                        transmit_counter.eq(15),
                        # generate the adat sync_pad along with the user_bits 0b100000000001uuuu where u is user_data
                        transmitted_frame.eq((1 << 15) | (1 << 4) | transmit_fifo.r_data[:5])
                    ]

            with m.Else():
                # this should not happen: panic / stop transmitting.
                adat += [
                    transmitted_frame.eq(0x00),
                    transmit_counter.eq(4)
                ]

        return m
Exemple #17
0
 def __init__(self):
     self.mem = Memory(width=MEMWIDTH, depth=Firestarter.memdepth)