Пример #1
0
async def run_stress_test(dut, idle_inserter=None, backpressure_inserter=None):

    tb = TB(dut)

    byte_lanes = tb.axil_master.read_if.byte_lanes
    counter_size = max(
        int(os.getenv("PARAM_STAT_COUNT_WIDTH")) // 8, byte_lanes)
    stat_inc_width = len(dut.s_axis_stat_tdata)
    stat_id_width = len(dut.s_axis_stat_tid)

    await tb.cycle_reset()

    tb.set_idle_generator(idle_inserter)
    tb.set_backpressure_generator(backpressure_inserter)

    await Timer(4000, 'ns')

    async def worker(source, queue, count=128):
        for k in range(count):
            count = random.randrange(1, 2**stat_inc_width)
            num = random.randrange(0, 2**stat_id_width)

            await source.send(AxiStreamTransaction(tdata=count, tid=num))

            await queue.put((num, count))

            await Timer(random.randint(1, 1000), 'ns')

    workers = []
    queue = Queue()

    for k in range(16):
        workers.append(
            cocotb.start_soon(worker(tb.stat_source, queue, count=128)))

    while workers:
        await workers.pop(0).join()

    await Timer(1000, 'ns')

    data_ref = [0] * 2**stat_id_width

    while not queue.empty():
        num, count = await queue.get()
        data_ref[num] += count

    print(data_ref)

    data = await tb.axil_master.read_words(0,
                                           2**stat_id_width,
                                           ws=counter_size)

    print(data)

    assert data == data_ref

    await RisingEdge(dut.clk)
    await RisingEdge(dut.clk)
Пример #2
0
class TagContext:
    def __init__(self, manager):
        self.current_tag = 0
        self._cmd_queue = Queue()
        self._current_cmd = None
        self._resp_queue = Queue()
        self._cr = None
        self._manager = manager

    async def get_resp(self):
        return await self._resp_queue.get()

    def get_resp_nowait(self):
        return self._resp_queue.get_nowait()

    def _start(self):
        if self._cr is None:
            self._cr = cocotb.start_soon(self._process_queue())

    def _flush(self):
        flushed_cmds = []
        if self._cr is not None:
            self._cr.kill()
            self._cr = None
        self._manager._set_idle(self)
        if self._current_cmd is not None:
            flushed_cmds.append(self._current_cmd)
            self._current_cmd = None
        while not self._cmd_queue.empty():
            flushed_cmds.append(self._cmd_queue.get_nowait())
        while not self._resp_queue.empty():
            self._resp_queue.get_nowait()
        return flushed_cmds

    async def _process_queue(self):
        while True:
            cmd = await self._cmd_queue.get()
            self._current_cmd = cmd
            await self._manager._process(self, cmd)
            self._current_cmd = None

            if self._cmd_queue.empty() and self._resp_queue.empty():
                self._manager._set_idle(self)
Пример #3
0
class UartSource:
    def __init__(self, data, baud=9600, bits=8, stop_bits=1, *args, **kwargs):
        self.log = logging.getLogger(f"cocotb.{data._path}")
        self._data = data
        self._baud = baud
        self._bits = bits
        self._stop_bits = stop_bits

        self.log.info("UART source")
        self.log.info("cocotbext-uart version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-uart")

        super().__init__(*args, **kwargs)

        self.active = False
        self.queue = Queue()

        self._idle = Event()
        self._idle.set()

        self._data.setimmediatevalue(1)

        self.log.info("UART source configuration:")
        self.log.info("  Baud rate: %d bps", self._baud)
        self.log.info("  Byte size: %d bits", self._bits)
        self.log.info("  Stop bits: %f bits", self._stop_bits)

        self._run_cr = None
        self._restart()

    def _restart(self):
        if self._run_cr is not None:
            self._run_cr.kill()
        self._run_cr = cocotb.fork(
            self._run(self._data, self._baud, self._bits, self._stop_bits))

    @property
    def baud(self):
        return self._baud

    @baud.setter
    def baud(self, value):
        self.baud = value
        self._restart()

    @property
    def bits(self):
        return self._bits

    @bits.setter
    def bits(self, value):
        self.bits = value
        self._restart()

    @property
    def stop_bits(self):
        return self._stop_bits

    @stop_bits.setter
    def stop_bits(self, value):
        self.stop_bits = value
        self._restart()

    async def write(self, data):
        for b in data:
            await self.queue.put(int(b))
            self._idle.clear()

    def write_nowait(self, data):
        for b in data:
            self.queue.put_nowait(int(b))
        self._idle.clear()

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def idle(self):
        return self.empty() and not self.active

    def clear(self):
        while not self.queue.empty():
            frame = self.queue.get_nowait()

    async def wait(self):
        await self._idle.wait()

    async def _run(self, data, baud, bits, stop_bits):
        self.active = False

        bit_t = Timer(int(1e9 / self.baud), 'ns')
        stop_bit_t = Timer(int(1e9 / self.baud * stop_bits), 'ns')

        while True:
            if self.empty():
                self.active = False
                self._idle.set()

            b = await self.queue.get()
            self.active = True

            self.log.info("Write byte 0x%02x", b)

            # start bit
            data.value = 0
            await bit_t

            # data bits
            for k in range(self.bits):
                data.value = b & 1
                b >>= 1
                await bit_t

            # stop bit
            data.value = 1
            await stop_bit_t
Пример #4
0
class UartSink:
    def __init__(self, data, baud=9600, bits=8, stop_bits=1, *args, **kwargs):
        self.log = logging.getLogger(f"cocotb.{data._path}")
        self._data = data
        self._baud = baud
        self._bits = bits
        self._stop_bits = stop_bits

        self.log.info("UART sink")
        self.log.info("cocotbext-uart version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-uart")

        super().__init__(*args, **kwargs)

        self.active = False
        self.queue = Queue()
        self.sync = Event()

        self.log.info("UART sink configuration:")
        self.log.info("  Baud rate: %d bps", self._baud)
        self.log.info("  Byte size: %d bits", self._bits)
        self.log.info("  Stop bits: %f bits", self._stop_bits)

        self._run_cr = None
        self._restart()

    def _restart(self):
        if self._run_cr is not None:
            self._run_cr.kill()
        self._run_cr = cocotb.fork(
            self._run(self._data, self._baud, self._bits, self._stop_bits))

    @property
    def baud(self):
        return self._baud

    @baud.setter
    def baud(self, value):
        self.baud = value
        self._restart()

    @property
    def bits(self):
        return self._bits

    @bits.setter
    def bits(self, value):
        self.bits = value
        self._restart()

    @property
    def stop_bits(self):
        return self._stop_bits

    @stop_bits.setter
    def stop_bits(self, value):
        self.stop_bits = value
        self._restart()

    async def read(self, count=-1):
        while self.empty():
            self.sync.clear()
            await self.sync.wait()
        return self.read_nowait(count)

    def read_nowait(self, count=-1):
        if count < 0:
            count = self.queue.qsize()
        if self.bits == 8:
            data = bytearray()
        else:
            data = []
        for k in range(count):
            data.append(self.queue.get_nowait())
        return data

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def idle(self):
        return not self.active

    def clear(self):
        while not self.queue.empty():
            frame = self.queue.get_nowait()

    async def wait(self, timeout=0, timeout_unit='ns'):
        if not self.empty():
            return
        self.sync.clear()
        if timeout:
            await First(self.sync.wait(), Timer(timeout, timeout_unit))
        else:
            await self.sync.wait()

    async def _run(self, data, baud, bits, stop_bits):
        self.active = False

        half_bit_t = Timer(int(1e9 / self.baud / 2), 'ns')
        bit_t = Timer(int(1e9 / self.baud), 'ns')
        stop_bit_t = Timer(int(1e9 / self.baud * stop_bits), 'ns')

        while True:
            await FallingEdge(data)

            self.active = True

            # start bit
            await half_bit_t

            # data bits
            b = 0
            for k in range(bits):
                await bit_t
                b |= bool(data.value.integer) << k

            # stop bit
            await stop_bit_t

            self.log.info("Read byte 0x%02x", b)

            self.queue.put_nowait(b)
            self.sync.set()

            self.active = False
Пример #5
0
async def run_stress_test(dut, backpressure_inserter=None):

    tb = TB(dut)

    stat_count = len(dut.stat_valid)
    stat_inc_width = len(dut.stat_inc) // stat_count

    await tb.cycle_reset()

    tb.set_backpressure_generator(backpressure_inserter)

    async def worker(num, queue_ref, queue_drive, count=1024):
        for k in range(count):
            count = random.randrange(1, 2**stat_inc_width)

            await queue_drive.put(count)
            await queue_ref.put((num, count))

            await Timer(random.randint(1, 100), 'ns')

    workers = []
    queue_ref = Queue()
    queue_drive = [Queue() for k in range(stat_count)]

    for k in range(stat_count):
        workers.append(
            cocotb.start_soon(worker(k, queue_ref, queue_drive[k],
                                     count=1024)))

    async def driver(dut, queues):
        while True:
            await RisingEdge(dut.clk)

            inc = 0
            valid = 0
            for num, queue in enumerate(queues):
                if not queue.empty():
                    count = await queue.get()
                    inc |= (count) << (stat_inc_width * num)
                    valid |= 1 << num

            dut.stat_inc <= inc
            dut.stat_valid <= valid

    driver = cocotb.start_soon(driver(dut, queue_drive))

    while workers:
        await workers.pop(0).join()

    await Timer(1000, 'ns')

    driver.kill()

    await Timer(1000, 'ns')

    data_ref = [0] * stat_count

    while not queue_ref.empty():
        num, count = await queue_ref.get()
        data_ref[num] += count

    print(data_ref)

    data = [0] * stat_count

    while not tb.stat_sink.empty():
        stat = await tb.stat_sink.recv()
        # print(stat)

        assert stat.tdata != 0

        data[stat.tid] += stat.tdata

    print(data)

    assert data == data_ref

    await RisingEdge(dut.clk)
    await RisingEdge(dut.clk)
Пример #6
0
class AxiLiteMasterWrite(Reset):
    def __init__(self, bus, clock, reset=None, reset_active_level=True):
        self.bus = bus
        self.clock = clock
        self.reset = reset
        self.log = logging.getLogger(
            f"cocotb.{bus.aw._entity._name}.{bus.aw._name}")

        self.log.info("AXI lite master (write)")
        self.log.info("cocotbext-axi version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-axi")

        self.aw_channel = AxiLiteAWSource(bus.aw, clock, reset,
                                          reset_active_level)
        self.aw_channel.queue_occupancy_limit = 2
        self.w_channel = AxiLiteWSource(bus.w, clock, reset,
                                        reset_active_level)
        self.w_channel.queue_occupancy_limit = 2
        self.b_channel = AxiLiteBSink(bus.b, clock, reset, reset_active_level)
        self.b_channel.queue_occupancy_limit = 2

        self.write_command_queue = Queue()
        self.current_write_command = None

        self.int_write_resp_command_queue = Queue()
        self.current_write_resp_command = None

        self.in_flight_operations = 0
        self._idle = Event()
        self._idle.set()

        self.width = len(self.w_channel.bus.wdata)
        self.byte_size = 8
        self.byte_lanes = self.width // self.byte_size
        self.strb_mask = 2**self.byte_lanes - 1

        self.awprot_present = hasattr(self.bus.aw, "awprot")

        self.log.info("AXI lite master configuration:")
        self.log.info("  Address width: %d bits",
                      len(self.aw_channel.bus.awaddr))
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width,
                      self.byte_lanes)

        self.log.info("AXI lite master signals:")
        for bus in (self.bus.aw, self.bus.w, self.bus.b):
            for sig in sorted(
                    list(set().union(bus._signals, bus._optional_signals))):
                if hasattr(bus, sig):
                    self.log.info("  %s width: %d bits", sig,
                                  len(getattr(bus, sig)))
                else:
                    self.log.info("  %s: not present", sig)

        assert self.byte_lanes == len(self.w_channel.bus.wstrb)
        assert self.byte_lanes * self.byte_size == self.width

        self._process_write_cr = None
        self._process_write_resp_cr = None

        self._init_reset(reset, reset_active_level)

    def init_write(self, address, data, prot=AxiProt.NONSECURE, event=None):
        if event is None:
            event = Event()

        if not isinstance(event, Event):
            raise ValueError("Expected event object")

        if not self.awprot_present and prot != AxiProt.NONSECURE:
            raise ValueError(
                "awprot sideband signal value specified, but signal is not connected"
            )

        self.in_flight_operations += 1
        self._idle.clear()

        self.write_command_queue.put_nowait(
            AxiLiteWriteCmd(address, bytearray(data), prot, event))

        return event

    def idle(self):
        return not self.in_flight_operations

    async def wait(self):
        while not self.idle():
            await self._idle.wait()

    async def write(self, address, data, prot=AxiProt.NONSECURE):
        event = self.init_write(address, data, prot)
        await event.wait()
        return event.data

    async def write_words(self,
                          address,
                          data,
                          byteorder='little',
                          ws=2,
                          prot=AxiProt.NONSECURE):
        words = data
        data = bytearray()
        for w in words:
            data.extend(w.to_bytes(ws, byteorder))
        await self.write(address, data, prot)

    async def write_dwords(self,
                           address,
                           data,
                           byteorder='little',
                           prot=AxiProt.NONSECURE):
        await self.write_words(address, data, byteorder, 4, prot)

    async def write_qwords(self,
                           address,
                           data,
                           byteorder='little',
                           prot=AxiProt.NONSECURE):
        await self.write_words(address, data, byteorder, 8, prot)

    async def write_byte(self, address, data, prot=AxiProt.NONSECURE):
        await self.write(address, [data], prot)

    async def write_word(self,
                         address,
                         data,
                         byteorder='little',
                         ws=2,
                         prot=AxiProt.NONSECURE):
        await self.write_words(address, [data], byteorder, ws, prot)

    async def write_dword(self,
                          address,
                          data,
                          byteorder='little',
                          prot=AxiProt.NONSECURE):
        await self.write_dwords(address, [data], byteorder, prot)

    async def write_qword(self,
                          address,
                          data,
                          byteorder='little',
                          prot=AxiProt.NONSECURE):
        await self.write_qwords(address, [data], byteorder, prot)

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._process_write_cr is not None:
                self._process_write_cr.kill()
                self._process_write_cr = None
            if self._process_write_resp_cr is not None:
                self._process_write_resp_cr.kill()
                self._process_write_resp_cr = None

            self.aw_channel.clear()
            self.w_channel.clear()
            self.b_channel.clear()

            def flush_cmd(cmd):
                self.log.warning("Flushed write operation during reset: %s",
                                 cmd)
                if cmd.event:
                    cmd.event.set(None)

            while not self.write_command_queue.empty():
                cmd = self.write_command_queue.get_nowait()
                flush_cmd(cmd)

            if self.current_write_command:
                cmd = self.current_write_command
                self.current_write_command = None
                flush_cmd(cmd)

            while not self.int_write_resp_command_queue.empty():
                cmd = self.int_write_resp_command_queue.get_nowait()
                flush_cmd(cmd)

            if self.current_write_resp_command:
                cmd = self.current_write_resp_command
                self.current_write_resp_command = None
                flush_cmd(cmd)

            self.in_flight_operations = 0
            self._idle.set()
        else:
            self.log.info("Reset de-asserted")
            if self._process_write_cr is None:
                self._process_write_cr = cocotb.fork(self._process_write())
            if self._process_write_resp_cr is None:
                self._process_write_resp_cr = cocotb.fork(
                    self._process_write_resp())

    async def _process_write(self):
        while True:
            cmd = await self.write_command_queue.get()
            self.current_write_command = cmd

            word_addr = (cmd.address // self.byte_lanes) * self.byte_lanes

            start_offset = cmd.address % self.byte_lanes
            end_offset = (
                (cmd.address + len(cmd.data) - 1) % self.byte_lanes) + 1

            strb_start = (self.strb_mask << start_offset) & self.strb_mask
            strb_end = self.strb_mask >> (self.byte_lanes - end_offset)

            cycles = (len(cmd.data) + (cmd.address % self.byte_lanes) +
                      self.byte_lanes - 1) // self.byte_lanes

            resp_cmd = AxiLiteWriteRespCmd(cmd.address, len(cmd.data), cycles,
                                           cmd.prot, cmd.event)
            await self.int_write_resp_command_queue.put(resp_cmd)

            offset = 0

            if self.log.isEnabledFor(logging.INFO):
                self.log.info("Write start addr: 0x%08x prot: %s data: %s",
                              cmd.address, cmd.prot, ' '.join(
                                  (f'{c:02x}' for c in cmd.data)))

            for k in range(cycles):
                start = 0
                stop = self.byte_lanes
                strb = self.strb_mask

                if k == 0:
                    start = start_offset
                    strb &= strb_start
                if k == cycles - 1:
                    stop = end_offset
                    strb &= strb_end

                val = 0
                for j in range(start, stop):
                    val |= cmd.data[offset] << j * 8
                    offset += 1

                aw = self.aw_channel._transaction_obj()
                aw.awaddr = word_addr + k * self.byte_lanes
                aw.awprot = cmd.prot

                w = self.w_channel._transaction_obj()
                w.wdata = val
                w.wstrb = strb

                await self.aw_channel.send(aw)
                await self.w_channel.send(w)

            self.current_write_command = None

    async def _process_write_resp(self):
        while True:
            cmd = await self.int_write_resp_command_queue.get()
            self.current_write_resp_command = cmd

            resp = AxiResp.OKAY

            for k in range(cmd.cycles):
                b = await self.b_channel.recv()

                cycle_resp = AxiResp(getattr(b, 'bresp', AxiResp.OKAY))

                if cycle_resp != AxiResp.OKAY:
                    resp = cycle_resp

            self.log.info(
                "Write complete addr: 0x%08x prot: %s resp: %s length: %d",
                cmd.address, cmd.prot, resp, cmd.length)

            write_resp = AxiLiteWriteResp(cmd.address, cmd.length, resp)

            cmd.event.set(write_resp)

            self.current_write_resp_command = None

            self.in_flight_operations -= 1

            if self.in_flight_operations == 0:
                self._idle.set()
Пример #7
0
class AxiStreamBase(Reset):

    _signals = ["tdata"]
    _optional_signals = [
        "tvalid", "tready", "tlast", "tkeep", "tid", "tdest", "tuser"
    ]

    _type = "base"

    _init_x = False

    _valid_init = None
    _ready_init = None

    def __init__(self,
                 bus,
                 clock,
                 reset=None,
                 reset_active_level=True,
                 byte_size=None,
                 byte_lanes=None,
                 *args,
                 **kwargs):

        self.bus = bus
        self.clock = clock
        self.reset = reset
        self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")

        self.log.info("AXI stream %s", self._type)
        self.log.info("cocotbext-axi version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-axi")

        super().__init__(*args, **kwargs)

        self.active = False
        self.queue = Queue()
        self.dequeue_event = Event()
        self.current_frame = None
        self.idle_event = Event()
        self.idle_event.set()
        self.active_event = Event()

        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

        self.width = len(self.bus.tdata)
        self.byte_lanes = 1

        if self._valid_init is not None and hasattr(self.bus, "tvalid"):
            self.bus.tvalid.setimmediatevalue(self._valid_init)
        if self._ready_init is not None and hasattr(self.bus, "tready"):
            self.bus.tready.setimmediatevalue(self._ready_init)

        for sig in self._signals + self._optional_signals:
            if hasattr(self.bus, sig):
                if self._init_x and sig not in ("tvalid", "tready"):
                    v = getattr(self.bus, sig).value
                    v.binstr = 'x' * len(v)
                    getattr(self.bus, sig).setimmediatevalue(v)

        if hasattr(self.bus, "tkeep"):
            self.byte_lanes = len(self.bus.tkeep)
            if byte_size is not None or byte_lanes is not None:
                raise ValueError(
                    "Cannot specify byte_size or byte_lanes if tkeep is connected"
                )
        else:
            if byte_lanes is not None:
                self.byte_lanes = byte_lanes
                if byte_size is not None:
                    raise ValueError(
                        "Cannot specify both byte_size and byte_lanes")
            elif byte_size is not None:
                self.byte_lanes = self.width // byte_size

        self.byte_size = self.width // self.byte_lanes
        self.byte_mask = 2**self.byte_size - 1

        self.log.info("AXI stream %s configuration:", self._type)
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width,
                      self.byte_lanes)
        self.log.info(
            "  tvalid: %s",
            "present" if hasattr(self.bus, "tvalid") else "not present")
        self.log.info(
            "  tready: %s",
            "present" if hasattr(self.bus, "tready") else "not present")
        self.log.info(
            "  tlast: %s",
            "present" if hasattr(self.bus, "tlast") else "not present")
        if hasattr(self.bus, "tkeep"):
            self.log.info("  tkeep width: %d bits", len(self.bus.tkeep))
        else:
            self.log.info("  tkeep: not present")
        if hasattr(self.bus, "tid"):
            self.log.info("  tid width: %d bits", len(self.bus.tid))
        else:
            self.log.info("  tid: not present")
        if hasattr(self.bus, "tdest"):
            self.log.info("  tdest width: %d bits", len(self.bus.tdest))
        else:
            self.log.info("  tdest: not present")
        if hasattr(self.bus, "tuser"):
            self.log.info("  tuser width: %d bits", len(self.bus.tuser))
        else:
            self.log.info("  tuser: not present")

        if self.byte_lanes * self.byte_size != self.width:
            raise ValueError(
                f"Bus does not evenly divide into byte lanes "
                f"({self.byte_lanes} * {self.byte_size} != {self.width})")

        self._run_cr = None

        self._init_reset(reset, reset_active_level)

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def clear(self):
        while not self.queue.empty():
            frame = self.queue.get_nowait()
            frame.sim_time_end = None
            frame.handle_tx_complete()
        self.dequeue_event.set()
        self.idle_event.set()
        self.active_event.clear()
        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._run_cr is not None:
                self._run_cr.kill()
                self._run_cr = None

            self.active = False

            if self.queue.empty():
                self.idle_event.set()
        else:
            self.log.info("Reset de-asserted")
            if self._run_cr is None:
                self._run_cr = cocotb.fork(self._run())

    async def _run(self):
        raise NotImplementedError()
Пример #8
0
class MiiSource(Reset):
    def __init__(self,
                 data,
                 er,
                 dv,
                 clock,
                 reset=None,
                 enable=None,
                 reset_active_level=True,
                 *args,
                 **kwargs):
        self.log = logging.getLogger(f"cocotb.{data._path}")
        self.data = data
        self.er = er
        self.dv = dv
        self.clock = clock
        self.reset = reset
        self.enable = enable

        self.log.info("MII source")
        self.log.info("cocotbext-eth version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-eth")

        super().__init__(*args, **kwargs)

        self.active = False
        self.queue = Queue()
        self.dequeue_event = Event()
        self.current_frame = None
        self.idle_event = Event()
        self.idle_event.set()

        self.ifg = 12

        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

        self.queue_occupancy_limit_bytes = -1
        self.queue_occupancy_limit_frames = -1

        self.width = 4
        self.byte_width = 1

        assert len(self.data) == 4
        self.data.setimmediatevalue(0)
        if self.er is not None:
            assert len(self.er) == 1
            self.er.setimmediatevalue(0)
        assert len(self.dv) == 1
        self.dv.setimmediatevalue(0)

        self._run_cr = None

        self._init_reset(reset, reset_active_level)

    async def send(self, frame):
        while self.full():
            self.dequeue_event.clear()
            await self.dequeue_event.wait()
        frame = GmiiFrame(frame)
        await self.queue.put(frame)
        self.idle_event.clear()
        self.queue_occupancy_bytes += len(frame)
        self.queue_occupancy_frames += 1

    def send_nowait(self, frame):
        if self.full():
            raise QueueFull()
        frame = GmiiFrame(frame)
        self.queue.put_nowait(frame)
        self.idle_event.clear()
        self.queue_occupancy_bytes += len(frame)
        self.queue_occupancy_frames += 1

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def full(self):
        if self.queue_occupancy_limit_bytes > 0 and self.queue_occupancy_bytes > self.queue_occupancy_limit_bytes:
            return True
        elif self.queue_occupancy_limit_frames > 0 and self.queue_occupancy_frames > self.queue_occupancy_limit_frames:
            return True
        else:
            return False

    def idle(self):
        return self.empty() and not self.active

    def clear(self):
        while not self.queue.empty():
            frame = self.queue.get_nowait()
            frame.sim_time_end = None
            frame.handle_tx_complete()
        self.dequeue_event.set()
        self.idle_event.set()
        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

    async def wait(self):
        await self.idle_event.wait()

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._run_cr is not None:
                self._run_cr.kill()
                self._run_cr = None

            self.active = False
            self.data.value = 0
            if self.er is not None:
                self.er.value = 0
            self.dv.value = 0

            if self.current_frame:
                self.log.warning("Flushed transmit frame during reset: %s",
                                 self.current_frame)
                self.current_frame.handle_tx_complete()
                self.current_frame = None

            if self.queue.empty():
                self.idle_event.set()
        else:
            self.log.info("Reset de-asserted")
            if self._run_cr is None:
                self._run_cr = cocotb.start_soon(self._run())

    async def _run(self):
        frame = None
        frame_offset = 0
        frame_data = None
        frame_error = None
        ifg_cnt = 0
        self.active = False

        clock_edge_event = RisingEdge(self.clock)

        while True:
            await clock_edge_event

            if self.enable is None or self.enable.value:
                if ifg_cnt > 0:
                    # in IFG
                    ifg_cnt -= 1

                elif frame is None and not self.queue.empty():
                    # send frame
                    frame = self.queue.get_nowait()
                    self.dequeue_event.set()
                    self.queue_occupancy_bytes -= len(frame)
                    self.queue_occupancy_frames -= 1
                    self.current_frame = frame
                    frame.sim_time_start = get_sim_time()
                    frame.sim_time_sfd = None
                    frame.sim_time_end = None
                    self.log.info("TX frame: %s", frame)
                    frame.normalize()

                    # convert to MII
                    frame_data = []
                    frame_error = []
                    for b, e in zip(frame.data, frame.error):
                        frame_data.append(b & 0x0F)
                        frame_data.append(b >> 4)
                        frame_error.append(e)
                        frame_error.append(e)

                    self.active = True
                    frame_offset = 0

                if frame is not None:
                    d = frame_data[frame_offset]
                    if frame.sim_time_sfd is None and d == 0xD:
                        frame.sim_time_sfd = get_sim_time()
                    self.data.value = d
                    if self.er is not None:
                        self.er.value = frame_error[frame_offset]
                    self.dv.value = 1
                    frame_offset += 1

                    if frame_offset >= len(frame_data):
                        ifg_cnt = max(self.ifg, 1)
                        frame.sim_time_end = get_sim_time()
                        frame.handle_tx_complete()
                        frame = None
                        self.current_frame = None
                else:
                    self.data.value = 0
                    if self.er is not None:
                        self.er.value = 0
                    self.dv.value = 0
                    self.active = False
                    self.idle_event.set()
Пример #9
0
class BaseRSerdesSource():
    def __init__(self,
                 data,
                 header,
                 clock,
                 enable=None,
                 slip=None,
                 scramble=True,
                 reverse=False,
                 *args,
                 **kwargs):
        self.log = logging.getLogger(f"cocotb.{data._path}")
        self.data = data
        self.header = header
        self.clock = clock
        self.enable = enable
        self.slip = slip
        self.scramble = scramble
        self.reverse = reverse

        self.log.info("BASE-R serdes source")
        self.log.info("Copyright (c) 2021 Alex Forencich")
        self.log.info("https://github.com/alexforencich/verilog-ethernet")

        super().__init__(*args, **kwargs)

        self.active = False
        self.queue = Queue()
        self.dequeue_event = Event()
        self.current_frame = None
        self.idle_event = Event()
        self.idle_event.set()

        self.enable_dic = True
        self.ifg = 12
        self.force_offset_start = False

        self.bit_offset = 0

        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

        self.queue_occupancy_limit_bytes = -1
        self.queue_occupancy_limit_frames = -1

        self.width = len(self.data)
        self.byte_size = 8
        self.byte_lanes = 8

        assert self.width == self.byte_lanes * self.byte_size

        self.log.info("BASE-R serdes source model configuration")
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width,
                      self.byte_lanes)
        self.log.info("  Enable scrambler: %s", self.scramble)
        self.log.info("  Bit reverse: %s", self.reverse)

        self.data.setimmediatevalue(0)
        self.header.setimmediatevalue(0)

        self._run_cr = cocotb.fork(self._run())

    async def send(self, frame):
        while self.full():
            self.dequeue_event.clear()
            await self.dequeue_event.wait()
        frame = XgmiiFrame(frame)
        await self.queue.put(frame)
        self.idle_event.clear()
        self.queue_occupancy_bytes += len(frame)
        self.queue_occupancy_frames += 1

    def send_nowait(self, frame):
        if self.full():
            raise QueueFull()
        frame = XgmiiFrame(frame)
        self.queue.put_nowait(frame)
        self.idle_event.clear()
        self.queue_occupancy_bytes += len(frame)
        self.queue_occupancy_frames += 1

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def full(self):
        if self.queue_occupancy_limit_bytes > 0 and self.queue_occupancy_bytes > self.queue_occupancy_limit_bytes:
            return True
        elif self.queue_occupancy_limit_frames > 0 and self.queue_occupancy_frames > self.queue_occupancy_limit_frames:
            return True
        else:
            return False

    def idle(self):
        return self.empty() and not self.active

    def clear(self):
        while not self.queue.empty():
            frame = self.queue.get_nowait()
            frame.sim_time_end = None
            frame.handle_tx_complete()
        self.dequeue_event.set()
        self.idle_event.set()
        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

    async def wait(self):
        await self.idle_event.wait()

    async def _run(self):
        frame = None
        frame_offset = 0
        ifg_cnt = 0
        deficit_idle_cnt = 0
        scrambler_state = 0
        last_d = 0
        self.active = False

        while True:
            await RisingEdge(self.clock)

            if self.enable is None or self.enable.value:
                if ifg_cnt + deficit_idle_cnt > self.byte_lanes - 1 or (
                        not self.enable_dic and ifg_cnt > 4):
                    # in IFG
                    ifg_cnt = ifg_cnt - self.byte_lanes
                    if ifg_cnt < 0:
                        if self.enable_dic:
                            deficit_idle_cnt = max(deficit_idle_cnt + ifg_cnt,
                                                   0)
                        ifg_cnt = 0

                elif frame is None:
                    # idle
                    if not self.queue.empty():
                        # send frame
                        frame = self.queue.get_nowait()
                        self.dequeue_event.set()
                        self.queue_occupancy_bytes -= len(frame)
                        self.queue_occupancy_frames -= 1
                        self.current_frame = frame
                        frame.sim_time_start = get_sim_time()
                        frame.sim_time_sfd = None
                        frame.sim_time_end = None
                        self.log.info("TX frame: %s", frame)
                        frame.normalize()
                        frame.start_lane = 0
                        assert frame.data[0] == EthPre.PRE
                        assert frame.ctrl[0] == 0
                        frame.data[0] = XgmiiCtrl.START
                        frame.ctrl[0] = 1
                        frame.data.append(XgmiiCtrl.TERM)
                        frame.ctrl.append(1)

                        # offset start
                        if self.enable_dic:
                            min_ifg = 3 - deficit_idle_cnt
                        else:
                            min_ifg = 0

                        if self.byte_lanes > 4 and (ifg_cnt > min_ifg or
                                                    self.force_offset_start):
                            ifg_cnt = ifg_cnt - 4
                            frame.start_lane = 4
                            frame.data = bytearray(
                                [XgmiiCtrl.IDLE] * 4) + frame.data
                            frame.ctrl = [1] * 4 + frame.ctrl

                        if self.enable_dic:
                            deficit_idle_cnt = max(deficit_idle_cnt + ifg_cnt,
                                                   0)
                        ifg_cnt = 0
                        self.active = True
                        frame_offset = 0
                    else:
                        # clear counters
                        deficit_idle_cnt = 0
                        ifg_cnt = 0

                if frame is not None:
                    dl = bytearray()
                    cl = []

                    for k in range(self.byte_lanes):
                        if frame is not None:
                            d = frame.data[frame_offset]
                            if frame.sim_time_sfd is None and d == EthPre.SFD:
                                frame.sim_time_sfd = get_sim_time()
                            dl.append(d)
                            cl.append(frame.ctrl[frame_offset])
                            frame_offset += 1

                            if frame_offset >= len(frame.data):
                                ifg_cnt = max(self.ifg - (self.byte_lanes - k),
                                              0)
                                frame.sim_time_end = get_sim_time()
                                frame.handle_tx_complete()
                                frame = None
                                self.current_frame = None
                        else:
                            dl.append(XgmiiCtrl.IDLE)
                            cl.append(1)

                    # remap control characters
                    ctrl = sum(
                        xgmii_ctrl_to_baser_mapping.get(d, BaseRCtrl.ERROR) <<
                        i * 7 for i, d in enumerate(dl))

                    if not any(cl):
                        # data
                        header = BaseRSync.DATA
                        data = int.from_bytes(dl, 'little')
                    else:
                        # control
                        header = BaseRSync.CTRL
                        if cl[0] and dl[0] == XgmiiCtrl.START and not any(
                                cl[1:]):
                            # start in lane 0
                            data = BaseRBlockType.START_0
                            for i in range(1, 8):
                                data |= dl[i] << i * 8
                        elif cl[4] and dl[4] == XgmiiCtrl.START and not any(
                                cl[5:]):
                            # start in lane 4
                            if cl[0] and (dl[0] == XgmiiCtrl.SEQ_OS or dl[0]
                                          == XgmiiCtrl.SIG_OS) and not any(
                                              cl[1:4]):
                                # ordered set in lane 0
                                data = BaseRBlockType.OS_START
                                for i in range(1, 4):
                                    data |= dl[i] << i * 8
                                if dl[0] == XgmiiCtrl.SIG_OS:
                                    # signal ordered set
                                    data |= BaseRO.SIG_OS << 32
                            else:
                                # other control
                                data = BaseRBlockType.START_4 | (
                                    ctrl & 0xfffffff) << 8

                            for i in range(5, 8):
                                data |= dl[i] << i * 8
                        elif cl[0] and (dl[0] == XgmiiCtrl.SEQ_OS or dl[0]
                                        == XgmiiCtrl.SIG_OS) and not any(
                                            cl[1:4]):
                            # ordered set in lane 0
                            if cl[4] and (dl[4] == XgmiiCtrl.SEQ_OS or dl[4]
                                          == XgmiiCtrl.SIG_OS) and not any(
                                              cl[5:8]):
                                # ordered set in lane 4
                                data = BaseRBlockType.OS_04
                                for i in range(5, 8):
                                    data |= dl[i] << i * 8
                                if dl[4] == XgmiiCtrl.SIG_OS:
                                    # signal ordered set
                                    data |= BaseRO.SIG_OS << 36
                            else:
                                data = BaseRBlockType.OS_0 | (
                                    ctrl & 0xfffffff) << 40
                            for i in range(1, 4):
                                data |= dl[i] << i * 8
                            if dl[0] == XgmiiCtrl.SIG_OS:
                                # signal ordered set
                                data |= BaseRO.SIG_OS << 32
                        elif cl[4] and (dl[4] == XgmiiCtrl.SEQ_OS or dl[4]
                                        == XgmiiCtrl.SIG_OS) and not any(
                                            cl[5:8]):
                            # ordered set in lane 4
                            data = BaseRBlockType.OS_4 | (ctrl
                                                          & 0xfffffff) << 8
                            for i in range(5, 8):
                                data |= dl[i] << i * 8
                            if dl[4] == XgmiiCtrl.SIG_OS:
                                # signal ordered set
                                data |= BaseRO.SIG_OS << 36
                        elif cl[0] and dl[0] == XgmiiCtrl.TERM:
                            # terminate in lane 0
                            data = BaseRBlockType.TERM_0 | (
                                ctrl & 0xffffffffffff80) << 8
                        elif cl[1] and dl[1] == XgmiiCtrl.TERM and not cl[0]:
                            # terminate in lane 1
                            data = BaseRBlockType.TERM_1 | (
                                ctrl & 0xffffffffffc000) << 8 | dl[0] << 8
                        elif cl[2] and dl[2] == XgmiiCtrl.TERM and not any(
                                cl[0:2]):
                            # terminate in lane 2
                            data = BaseRBlockType.TERM_2 | (
                                ctrl & 0xffffffffe00000) << 8
                            for i in range(2):
                                data |= dl[i] << ((i + 1) * 8)
                        elif cl[3] and dl[3] == XgmiiCtrl.TERM and not any(
                                cl[0:3]):
                            # terminate in lane 3
                            data = BaseRBlockType.TERM_3 | (
                                ctrl & 0xfffffff0000000) << 8
                            for i in range(3):
                                data |= dl[i] << ((i + 1) * 8)
                        elif cl[4] and dl[4] == XgmiiCtrl.TERM and not any(
                                cl[0:4]):
                            # terminate in lane 4
                            data = BaseRBlockType.TERM_4 | (
                                ctrl & 0xfffff800000000) << 8
                            for i in range(4):
                                data |= dl[i] << ((i + 1) * 8)
                        elif cl[5] and dl[5] == XgmiiCtrl.TERM and not any(
                                cl[0:5]):
                            # terminate in lane 5
                            data = BaseRBlockType.TERM_5 | (
                                ctrl & 0xfffc0000000000) << 8
                            for i in range(5):
                                data |= dl[i] << ((i + 1) * 8)
                        elif cl[6] and dl[6] == XgmiiCtrl.TERM and not any(
                                cl[0:6]):
                            # terminate in lane 6
                            data = BaseRBlockType.TERM_6 | (
                                ctrl & 0xfe000000000000) << 8
                            for i in range(6):
                                data |= dl[i] << ((i + 1) * 8)
                        elif cl[7] and dl[7] == XgmiiCtrl.TERM and not any(
                                cl[0:7]):
                            # terminate in lane 7
                            data = BaseRBlockType.TERM_7
                            for i in range(7):
                                data |= dl[i] << ((i + 1) * 8)
                        else:
                            # all control
                            data = BaseRBlockType.CTRL | ctrl << 8
                else:
                    data = BaseRBlockType.CTRL
                    header = BaseRSync.CTRL
                    self.active = False
                    self.idle_event.set()

                if self.scramble:
                    # 64b/66b scrambler
                    b = 0
                    for i in range(len(self.data)):
                        if bool(scrambler_state & (1 << 38)) ^ bool(
                                scrambler_state
                                & (1 << 57)) ^ bool(data & (1 << i)):
                            scrambler_state = (
                                (scrambler_state & 0x1ffffffffffffff) << 1) | 1
                            b = b | (1 << i)
                        else:
                            scrambler_state = (scrambler_state
                                               & 0x1ffffffffffffff) << 1
                    data = b

                if self.slip is not None and self.slip.value:
                    self.bit_offset += 1

                self.bit_offset = max(0, self.bit_offset) % 66

                if self.bit_offset != 0:
                    d = data << 2 | header

                    out_d = ((last_d | d << 66) >>
                             66 - self.bit_offset) & 0x3ffffffffffffffff

                    last_d = d

                    data = out_d >> 2
                    header = out_d & 3

                if self.reverse:
                    # bit reverse
                    data = sum(1 << (63 - i) for i in range(64)
                               if (data >> i) & 1)
                    header = sum(1 << (1 - i) for i in range(2)
                                 if (header >> i) & 1)

                self.data <= data
                self.header <= header
Пример #10
0
class BaseRSerdesSink:
    def __init__(self,
                 data,
                 header,
                 clock,
                 enable=None,
                 scramble=True,
                 reverse=False,
                 *args,
                 **kwargs):
        self.log = logging.getLogger(f"cocotb.{data._path}")
        self.data = data
        self.header = header
        self.clock = clock
        self.enable = enable
        self.scramble = scramble
        self.reverse = reverse

        self.log.info("BASE-R serdes sink")
        self.log.info("Copyright (c) 2021 Alex Forencich")
        self.log.info("https://github.com/alexforencich/verilog-ethernet")

        super().__init__(*args, **kwargs)

        self.active = False
        self.queue = Queue()
        self.active_event = Event()

        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

        self.width = len(self.data)
        self.byte_size = 8
        self.byte_lanes = 8

        assert self.width == self.byte_lanes * self.byte_size

        self.log.info("BASE-R serdes sink model configuration")
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width,
                      self.byte_lanes)
        self.log.info("  Enable scrambler: %s", self.scramble)
        self.log.info("  Bit reverse: %s", self.reverse)

        self._run_cr = cocotb.fork(self._run())

    def _recv(self, frame, compact=True):
        if self.queue.empty():
            self.active_event.clear()
        self.queue_occupancy_bytes -= len(frame)
        self.queue_occupancy_frames -= 1
        if compact:
            frame.compact()
        return frame

    async def recv(self, compact=True):
        frame = await self.queue.get()
        return self._recv(frame, compact)

    def recv_nowait(self, compact=True):
        frame = self.queue.get_nowait()
        return self._recv(frame, compact)

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def idle(self):
        return not self.active

    def clear(self):
        while not self.queue.empty():
            self.queue.get_nowait()
        self.active_event.clear()
        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

    async def wait(self, timeout=0, timeout_unit=None):
        if not self.empty():
            return
        if timeout:
            await First(self.active_event.wait(), Timer(timeout, timeout_unit))
        else:
            await self.active_event.wait()

    async def _run(self):
        frame = None
        scrambler_state = 0
        self.active = False

        while True:
            await RisingEdge(self.clock)

            if self.enable is None or self.enable.value:
                data = self.data.value.integer
                header = self.header.value.integer

                if self.reverse:
                    # bit reverse
                    data = sum(1 << (63 - i) for i in range(64)
                               if (data >> i) & 1)
                    header = sum(1 << (1 - i) for i in range(2)
                                 if (header >> i) & 1)

                if self.scramble:
                    # 64b/66b descrambler
                    b = 0
                    for i in range(len(self.data)):
                        if bool(scrambler_state & (1 << 38)) ^ bool(
                                scrambler_state
                                & (1 << 57)) ^ bool(data & (1 << i)):
                            b = b | (1 << i)
                        scrambler_state = (scrambler_state & 0x1ffffffffffffff
                                           ) << 1 | bool(data & (1 << i))
                    data = b

                # 10GBASE-R decoding

                # remap control characters
                ctrl = bytearray(
                    baser_ctrl_to_xgmii_mapping.get((data >> i * 7 + 8)
                                                    & 0x7f, XgmiiCtrl.ERROR)
                    for i in range(8))

                data = data.to_bytes(8, 'little')

                dl = bytearray()
                cl = []
                if header == BaseRSync.DATA:
                    # data
                    dl = data
                    cl = [0] * 8
                elif header == BaseRSync.CTRL:
                    if data[0] == BaseRBlockType.CTRL:
                        # C7 C6 C5 C4 C3 C2 C1 C0 BT
                        dl = ctrl
                        cl = [1] * 8
                    elif data[0] == BaseRBlockType.OS_4:
                        # D7 D6 D5 O4 C3 C2 C1 C0 BT
                        dl = ctrl[0:4]
                        cl = [1] * 4
                        if (data[4] >> 4) & 0xf == BaseRO.SEQ_OS:
                            dl.append(XgmiiCtrl.SEQ_OS)
                        elif (data[4] >> 4) & 0xf == BaseRO.SIG_OS:
                            dl.append(XgmiiCtrl.SIG_OS)
                        else:
                            dl.append(XgmiiCtrl.ERROR)
                        cl.append(1)
                        dl += data[5:]
                        cl += [0] * 3
                    elif data[0] == BaseRBlockType.START_4:
                        # D7 D6 D5    C3 C2 C1 C0 BT
                        dl = ctrl[0:4]
                        cl = [1] * 4
                        dl.append(XgmiiCtrl.START)
                        cl.append(1)
                        dl += data[5:]
                        cl += [0] * 3
                    elif data[0] == BaseRBlockType.OS_START:
                        # D7 D6 D5    O0 D3 D2 D1 BT
                        if data[4] & 0xf == BaseRO.SEQ_OS:
                            dl.append(XgmiiCtrl.SEQ_OS)
                        elif data[4] & 0xf == BaseRO.SIG_OS:
                            dl.append(XgmiiCtrl.SIG_OS)
                        else:
                            dl.append(XgmiiCtrl.ERROR)
                        cl.append(1)
                        dl += data[1:4]
                        cl += [0] * 3
                        dl.append(XgmiiCtrl.START)
                        cl.append(1)
                        dl += data[5:]
                        cl += [0] * 3
                    elif data[0] == BaseRBlockType.OS_04:
                        # D7 D6 D5 O4 O0 D3 D2 D1 BT
                        if data[4] & 0xf == BaseRO.SEQ_OS:
                            dl.append(XgmiiCtrl.SEQ_OS)
                        elif data[4] & 0xf == BaseRO.SIG_OS:
                            dl.append(XgmiiCtrl.SIG_OS)
                        else:
                            dl.append(XgmiiCtrl.ERROR)
                        cl.append(1)
                        dl += data[1:4]
                        cl += [0] * 3
                        if (data[4] >> 4) & 0xf == BaseRO.SEQ_OS:
                            dl.append(XgmiiCtrl.SEQ_OS)
                        elif (data[4] >> 4) & 0xf == BaseRO.SIG_OS:
                            dl.append(XgmiiCtrl.SIG_OS)
                        else:
                            dl.append(XgmiiCtrl.ERROR)
                        cl.append(1)
                        dl += data[5:]
                        cl += [0] * 3
                    elif data[0] == BaseRBlockType.START_0:
                        # D7 D6 D5 D4 D3 D2 D1    BT
                        dl.append(XgmiiCtrl.START)
                        cl.append(1)
                        dl += data[1:]
                        cl += [0] * 7
                    elif data[0] == BaseRBlockType.OS_0:
                        # C7 C6 C5 C4 O0 D3 D2 D1 BT
                        if data[4] & 0xf == BaseRO.SEQ_OS:
                            dl.append(XgmiiCtrl.SEQ_OS)
                        elif data[4] & 0xf == BaseRO.SIG_OS:
                            dl.append(XgmiiCtrl.SEQ_OS)
                        else:
                            dl.append(XgmiiCtrl.ERROR)
                        cl.append(1)
                        dl += data[1:4]
                        cl += [0] * 3
                        dl += ctrl[4:]
                        cl += [1] * 4
                    elif data[0] in {
                            BaseRBlockType.TERM_0, BaseRBlockType.TERM_1,
                            BaseRBlockType.TERM_2, BaseRBlockType.TERM_3,
                            BaseRBlockType.TERM_4, BaseRBlockType.TERM_5,
                            BaseRBlockType.TERM_6, BaseRBlockType.TERM_7
                    }:
                        # C7 C6 C5 C4 C3 C2 C1    BT
                        # C7 C6 C5 C4 C3 C2    D0 BT
                        # C7 C6 C5 C4 C3    D1 D0 BT
                        # C7 C6 C5 C4    D2 D1 D0 BT
                        # C7 C6 C5    D3 D2 D1 D0 BT
                        # C7 C6    D4 D3 D2 D1 D0 BT
                        # C7    D5 D4 D3 D2 D1 D0 BT
                        #    D6 D5 D4 D3 D2 D1 D0 BT
                        term_lane = block_type_term_lane_mapping[data[0]]
                        dl += data[1:term_lane + 1]
                        cl += [0] * term_lane
                        dl.append(XgmiiCtrl.TERM)
                        cl.append(1)
                        dl += ctrl[term_lane + 1:]
                        cl += [1] * (7 - term_lane)
                    else:
                        # invalid block type
                        self.log.warning("Invalid block type")
                        dl = [XgmiiCtrl.ERROR] * 8
                        cl = [1] * 8
                else:
                    # invalid sync header
                    self.log.warning("Invalid sync header")
                    dl = [XgmiiCtrl.ERROR] * 8
                    cl = [1] * 8

                for offset in range(self.byte_lanes):
                    d_val = dl[offset]
                    c_val = cl[offset]

                    if frame is None:
                        if c_val and d_val == XgmiiCtrl.START:
                            # start
                            frame = XgmiiFrame(bytearray([EthPre.PRE]), [0])
                            frame.sim_time_start = get_sim_time()
                            frame.start_lane = offset
                    else:
                        if c_val:
                            # got a control character; terminate frame reception
                            if d_val != XgmiiCtrl.TERM:
                                # store control character if it's not a termination
                                frame.data.append(d_val)
                                frame.ctrl.append(c_val)

                            frame.compact()
                            frame.sim_time_end = get_sim_time()
                            self.log.info("RX frame: %s", frame)

                            self.queue_occupancy_bytes += len(frame)
                            self.queue_occupancy_frames += 1

                            self.queue.put_nowait(frame)
                            self.active_event.set()

                            frame = None
                        else:
                            if frame.sim_time_sfd is None and d_val == EthPre.SFD:
                                frame.sim_time_sfd = get_sim_time()

                            frame.data.append(d_val)
                            frame.ctrl.append(c_val)
Пример #11
0
class AxiMasterRead(Region, Reset):
    def __init__(self,
                 bus,
                 clock,
                 reset=None,
                 reset_active_level=True,
                 max_burst_len=256,
                 **kwargs):
        self.bus = bus
        self.clock = clock
        self.reset = reset
        self.log = logging.getLogger(
            f"cocotb.{bus.ar._entity._name}.{bus.ar._name}")

        self.log.info("AXI master (read)")
        self.log.info("cocotbext-axi version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-axi")

        self.ar_channel = AxiARSource(bus.ar, clock, reset, reset_active_level)
        self.ar_channel.queue_occupancy_limit = 2
        self.r_channel = AxiRSink(bus.r, clock, reset, reset_active_level)
        self.r_channel.queue_occupancy_limit = 2

        self.read_command_queue = Queue()
        self.read_command_queue.queue_occupancy_limit = 2
        self.current_read_command = None

        self.id_count = 2**len(self.ar_channel.bus.arid)
        self.cur_id = 0
        self.active_id = Counter()

        self.tag_context_manager = TagContextManager(
            self._process_read_resp_id)

        self.in_flight_operations = 0
        self._idle = Event()
        self._idle.set()

        self.address_width = len(self.ar_channel.bus.araddr)
        self.id_width = len(self.ar_channel.bus.arid)
        self.width = len(self.r_channel.bus.rdata)
        self.byte_size = 8
        self.byte_lanes = self.width // self.byte_size

        self.max_burst_len = max(min(max_burst_len, 256), 1)
        self.max_burst_size = (self.byte_lanes - 1).bit_length()

        self.arlock_present = hasattr(self.bus.ar, "arlock")
        self.arcache_present = hasattr(self.bus.ar, "arcache")
        self.arprot_present = hasattr(self.bus.ar, "arprot")
        self.arqos_present = hasattr(self.bus.ar, "arqos")
        self.arregion_present = hasattr(self.bus.ar, "arregion")
        self.aruser_present = hasattr(self.bus.ar, "aruser")
        self.ruser_present = hasattr(self.bus.r, "ruser")

        super().__init__(2**self.address_width, **kwargs)

        self.log.info("AXI master configuration:")
        self.log.info("  Address width: %d bits", self.address_width)
        self.log.info("  ID width: %d bits", self.id_width)
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width,
                      self.byte_lanes)
        self.log.info("  Max burst size: %d (%d bytes)", self.max_burst_size,
                      2**self.max_burst_size)
        self.log.info("  Max burst length: %d cycles (%d bytes)",
                      self.max_burst_len, self.max_burst_len * self.byte_lanes)

        self.log.info("AXI master signals:")
        for bus in (self.bus.ar, self.bus.r):
            for sig in sorted(
                    list(set().union(bus._signals, bus._optional_signals))):
                if hasattr(bus, sig):
                    self.log.info("  %s width: %d bits", sig,
                                  len(getattr(bus, sig)))
                else:
                    self.log.info("  %s: not present", sig)

        assert self.byte_lanes * self.byte_size == self.width

        assert len(self.r_channel.bus.rid) == len(self.ar_channel.bus.arid)

        self._process_read_cr = None
        self._process_read_resp_cr = None

        self._init_reset(reset, reset_active_level)

    def init_read(self,
                  address,
                  length,
                  arid=None,
                  burst=AxiBurstType.INCR,
                  size=None,
                  lock=AxiLockType.NORMAL,
                  cache=0b0011,
                  prot=AxiProt.NONSECURE,
                  qos=0,
                  region=0,
                  user=0,
                  event=None):

        if event is None:
            event = Event()

        if not isinstance(event, Event):
            raise ValueError("Expected event object")

        if address < 0 or address >= 2**self.address_width:
            raise ValueError("Address out of range")

        if length < 0:
            raise ValueError("Read length must be positive")

        if arid is None or arid < 0:
            arid = None
        elif arid > self.id_count:
            raise ValueError(
                "Requested ID exceeds maximum ID allowed for ID signal width")

        burst = AxiBurstType(burst)

        if size is None or size < 0:
            size = self.max_burst_size
        elif size > self.max_burst_size:
            raise ValueError(
                "Requested burst size exceeds maximum burst size allowed for bus width"
            )

        lock = AxiLockType(lock)
        prot = AxiProt(prot)

        if not self.arlock_present and lock != AxiLockType.NORMAL:
            raise ValueError(
                "arlock sideband signal value specified, but signal is not connected"
            )

        if not self.arcache_present and cache != 0b0011:
            raise ValueError(
                "arcache sideband signal value specified, but signal is not connected"
            )

        if not self.arprot_present and prot != AxiProt.NONSECURE:
            raise ValueError(
                "arprot sideband signal value specified, but signal is not connected"
            )

        if not self.arqos_present and qos != 0:
            raise ValueError(
                "arqos sideband signal value specified, but signal is not connected"
            )

        if not self.arregion_present and region != 0:
            raise ValueError(
                "arregion sideband signal value specified, but signal is not connected"
            )

        if not self.aruser_present and user != 0:
            raise ValueError(
                "aruser sideband signal value specified, but signal is not connected"
            )

        cocotb.start_soon(
            self._read_wrapper(address, length, arid, burst, size, lock, cache,
                               prot, qos, region, user, event))

        return event

    def idle(self):
        return not self.in_flight_operations

    async def wait(self):
        while not self.idle():
            await self._idle.wait()

    async def read(self,
                   address,
                   length,
                   arid=None,
                   burst=AxiBurstType.INCR,
                   size=None,
                   lock=AxiLockType.NORMAL,
                   cache=0b0011,
                   prot=AxiProt.NONSECURE,
                   qos=0,
                   region=0,
                   user=0):

        if address < 0 or address >= 2**self.address_width:
            raise ValueError("Address out of range")

        if length < 0:
            raise ValueError("Read length must be positive")

        if arid is None or arid < 0:
            arid = None
        elif arid > self.id_count:
            raise ValueError(
                "Requested ID exceeds maximum ID allowed for ID signal width")

        burst = AxiBurstType(burst)

        if size is None or size < 0:
            size = self.max_burst_size
        elif size > self.max_burst_size:
            raise ValueError(
                "Requested burst size exceeds maximum burst size allowed for bus width"
            )

        lock = AxiLockType(lock)
        prot = AxiProt(prot)

        if not self.arlock_present and lock != AxiLockType.NORMAL:
            raise ValueError(
                "arlock sideband signal value specified, but signal is not connected"
            )

        if not self.arcache_present and cache != 0b0011:
            raise ValueError(
                "arcache sideband signal value specified, but signal is not connected"
            )

        if not self.arprot_present and prot != AxiProt.NONSECURE:
            raise ValueError(
                "arprot sideband signal value specified, but signal is not connected"
            )

        if not self.arqos_present and qos != 0:
            raise ValueError(
                "arqos sideband signal value specified, but signal is not connected"
            )

        if not self.arregion_present and region != 0:
            raise ValueError(
                "arregion sideband signal value specified, but signal is not connected"
            )

        if not self.aruser_present and user != 0:
            raise ValueError(
                "aruser sideband signal value specified, but signal is not connected"
            )

        event = Event()

        self.in_flight_operations += 1
        self._idle.clear()

        cmd = AxiReadCmd(address, length, arid, burst, size, lock, cache, prot,
                         qos, region, user, event)
        await self.read_command_queue.put(cmd)

        await event.wait()
        return event.data

    async def _read_wrapper(self, address, length, arid, burst, size, lock,
                            cache, prot, qos, region, user, event):
        event.set(await self.read(address, length, arid, burst, size, lock,
                                  cache, prot, qos, region, user))

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._process_read_cr is not None:
                self._process_read_cr.kill()
                self._process_read_cr = None
            if self._process_read_resp_cr is not None:
                self._process_read_resp_cr.kill()
                self._process_read_resp_cr = None

            self.ar_channel.clear()
            self.r_channel.clear()

            def flush_cmd(cmd):
                self.log.warning("Flushed read operation during reset: %s",
                                 cmd)
                if cmd.event:
                    cmd.event.set(None)

            while not self.read_command_queue.empty():
                cmd = self.read_command_queue.get_nowait()
                flush_cmd(cmd)

            if self.current_read_command:
                cmd = self.current_read_command
                self.current_read_command = None
                flush_cmd(cmd)

            for cmd in self.tag_context_manager.flush():
                flush_cmd(cmd)

            self.cur_id = 0
            self.active_id = Counter()

            self.in_flight_operations = 0
            self._idle.set()
        else:
            self.log.info("Reset de-asserted")
            if self._process_read_cr is None:
                self._process_read_cr = cocotb.start_soon(self._process_read())
            if self._process_read_resp_cr is None:
                self._process_read_resp_cr = cocotb.start_soon(
                    self._process_read_resp())

    async def _process_read(self):
        while True:
            cmd = await self.read_command_queue.get()
            self.current_read_command = cmd

            num_bytes = 2**cmd.size

            aligned_addr = (cmd.address // num_bytes) * num_bytes

            cycles = (cmd.length + num_bytes - 1 +
                      (cmd.address % num_bytes)) // num_bytes

            burst_list = []

            cur_addr = aligned_addr
            n = 0

            burst_length = 0

            if cmd.arid is not None:
                arid = cmd.arid
            else:
                arid = self.cur_id
                self.cur_id = (self.cur_id + 1) % self.id_count

            self.log.info("Read start addr: 0x%08x arid: 0x%x prot: %s",
                          cmd.address, arid, cmd.prot)

            for k in range(cycles):

                n += 1
                if n >= burst_length:
                    n = 0

                    # split on burst length
                    burst_length = min(cycles - k,
                                       min(max(self.max_burst_len, 1), 256))
                    # split on 4k address boundary
                    burst_length = (min(burst_length * num_bytes, 0x1000 -
                                        (cur_addr & 0xfff)) + num_bytes -
                                    1) // num_bytes

                    burst_list.append(burst_length)

                    ar = self.ar_channel._transaction_obj()
                    ar.arid = arid
                    ar.araddr = cur_addr
                    ar.arlen = burst_length - 1
                    ar.arsize = cmd.size
                    ar.arburst = cmd.burst
                    ar.arlock = cmd.lock
                    ar.arcache = cmd.cache
                    ar.arprot = cmd.prot
                    ar.arqos = cmd.qos
                    ar.arregion = cmd.region
                    ar.aruser = cmd.user

                    self.active_id[arid] += 1
                    await self.ar_channel.send(ar)

                    self.log.info(
                        "Read burst start arid: 0x%x araddr: 0x%08x arlen: %d arsize: %d arprot: %s",
                        arid, cur_addr, burst_length - 1, cmd.size, cmd.prot)

                cur_addr += num_bytes

            resp_cmd = AxiReadRespCmd(cmd.address, cmd.length, cmd.size,
                                      cycles, cmd.prot, burst_list, cmd.event)
            self.tag_context_manager.start_cmd(arid, resp_cmd)

            self.current_read_command = None

    async def _process_read_resp(self):
        while True:
            r = await self.r_channel.recv()

            rid = int(getattr(r, 'rid', 0))

            assert self.active_id[rid] > 0, "unexpected burst ID"

            self.tag_context_manager.put_resp(rid, r)

    async def _process_read_resp_id(self, context, cmd):
        rid = context.current_tag

        num_bytes = 2**cmd.size

        aligned_addr = (cmd.address // num_bytes) * num_bytes
        word_addr = (cmd.address // self.byte_lanes) * self.byte_lanes

        start_offset = cmd.address % self.byte_lanes

        cycle_offset = aligned_addr - word_addr
        data = bytearray()

        resp = AxiResp.OKAY
        user = []

        first = True

        for burst_length in cmd.burst_list:
            for k in range(burst_length):
                r = await context.get_resp()

                assert self.active_id[rid] > 0, "unexpected burst ID"

                if k == burst_length - 1:
                    assert int(r.rlast), "missing rlast at end of burst"
                else:
                    assert not int(r.rlast), "unexpected rlast within burst"

                cycle_data = int(r.rdata)
                cycle_resp = AxiResp(int(getattr(r, "rresp", AxiResp.OKAY)))
                cycle_user = int(getattr(r, "ruser", 0))

                if cycle_resp != AxiResp.OKAY:
                    resp = cycle_resp

                if cycle_user is not None:
                    user.append(cycle_user)

                start = cycle_offset
                stop = cycle_offset + num_bytes

                if first:
                    start = start_offset

                for j in range(start, stop):
                    data.append((cycle_data >> j * 8) & 0xff)

                cycle_offset = (cycle_offset + num_bytes) % self.byte_lanes

                first = False

            self.active_id[rid] -= 1

            self.log.info("Read burst complete rid: 0x%x rresp: %s", rid, resp)

        data = data[:cmd.length]

        if not self.ruser_present:
            user = None

        if self.log.isEnabledFor(logging.INFO):
            self.log.info(
                "Read complete addr: 0x%08x prot: %s resp: %s data: %s",
                cmd.address, cmd.prot, resp, ' '.join(
                    (f'{c:02x}' for c in data)))

        read_resp = AxiReadResp(cmd.address, bytes(data), resp, user)

        cmd.event.set(read_resp)

        self.in_flight_operations -= 1

        if self.in_flight_operations == 0:
            self._idle.set()
Пример #12
0
class AxiMasterWrite(Region, Reset):
    def __init__(self,
                 bus,
                 clock,
                 reset=None,
                 reset_active_level=True,
                 max_burst_len=256,
                 **kwargs):
        self.bus = bus
        self.clock = clock
        self.reset = reset
        self.log = logging.getLogger(
            f"cocotb.{bus.aw._entity._name}.{bus.aw._name}")

        self.log.info("AXI master (write)")
        self.log.info("cocotbext-axi version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-axi")

        self.aw_channel = AxiAWSource(bus.aw, clock, reset, reset_active_level)
        self.aw_channel.queue_occupancy_limit = 2
        self.w_channel = AxiWSource(bus.w, clock, reset, reset_active_level)
        self.w_channel.queue_occupancy_limit = 2
        self.b_channel = AxiBSink(bus.b, clock, reset, reset_active_level)
        self.b_channel.queue_occupancy_limit = 2

        self.write_command_queue = Queue()
        self.write_command_queue.queue_occupancy_limit = 2
        self.current_write_command = None

        self.id_count = 2**len(self.aw_channel.bus.awid)
        self.cur_id = 0
        self.active_id = Counter()

        self.tag_context_manager = TagContextManager(
            self._process_write_resp_id)

        self.in_flight_operations = 0
        self._idle = Event()
        self._idle.set()

        self.address_width = len(self.aw_channel.bus.awaddr)
        self.id_width = len(self.aw_channel.bus.awid)
        self.width = len(self.w_channel.bus.wdata)
        self.byte_size = 8
        self.byte_lanes = self.width // self.byte_size
        self.strb_mask = 2**self.byte_lanes - 1

        self.max_burst_len = max(min(max_burst_len, 256), 1)
        self.max_burst_size = (self.byte_lanes - 1).bit_length()

        self.awlock_present = hasattr(self.bus.aw, "awlock")
        self.awcache_present = hasattr(self.bus.aw, "awcache")
        self.awprot_present = hasattr(self.bus.aw, "awprot")
        self.awqos_present = hasattr(self.bus.aw, "awqos")
        self.awregion_present = hasattr(self.bus.aw, "awregion")
        self.awuser_present = hasattr(self.bus.aw, "awuser")
        self.wstrb_present = hasattr(self.bus.w, "wstrb")
        self.wuser_present = hasattr(self.bus.w, "wuser")
        self.buser_present = hasattr(self.bus.b, "buser")

        super().__init__(2**self.address_width, **kwargs)

        self.log.info("AXI master configuration:")
        self.log.info("  Address width: %d bits", self.address_width)
        self.log.info("  ID width: %d bits", self.id_width)
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width,
                      self.byte_lanes)
        self.log.info("  Max burst size: %d (%d bytes)", self.max_burst_size,
                      2**self.max_burst_size)
        self.log.info("  Max burst length: %d cycles (%d bytes)",
                      self.max_burst_len, self.max_burst_len * self.byte_lanes)

        self.log.info("AXI master signals:")
        for bus in (self.bus.aw, self.bus.w, self.bus.b):
            for sig in sorted(
                    list(set().union(bus._signals, bus._optional_signals))):
                if hasattr(bus, sig):
                    self.log.info("  %s width: %d bits", sig,
                                  len(getattr(bus, sig)))
                else:
                    self.log.info("  %s: not present", sig)

        if self.wstrb_present:
            assert self.byte_lanes == len(self.w_channel.bus.wstrb)
        assert self.byte_lanes * self.byte_size == self.width

        assert len(self.b_channel.bus.bid) == len(self.aw_channel.bus.awid)

        self._process_write_cr = None
        self._process_write_resp_cr = None

        self._init_reset(reset, reset_active_level)

    def init_write(self,
                   address,
                   data,
                   awid=None,
                   burst=AxiBurstType.INCR,
                   size=None,
                   lock=AxiLockType.NORMAL,
                   cache=0b0011,
                   prot=AxiProt.NONSECURE,
                   qos=0,
                   region=0,
                   user=0,
                   wuser=0,
                   event=None):

        if event is None:
            event = Event()

        if not isinstance(event, Event):
            raise ValueError("Expected event object")

        if address < 0 or address >= 2**self.address_width:
            raise ValueError("Address out of range")

        if isinstance(data, int):
            raise ValueError("Expected bytes or bytearray for data")

        if awid is None or awid < 0:
            awid = None
        elif awid > self.id_count:
            raise ValueError(
                "Requested ID exceeds maximum ID allowed for ID signal width")

        burst = AxiBurstType(burst)

        if size is None or size < 0:
            size = self.max_burst_size
        elif size > self.max_burst_size:
            raise ValueError(
                "Requested burst size exceeds maximum burst size allowed for bus width"
            )

        lock = AxiLockType(lock)
        prot = AxiProt(prot)

        if not self.awlock_present and lock != AxiLockType.NORMAL:
            raise ValueError(
                "awlock sideband signal value specified, but signal is not connected"
            )

        if not self.awcache_present and cache != 0b0011:
            raise ValueError(
                "awcache sideband signal value specified, but signal is not connected"
            )

        if not self.awprot_present and prot != AxiProt.NONSECURE:
            raise ValueError(
                "awprot sideband signal value specified, but signal is not connected"
            )

        if not self.awqos_present and qos != 0:
            raise ValueError(
                "awqos sideband signal value specified, but signal is not connected"
            )

        if not self.awregion_present and region != 0:
            raise ValueError(
                "awregion sideband signal value specified, but signal is not connected"
            )

        if not self.awuser_present and user != 0:
            raise ValueError(
                "awuser sideband signal value specified, but signal is not connected"
            )

        if not self.wuser_present and wuser != 0:
            raise ValueError(
                "wuser sideband signal value specified, but signal is not connected"
            )

        if wuser is None:
            wuser = 0
        elif isinstance(wuser, int):
            pass
        else:
            wuser = list(wuser)

        data = bytes(data)

        cocotb.start_soon(
            self._write_wrapper(address, data, awid, burst, size, lock, cache,
                                prot, qos, region, user, wuser, event))

        return event

    def idle(self):
        return not self.in_flight_operations

    async def wait(self):
        while not self.idle():
            await self._idle.wait()

    async def write(self,
                    address,
                    data,
                    awid=None,
                    burst=AxiBurstType.INCR,
                    size=None,
                    lock=AxiLockType.NORMAL,
                    cache=0b0011,
                    prot=AxiProt.NONSECURE,
                    qos=0,
                    region=0,
                    user=0,
                    wuser=0):

        if address < 0 or address >= 2**self.address_width:
            raise ValueError("Address out of range")

        if isinstance(data, int):
            raise ValueError("Expected bytes or bytearray for data")

        if awid is None or awid < 0:
            awid = None
        elif awid > self.id_count:
            raise ValueError(
                "Requested ID exceeds maximum ID allowed for ID signal width")

        burst = AxiBurstType(burst)

        if size is None or size < 0:
            size = self.max_burst_size
        elif size > self.max_burst_size:
            raise ValueError(
                "Requested burst size exceeds maximum burst size allowed for bus width"
            )

        lock = AxiLockType(lock)
        prot = AxiProt(prot)

        if not self.awlock_present and lock != AxiLockType.NORMAL:
            raise ValueError(
                "awlock sideband signal value specified, but signal is not connected"
            )

        if not self.awcache_present and cache != 0b0011:
            raise ValueError(
                "awcache sideband signal value specified, but signal is not connected"
            )

        if not self.awprot_present and prot != AxiProt.NONSECURE:
            raise ValueError(
                "awprot sideband signal value specified, but signal is not connected"
            )

        if not self.awqos_present and qos != 0:
            raise ValueError(
                "awqos sideband signal value specified, but signal is not connected"
            )

        if not self.awregion_present and region != 0:
            raise ValueError(
                "awregion sideband signal value specified, but signal is not connected"
            )

        if not self.awuser_present and user != 0:
            raise ValueError(
                "awuser sideband signal value specified, but signal is not connected"
            )

        if not self.wuser_present and wuser != 0:
            raise ValueError(
                "wuser sideband signal value specified, but signal is not connected"
            )

        if wuser is None:
            wuser = 0
        elif isinstance(wuser, int):
            pass
        else:
            wuser = list(wuser)

        event = Event()
        data = bytes(data)

        self.in_flight_operations += 1
        self._idle.clear()

        cmd = AxiWriteCmd(address, data, awid, burst, size, lock, cache, prot,
                          qos, region, user, wuser, event)
        await self.write_command_queue.put(cmd)

        await event.wait()
        return event.data

    async def _write_wrapper(self, address, data, awid, burst, size, lock,
                             cache, prot, qos, region, user, wuser, event):
        event.set(await self.write(address, data, awid, burst, size, lock,
                                   cache, prot, qos, region, user, wuser))

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._process_write_cr is not None:
                self._process_write_cr.kill()
                self._process_write_cr = None
            if self._process_write_resp_cr is not None:
                self._process_write_resp_cr.kill()
                self._process_write_resp_cr = None

            self.aw_channel.clear()
            self.w_channel.clear()
            self.b_channel.clear()

            def flush_cmd(cmd):
                self.log.warning("Flushed write operation during reset: %s",
                                 cmd)
                if cmd.event:
                    cmd.event.set(None)

            while not self.write_command_queue.empty():
                cmd = self.write_command_queue.get_nowait()
                flush_cmd(cmd)

            if self.current_write_command:
                cmd = self.current_write_command
                self.current_write_command = None
                flush_cmd(cmd)

            for cmd in self.tag_context_manager.flush():
                flush_cmd(cmd)

            self.cur_id = 0
            self.active_id = Counter()

            self.in_flight_operations = 0
            self._idle.set()
        else:
            self.log.info("Reset de-asserted")
            if self._process_write_cr is None:
                self._process_write_cr = cocotb.start_soon(
                    self._process_write())
            if self._process_write_resp_cr is None:
                self._process_write_resp_cr = cocotb.start_soon(
                    self._process_write_resp())

    async def _process_write(self):
        while True:
            cmd = await self.write_command_queue.get()
            self.current_write_command = cmd

            num_bytes = 2**cmd.size

            aligned_addr = (cmd.address // num_bytes) * num_bytes
            word_addr = (cmd.address // self.byte_lanes) * self.byte_lanes

            start_offset = cmd.address % self.byte_lanes
            end_offset = (
                (cmd.address + len(cmd.data) - 1) % self.byte_lanes) + 1

            cycles = (len(cmd.data) +
                      (cmd.address % num_bytes) + num_bytes - 1) // num_bytes

            cur_addr = aligned_addr
            offset = 0
            cycle_offset = aligned_addr - word_addr
            n = 0
            transfer_count = 0

            burst_list = []
            burst_length = 0

            if cmd.awid is not None:
                awid = cmd.awid
            else:
                awid = self.cur_id
                self.cur_id = (self.cur_id + 1) % self.id_count

            wuser = cmd.wuser

            if self.log.isEnabledFor(logging.INFO):
                self.log.info(
                    "Write start addr: 0x%08x awid: 0x%x prot: %s data: %s",
                    cmd.address, awid, cmd.prot, ' '.join(
                        (f'{c:02x}' for c in cmd.data)))

            for k in range(cycles):
                start = cycle_offset
                stop = cycle_offset + num_bytes

                if k == 0:
                    start = start_offset
                if k == cycles - 1:
                    stop = end_offset

                strb = (self.strb_mask << start) & self.strb_mask & (
                    self.strb_mask >> (self.byte_lanes - stop))

                val = 0
                for j in range(start, stop):
                    val |= cmd.data[offset] << j * 8
                    offset += 1

                if n >= burst_length:
                    transfer_count += 1
                    n = 0

                    # split on burst length
                    burst_length = min(cycles - k,
                                       min(max(self.max_burst_len, 1), 256))
                    # split on 4k address boundary
                    burst_length = (min(burst_length * num_bytes, 0x1000 -
                                        (cur_addr & 0xfff)) + num_bytes -
                                    1) // num_bytes

                    burst_list.append(burst_length)

                    aw = self.aw_channel._transaction_obj()
                    aw.awid = awid
                    aw.awaddr = cur_addr
                    aw.awlen = burst_length - 1
                    aw.awsize = cmd.size
                    aw.awburst = cmd.burst
                    aw.awlock = cmd.lock
                    aw.awcache = cmd.cache
                    aw.awprot = cmd.prot
                    aw.awqos = cmd.qos
                    aw.awregion = cmd.region
                    aw.awuser = cmd.user

                    self.active_id[awid] += 1
                    await self.aw_channel.send(aw)

                    self.log.info(
                        "Write burst start awid: 0x%x awaddr: 0x%08x awlen: %d awsize: %d awprot: %s",
                        awid, cur_addr, burst_length - 1, cmd.size, cmd.prot)

                n += 1

                if not self.wstrb_present and strb != self.strb_mask:
                    self.log.warning(
                        "Partial operation requested with wstrb not connected, write will be zero-padded (0x%x != 0x%x)",
                        strb, self.strb_mask)

                w = self.w_channel._transaction_obj()
                w.wdata = val
                w.wstrb = strb
                w.wlast = n >= burst_length

                if isinstance(wuser, int):
                    w.wuser = wuser
                else:
                    if wuser and k < len(wuser):
                        w.wuser = wuser[k]
                    else:
                        w.wuser = 0

                await self.w_channel.send(w)

                cur_addr += num_bytes
                cycle_offset = (cycle_offset + num_bytes) % self.byte_lanes

            resp_cmd = AxiWriteRespCmd(cmd.address, len(cmd.data), cmd.size,
                                       cycles, cmd.prot, burst_list, cmd.event)
            self.tag_context_manager.start_cmd(awid, resp_cmd)

            self.current_write_command = None

    async def _process_write_resp(self):
        while True:
            b = await self.b_channel.recv()

            bid = int(getattr(b, 'bid', 0))

            assert self.active_id[bid] > 0, "unexpected burst ID"

            self.tag_context_manager.put_resp(bid, b)

    async def _process_write_resp_id(self, context, cmd):
        bid = context.current_tag

        resp = AxiResp.OKAY
        user = []

        for burst_length in cmd.burst_list:
            b = await context.get_resp()

            burst_resp = AxiResp(int(getattr(b, 'bresp', AxiResp.OKAY)))
            burst_user = int(getattr(b, 'buser', 0))

            if burst_resp != AxiResp.OKAY:
                resp = burst_resp

            if burst_user is not None:
                user.append(burst_user)

            assert self.active_id[bid] > 0, "unexpected burst ID"

            self.active_id[bid] -= 1

            self.log.info("Write burst complete bid: 0x%x bresp: %s", bid,
                          burst_resp)

        if not self.buser_present:
            user = None

        self.log.info(
            "Write complete addr: 0x%08x prot: %s resp: %s length: %d",
            cmd.address, cmd.prot, resp, cmd.length)

        write_resp = AxiWriteResp(cmd.address, cmd.length, resp, user)

        cmd.event.set(write_resp)

        self.in_flight_operations -= 1

        if self.in_flight_operations == 0:
            self._idle.set()
Пример #13
0
class EthMacRx(Reset):
    def __init__(self,
                 bus,
                 clock,
                 reset=None,
                 ptp_time=None,
                 reset_active_level=True,
                 ifg=12,
                 speed=1000e6,
                 *args,
                 **kwargs):

        self.bus = bus
        self.clock = clock
        self.reset = reset
        self.ptp_time = ptp_time
        self.ifg = ifg
        self.speed = speed
        self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")

        self.log.info("Ethernet MAC RX model")
        self.log.info("cocotbext-eth version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-eth")

        super().__init__(*args, **kwargs)

        self.stream = AxiStreamSource(bus,
                                      clock,
                                      reset,
                                      reset_active_level=reset_active_level)
        self.stream.queue_occupancy_limit = 4

        self.active = False
        self.queue = Queue()
        self.dequeue_event = Event()
        self.current_frame = None
        self.idle_event = Event()
        self.idle_event.set()

        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

        self.queue_occupancy_limit_bytes = -1
        self.queue_occupancy_limit_frames = -1

        self.time_scale = cocotb.utils.get_sim_steps(1, 'sec')

        self.width = len(self.bus.tdata)
        self.byte_lanes = 1

        if hasattr(self.bus, "tkeep"):
            self.byte_lanes = len(self.bus.tkeep)

        self.byte_size = self.width // self.byte_lanes
        self.byte_mask = 2**self.byte_size - 1

        self.log.info("Ethernet MAC RX model configuration")
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width,
                      self.byte_lanes)
        if hasattr(self.bus, "tkeep"):
            self.log.info("  tkeep width: %d bits", len(self.bus.tkeep))
        else:
            self.log.info("  tkeep: not present")
        if hasattr(self.bus, "tuser"):
            self.log.info("  tuser width: %d bits", len(self.bus.tuser))
        else:
            self.log.info("  tuser: not present")
        if self.ptp_time:
            self.log.info("  ptp_time width: %d bits", len(self.ptp_time))
        else:
            self.log.info("  ptp_time: not present")

        if self.byte_size != 8:
            raise ValueError("Byte size must be 8")

        if self.byte_lanes * self.byte_size != self.width:
            raise ValueError(
                f"Bus does not evenly divide into byte lanes "
                f"({self.byte_lanes} * {self.byte_size} != {self.width})")

        self._run_cr = None

        self._init_reset(reset, reset_active_level)

    async def send(self, frame):
        while self.full():
            self.dequeue_event.clear()
            await self.dequeue_event.wait()
        frame = EthMacFrame(frame)
        await self.queue.put(frame)
        self.idle_event.clear()
        self.queue_occupancy_bytes += len(frame)
        self.queue_occupancy_frames += 1

    def send_nowait(self, frame):
        if self.full():
            raise QueueFull()
        frame = EthMacFrame(frame)
        self.queue.put_nowait(frame)
        self.idle_event.clear()
        self.queue_occupancy_bytes += len(frame)
        self.queue_occupancy_frames += 1

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def full(self):
        if self.queue_occupancy_limit_bytes > 0 and self.queue_occupancy_bytes > self.queue_occupancy_limit_bytes:
            return True
        elif self.queue_occupancy_limit_frames > 0 and self.queue_occupancy_frames > self.queue_occupancy_limit_frames:
            return True
        else:
            return False

    def idle(self):
        return self.empty() and not self.active

    def clear(self):
        while not self.queue.empty():
            frame = self.queue.get_nowait()
            frame.sim_time_end = None
            frame.handle_tx_complete()
        self.dequeue_event.set()
        self.idle_event.set()
        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

    async def wait(self):
        await self.idle_event.wait()

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._run_cr is not None:
                self._run_cr.kill()
                self._run_cr = None

            self.active = False

            if self.current_frame:
                self.log.warning("Flushed transmit frame during reset: %s",
                                 self.current_frame)
                self.current_frame.handle_tx_complete()
                self.current_frame = None

            if self.queue.empty():
                self.idle_event.set()
        else:
            self.log.info("Reset de-asserted")
            if self._run_cr is None:
                self._run_cr = cocotb.start_soon(self._run())

    async def _run(self):
        frame = None
        frame_offset = 0
        tuser = 0
        self.active = False

        while True:
            # wait for data
            frame = await self.queue.get()
            tuser = 0
            self.dequeue_event.set()
            self.queue_occupancy_bytes -= len(frame)
            self.queue_occupancy_frames -= 1
            self.current_frame = frame
            frame.sim_time_start = get_sim_time()
            frame.sim_time_sfd = None
            frame.sim_time_end = None
            self.log.info("TX frame: %s", frame)
            frame_offset = 0

            # wait for preamble time
            await Timer(self.time_scale * 8 * 8 // self.speed, 'step')

            frame.sim_time_sfd = get_sim_time()

            if self.ptp_time:
                frame.ptp_timestamp = self.ptp_time.value.integer
                tuser |= frame.ptp_timestamp << 1

            # process frame data
            while frame is not None:
                byte_count = 0

                cycle = AxiStreamTransaction()

                cycle.tdata = 0
                cycle.tkeep = 0
                cycle.tlast = 0
                cycle.tuser = tuser

                for offset in range(self.byte_lanes):
                    cycle.tdata |= (frame.data[frame_offset] & self.byte_mask
                                    ) << (offset * self.byte_size)
                    cycle.tkeep |= 1 << offset
                    byte_count += 1
                    frame_offset += 1

                    if frame_offset >= len(frame.data):
                        cycle.tlast = 1
                        frame.sim_time_end = get_sim_time()
                        frame.handle_tx_complete()
                        frame = None
                        self.current_frame = None
                        break

                await self.stream.send(cycle)

                # wait for serialization time
                await Timer(self.time_scale * byte_count * 8 // self.speed,
                            'step')

            # wait for IFG
            await Timer(self.time_scale * self.ifg * 8 // self.speed, 'step')
Пример #14
0
class EthMacTx(Reset):
    def __init__(self,
                 bus,
                 clock,
                 reset=None,
                 ptp_time=None,
                 ptp_ts=None,
                 ptp_ts_tag=None,
                 ptp_ts_valid=None,
                 reset_active_level=True,
                 ifg=12,
                 speed=1000e6,
                 *args,
                 **kwargs):

        self.bus = bus
        self.clock = clock
        self.reset = reset
        self.ptp_time = ptp_time
        self.ptp_ts = ptp_ts
        self.ptp_ts_tag = ptp_ts_tag
        self.ptp_ts_valid = ptp_ts_valid
        self.ifg = ifg
        self.speed = speed
        self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")

        self.log.info("Ethernet MAC TX model")
        self.log.info("cocotbext-eth version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-eth")

        super().__init__(*args, **kwargs)

        self.stream = AxiStreamSink(bus,
                                    clock,
                                    reset,
                                    reset_active_level=reset_active_level)
        self.stream.queue_occupancy_limit = 4

        self.active = False
        self.queue = Queue()
        self.active_event = Event()

        self.ts_queue = Queue()

        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

        self.time_scale = cocotb.utils.get_sim_steps(1, 'sec')

        self.width = len(self.bus.tdata)
        self.byte_lanes = 1

        if hasattr(self.bus, "tkeep"):
            self.byte_lanes = len(self.bus.tkeep)

        self.byte_size = self.width // self.byte_lanes
        self.byte_mask = 2**self.byte_size - 1

        self.log.info("Ethernet MAC TX model configuration")
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width,
                      self.byte_lanes)
        if hasattr(self.bus, "tkeep"):
            self.log.info("  tkeep width: %d bits", len(self.bus.tkeep))
        else:
            self.log.info("  tkeep: not present")
        if hasattr(self.bus, "tuser"):
            self.log.info("  tuser width: %d bits", len(self.bus.tuser))
        else:
            self.log.info("  tuser: not present")
        if self.ptp_time:
            self.log.info("  ptp_time width: %d bits", len(self.ptp_time))
        else:
            self.log.info("  ptp_time: not present")

        if self.bus.tready is None:
            raise ValueError("tready is required")

        if self.byte_size != 8:
            raise ValueError("Byte size must be 8")

        if self.byte_lanes * self.byte_size != self.width:
            raise ValueError(
                f"Bus does not evenly divide into byte lanes "
                f"({self.byte_lanes} * {self.byte_size} != {self.width})")

        if self.ptp_ts:
            self.ptp_ts.setimmediatevalue(0)
        if self.ptp_ts_tag:
            self.ptp_ts_tag.setimmediatevalue(0)
        if self.ptp_ts_valid:
            self.ptp_ts_valid.setimmediatevalue(0)

        self._run_cr = None
        self._run_ts_cr = None

        self._init_reset(reset, reset_active_level)

    def _recv(self, frame):
        if self.queue.empty():
            self.active_event.clear()
        self.queue_occupancy_bytes -= len(frame)
        self.queue_occupancy_frames -= 1
        return frame

    async def recv(self):
        frame = await self.queue.get()
        return self._recv(frame)

    def recv_nowait(self):
        frame = self.queue.get_nowait()
        return self._recv(frame)

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def idle(self):
        return not self.active

    def clear(self):
        while not self.queue.empty():
            self.queue.get_nowait()
        self.active_event.clear()
        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

    async def wait(self, timeout=0, timeout_unit=None):
        if not self.empty():
            return
        if timeout:
            await First(self.active_event.wait(), Timer(timeout, timeout_unit))
        else:
            await self.active_event.wait()

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._run_cr is not None:
                self._run_cr.kill()
                self._run_cr = None
            if self._run_ts_cr is not None:
                self._run_ts_cr.kill()
                self._run_ts_cr = None

            if self.ptp_ts_valid:
                self.ptp_ts_valid.value = 0

            self.active = False

            while not self.ts_queue.empty():
                self.ts_queue.get_nowait()
        else:
            self.log.info("Reset de-asserted")
            if self._run_cr is None:
                self._run_cr = cocotb.start_soon(self._run())
            if self._run_ts_cr is None and self.ptp_ts:
                self._run_ts_cr = cocotb.start_soon(self._run_ts())

    async def _run(self):
        frame = None
        self.active = False

        while True:
            # wait for data
            cycle = await self.stream.recv()

            frame = EthMacFrame(bytearray())
            frame.sim_time_start = get_sim_time()

            # wait for preamble time
            await Timer(self.time_scale * 8 * 8 // self.speed, 'step')

            frame.sim_time_sfd = get_sim_time()

            if self.ptp_time:
                frame.ptp_timestamp = self.ptp_time.value.integer
                frame.ptp_tag = cycle.tuser.integer >> 1
                self.ts_queue.put_nowait((frame.ptp_timestamp, frame.ptp_tag))

            # process frame data
            while True:
                byte_count = 0

                for offset in range(self.byte_lanes):
                    if not hasattr(self.bus, "tkeep") or (
                            cycle.tkeep.integer >> offset) & 1:
                        frame.data.append((cycle.tdata.integer >>
                                           (offset * self.byte_size))
                                          & self.byte_mask)
                        byte_count += 1

                # wait for serialization time
                await Timer(self.time_scale * byte_count * 8 // self.speed,
                            'step')

                if cycle.tlast.integer:
                    frame.sim_time_end = get_sim_time()
                    self.log.info("RX frame: %s", frame)

                    self.queue_occupancy_bytes += len(frame)
                    self.queue_occupancy_frames += 1

                    await self.queue.put(frame)
                    self.active_event.set()

                    frame = None

                    break

                # get next cycle
                # TODO improve underflow handling
                assert not self.stream.empty(), "underflow"
                cycle = await self.stream.recv()

            # wait for IFG
            await Timer(self.time_scale * self.ifg * 8 // self.speed, 'step')

    async def _run_ts(self):
        clock_edge_event = RisingEdge(self.clock)

        while True:
            await clock_edge_event
            self.ptp_ts_valid.value = 0

            if not self.ts_queue.empty():
                ts, tag = self.ts_queue.get_nowait()
                self.ptp_ts.value = ts
                if self.ptp_ts_tag is not None:
                    self.ptp_ts_tag.value = tag
                self.ptp_ts_valid.value = 1
Пример #15
0
class Port:
    """Base port"""
    def __init__(self, fc_init=[[0]*6]*8, *args, **kwargs):
        self.log = logging.getLogger(f"cocotb.pcie.{type(self).__name__}.{id(self)}")
        self.log.name = f"cocotb.pcie.{type(self).__name__}"

        self.parent = None
        self.rx_handler = None

        self.max_link_speed = None
        self.max_link_width = None

        self.tx_queue = Queue(1)
        self.tx_queue_sync = Event()

        self.rx_queue = Queue()

        self.cur_link_speed = None
        self.cur_link_width = None

        self.time_scale = get_sim_steps(1, 'sec')

        # ACK/NAK protocol
        # TX
        self.next_transmit_seq = 0x000
        self.ackd_seq = 0xfff
        self.retry_buffer = Queue()

        # RX
        self.next_recv_seq = 0x000
        self.nak_scheduled = False
        self.ack_nak_latency_timer = 0

        self.max_payload_size = 128
        self.max_latency_timer = 0

        self.send_ack = Event()

        self._ack_latency_timer_cr = None

        # Flow control
        self.send_fc = Event()

        self.fc_state = [FcChannelState(fc_init[k], self.start_fc_update_timer) for k in range(8)]

        self.fc_initialized = False
        self.fc_init_vc = 0
        self.fc_init_type = FcType.P

        self.fc_idle_timer_steps = get_sim_steps(10, 'us')
        self.fc_update_steps = get_sim_steps(30, 'us')

        self._fc_update_timer_cr = None

        super().__init__(*args, **kwargs)

        # VC0 is always active
        self.fc_state[0].active = True

        cocotb.fork(self._run_transmit())
        cocotb.fork(self._run_receive())
        cocotb.fork(self._run_fc_update_idle_timer())

    def classify_tlp_vc(self, tlp):
        return 0

    async def send(self, pkt):
        pkt.release_fc()
        await self.fc_state[self.classify_tlp_vc(pkt)].tx_tlp_fc_gate(pkt)
        await self.tx_queue.put(pkt)
        self.tx_queue_sync.set()

    async def _run_transmit(self):
        await NullTrigger()
        while True:
            while self.tx_queue.empty() and not self.send_ack.is_set() and not self.send_fc.is_set() and self.fc_initialized:
                self.tx_queue_sync.clear()
                await First(self.tx_queue_sync.wait(), self.send_ack.wait(), self.send_fc.wait())

            pkt = None

            if self.send_ack.is_set():
                # Send ACK or NAK DLLP
                # Runs when
                #  - ACK timer expires
                #  - ACK/NAK transmit requested
                self.send_ack.clear()
                if self.nak_scheduled:
                    pkt = Dllp.create_nak((self.next_recv_seq-1) & 0xfff)
                else:
                    pkt = Dllp.create_ack((self.next_recv_seq-1) & 0xfff)
            elif self.send_fc.is_set() or (not self.fc_initialized and self.tx_queue.empty()):
                # Send FC DLLP
                # Runs when
                #  - FC timer expires
                #  - FC update DLLP transmit requested
                #  - FC init is not done AND no TLPs are queued for transmit
                if self.send_fc.is_set():
                    # Send FC update DLLP
                    for fc_ch in self.fc_state:
                        if not fc_ch.active or not fc_ch.fi2:
                            continue

                        sim_time = get_sim_time()
                        if fc_ch.next_fc_p_tx <= sim_time:
                            pkt = Dllp()
                            pkt.vc = self.fc_init_vc
                            pkt.type = DllpType.UPDATE_FC_P
                            pkt.hdr_fc = fc_ch.ph.rx_credits_allocated
                            pkt.data_fc = fc_ch.pd.rx_credits_allocated
                            fc_ch.next_fc_p_tx = sim_time + self.fc_update_steps
                            break
                        if fc_ch.next_fc_np_tx <= sim_time:
                            pkt = Dllp()
                            pkt.vc = self.fc_init_vc
                            pkt.type = DllpType.UPDATE_FC_NP
                            pkt.hdr_fc = fc_ch.nph.rx_credits_allocated
                            pkt.data_fc = fc_ch.npd.rx_credits_allocated
                            fc_ch.next_fc_np_tx = sim_time + self.fc_update_steps
                            break
                        if fc_ch.next_fc_cpl_tx <= sim_time:
                            pkt = Dllp()
                            pkt.vc = self.fc_init_vc
                            pkt.type = DllpType.UPDATE_FC_CPL
                            pkt.hdr_fc = fc_ch.cplh.rx_credits_allocated
                            pkt.data_fc = fc_ch.cpld.rx_credits_allocated
                            fc_ch.next_fc_cpl_tx = sim_time + self.fc_update_steps
                            break

                if not self.fc_initialized and not pkt:
                    # Send FC init DLLP
                    fc_ch = self.fc_state[self.fc_init_vc]
                    pkt = Dllp()
                    pkt.vc = self.fc_init_vc
                    if self.fc_init_type == FcType.P:
                        pkt.type = DllpType.INIT_FC1_P if not fc_ch.fi1 else DllpType.INIT_FC2_P
                        pkt.hdr_fc = fc_ch.ph.rx_credits_allocated
                        pkt.data_fc = fc_ch.pd.rx_credits_allocated
                        self.fc_init_type = FcType.NP
                    elif self.fc_init_type == FcType.NP:
                        pkt.type = DllpType.INIT_FC1_NP if not fc_ch.fi1 else DllpType.INIT_FC2_NP
                        pkt.hdr_fc = fc_ch.nph.rx_credits_allocated
                        pkt.data_fc = fc_ch.npd.rx_credits_allocated
                        self.fc_init_type = FcType.CPL
                    elif self.fc_init_type == FcType.CPL:
                        pkt.type = DllpType.INIT_FC1_CPL if not fc_ch.fi1 else DllpType.INIT_FC2_CPL
                        pkt.hdr_fc = fc_ch.cplh.rx_credits_allocated
                        pkt.data_fc = fc_ch.cpld.rx_credits_allocated
                        self.fc_init_type = FcType.P
                        # find next active VC that hasn't finished FC init
                        for k in range(8):
                            vc = (self.fc_init_vc+1+k) % 8
                            if self.fc_state[vc].active and not self.fc_state[vc].fi2:
                                self.fc_init_vc = vc
                                break

                    # check all active VC and report FC not initialized if any are not complete
                    self.fc_initialized = True
                    for vc in range(8):
                        if self.fc_state[vc].active and not self.fc_state[vc].fi2:
                            self.fc_initialized = False

                if not pkt:
                    # no more DLLPs to send, clear event
                    self.send_fc.clear()

            if pkt is not None:
                self.log.debug("Send DLLP %s", pkt)
            elif not self.tx_queue.empty():
                pkt = self.tx_queue.get_nowait()
                pkt.seq = self.next_transmit_seq
                self.log.debug("Send TLP %s", pkt)
                self.next_transmit_seq = (self.next_transmit_seq + 1) & 0xfff
                self.retry_buffer.put_nowait(pkt)

            if pkt:
                await self.handle_tx(pkt)

    async def handle_tx(self, pkt):
        raise NotImplementedError()

    async def ext_recv(self, pkt):
        if isinstance(pkt, Dllp):
            # DLLP
            self.log.debug("Receive DLLP %s", pkt)
            self.handle_dllp(pkt)
        else:
            # TLP
            self.log.debug("Receive TLP %s", pkt)
            if pkt.seq == self.next_recv_seq:
                # expected seq
                self.next_recv_seq = (self.next_recv_seq + 1) & 0xfff
                self.nak_scheduled = False
                self.start_ack_latency_timer()
                pkt = Tlp(pkt)
                self.fc_state[self.classify_tlp_vc(pkt)].rx_process_tlp_fc(pkt)
                self.rx_queue.put_nowait(pkt)
            elif (self.next_recv_seq - pkt.seq) & 0xfff < 2048:
                self.log.warning("Received duplicate TLP, discarding (seq %d, expecting %d)", pkt.seq, self.next_recv_seq)
                self.stop_ack_latency_timer()
                self.send_ack.set()
            else:
                self.log.warning("Received out-of-sequence TLP, sending NAK (seq %d, expecting %d)", pkt.seq, self.next_recv_seq)
                if not self.nak_scheduled:
                    self.nak_scheduled = True
                    self.stop_ack_latency_timer()
                    self.send_ack.set()

    async def _run_receive(self):
        while True:
            tlp = await self.rx_queue.get()
            if self.rx_handler is None:
                raise Exception("Receive handler not set")
            await self.rx_handler(tlp)

    def handle_dllp(self, dllp):
        if dllp.type == DllpType.NOP:
            # discard NOP
            pass
        elif dllp.type in {DllpType.ACK, DllpType.NAK}:
            # ACK or NAK
            if (((self.next_transmit_seq-1) & 0xfff) - dllp.seq) & 0xfff > 2048:
                self.log.warning("Received ACK/NAK DLLP for future TLP, discarding (seq %d, next TX %d, ACK %d)",
                    dllp.seq, self.next_transmit_seq, self.ackd_seq)
            elif (dllp.seq - self.ackd_seq) & 0xfff > 2048:
                self.log.warning("Received ACK/NAK DLLP for previously-ACKed TLP, discarding (seq %d, next TX %d, ACK %d)",
                    dllp.seq, self.next_transmit_seq, self.ackd_seq)
            else:
                while dllp.seq != self.ackd_seq:
                    # purge TLPs from retry buffer
                    self.retry_buffer.get_nowait()
                    self.ackd_seq = (self.ackd_seq + 1) & 0xfff
                    self.log.debug("ACK TLP seq %d", self.ackd_seq)
                if dllp.type == DllpType.NAK:
                    # retransmit
                    self.log.warning("Got NAK DLLP, start TLP replay")
                    raise Exception("TODO")
        elif dllp.type in {DllpType.INIT_FC1_P, DllpType.INIT_FC1_NP, DllpType.INIT_FC1_CPL,
                DllpType.INIT_FC2_P, DllpType.INIT_FC2_NP, DllpType.INIT_FC2_CPL,
                DllpType.UPDATE_FC_P, DllpType.UPDATE_FC_NP, DllpType.UPDATE_FC_CPL}:
            # Flow control
            self.fc_state[dllp.vc].handle_fc_dllp(dllp)
        else:
            raise Exception("TODO")

    def start_ack_latency_timer(self):
        if self._ack_latency_timer_cr is not None:
            if not self._ack_latency_timer_cr._finished:
                # already running
                return
        self._ack_latency_timer_cr = cocotb.fork(self._run_ack_latency_timer())

    def stop_ack_latency_timer(self):
        if self._ack_latency_timer_cr is not None:
            self._ack_latency_timer_cr.kill()
            self._ack_latency_timer_cr = None

    async def _run_ack_latency_timer(self):
        d = int(self.time_scale * self.max_latency_timer)
        await Timer(max(d, 1), 'step')
        if not self.nak_scheduled:
            self.send_ack.set()

    def start_fc_update_timer(self):
        if self._fc_update_timer_cr is not None:
            if not self._fc_update_timer_cr._finished:
                # already running
                return
        self._fc_update_timer_cr = cocotb.fork(self._run_fc_update_timer())

    def stop_fc_update_timer(self):
        if self._fc_update_timer_cr is not None:
            self._fc_update_timer_cr.kill()
            self._fc_update_timer_cr = None

    async def _run_fc_update_timer(self):
        d = int(self.time_scale * self.max_latency_timer)
        await Timer(max(d, 1), 'step')
        self.send_fc.set()

    async def _run_fc_update_idle_timer(self):
        while True:
            await Timer(self.fc_idle_timer_steps, 'step')
            self.send_fc.set()
Пример #16
0
class XgmiiSource(Reset):
    def __init__(self,
                 data,
                 ctrl,
                 clock,
                 reset=None,
                 enable=None,
                 reset_active_level=True,
                 *args,
                 **kwargs):
        self.log = logging.getLogger(f"cocotb.{data._path}")
        self.data = data
        self.ctrl = ctrl
        self.clock = clock
        self.reset = reset
        self.enable = enable

        self.log.info("XGMII source")
        self.log.info("cocotbext-eth version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-eth")

        super().__init__(*args, **kwargs)

        self.active = False
        self.queue = Queue()
        self.dequeue_event = Event()
        self.current_frame = None
        self.idle_event = Event()
        self.idle_event.set()

        self.enable_dic = True
        self.ifg = 12
        self.force_offset_start = False

        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

        self.queue_occupancy_limit_bytes = -1
        self.queue_occupancy_limit_frames = -1

        self.width = len(self.data)
        self.byte_size = 8
        self.byte_lanes = len(self.ctrl)

        assert self.width == self.byte_lanes * self.byte_size

        self.log.info("XGMII source model configuration")
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width,
                      self.byte_lanes)

        self.idle_d = 0
        self.idle_c = 0

        for k in range(self.byte_lanes):
            self.idle_d |= XgmiiCtrl.IDLE << k * 8
            self.idle_c |= 1 << k

        self.data.setimmediatevalue(0)
        self.ctrl.setimmediatevalue(0)

        self._run_cr = None

        self._init_reset(reset, reset_active_level)

    async def send(self, frame):
        while self.full():
            self.dequeue_event.clear()
            await self.dequeue_event.wait()
        frame = XgmiiFrame(frame)
        await self.queue.put(frame)
        self.idle_event.clear()
        self.queue_occupancy_bytes += len(frame)
        self.queue_occupancy_frames += 1

    def send_nowait(self, frame):
        if self.full():
            raise QueueFull()
        frame = XgmiiFrame(frame)
        self.queue.put_nowait(frame)
        self.idle_event.clear()
        self.queue_occupancy_bytes += len(frame)
        self.queue_occupancy_frames += 1

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def full(self):
        if self.queue_occupancy_limit_bytes > 0 and self.queue_occupancy_bytes > self.queue_occupancy_limit_bytes:
            return True
        elif self.queue_occupancy_limit_frames > 0 and self.queue_occupancy_frames > self.queue_occupancy_limit_frames:
            return True
        else:
            return False

    def idle(self):
        return self.empty() and not self.active

    def clear(self):
        while not self.queue.empty():
            frame = self.queue.get_nowait()
            frame.sim_time_end = None
            frame.handle_tx_complete()
        self.dequeue_event.set()
        self.idle_event.set()
        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

    async def wait(self):
        await self.idle_event.wait()

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._run_cr is not None:
                self._run_cr.kill()
                self._run_cr = None

            self.active = False
            self.data.value = 0
            self.ctrl.value = 0

            if self.current_frame:
                self.log.warning("Flushed transmit frame during reset: %s",
                                 self.current_frame)
                self.current_frame.handle_tx_complete()
                self.current_frame = None

            if self.queue.empty():
                self.idle_event.set()
        else:
            self.log.info("Reset de-asserted")
            if self._run_cr is None:
                self._run_cr = cocotb.start_soon(self._run())

    async def _run(self):
        frame = None
        frame_offset = 0
        ifg_cnt = 0
        deficit_idle_cnt = 0
        self.active = False

        clock_edge_event = RisingEdge(self.clock)

        while True:
            await clock_edge_event

            if self.enable is None or self.enable.value:
                if ifg_cnt + deficit_idle_cnt > self.byte_lanes - 1 or (
                        not self.enable_dic and ifg_cnt > 4):
                    # in IFG
                    ifg_cnt = ifg_cnt - self.byte_lanes
                    if ifg_cnt < 0:
                        if self.enable_dic:
                            deficit_idle_cnt = max(deficit_idle_cnt + ifg_cnt,
                                                   0)
                        ifg_cnt = 0

                elif frame is None:
                    # idle
                    if not self.queue.empty():
                        # send frame
                        frame = self.queue.get_nowait()
                        self.dequeue_event.set()
                        self.queue_occupancy_bytes -= len(frame)
                        self.queue_occupancy_frames -= 1
                        self.current_frame = frame
                        frame.sim_time_start = get_sim_time()
                        frame.sim_time_sfd = None
                        frame.sim_time_end = None
                        self.log.info("TX frame: %s", frame)
                        frame.normalize()
                        frame.start_lane = 0
                        assert frame.data[0] == EthPre.PRE
                        assert frame.ctrl[0] == 0
                        frame.data[0] = XgmiiCtrl.START
                        frame.ctrl[0] = 1
                        frame.data.append(XgmiiCtrl.TERM)
                        frame.ctrl.append(1)

                        # offset start
                        if self.enable_dic:
                            min_ifg = 3 - deficit_idle_cnt
                        else:
                            min_ifg = 0

                        if self.byte_lanes > 4 and (ifg_cnt > min_ifg or
                                                    self.force_offset_start):
                            ifg_cnt = ifg_cnt - 4
                            frame.start_lane = 4
                            frame.data = bytearray(
                                [XgmiiCtrl.IDLE] * 4) + frame.data
                            frame.ctrl = [1] * 4 + frame.ctrl

                        if self.enable_dic:
                            deficit_idle_cnt = max(deficit_idle_cnt + ifg_cnt,
                                                   0)
                        ifg_cnt = 0
                        self.active = True
                        frame_offset = 0
                    else:
                        # clear counters
                        deficit_idle_cnt = 0
                        ifg_cnt = 0

                if frame is not None:
                    d_val = 0
                    c_val = 0

                    for k in range(self.byte_lanes):
                        if frame is not None:
                            d = frame.data[frame_offset]
                            if frame.sim_time_sfd is None and d == EthPre.SFD:
                                frame.sim_time_sfd = get_sim_time()
                            d_val |= d << k * 8
                            c_val |= frame.ctrl[frame_offset] << k
                            frame_offset += 1

                            if frame_offset >= len(frame.data):
                                ifg_cnt = max(self.ifg - (self.byte_lanes - k),
                                              0)
                                frame.sim_time_end = get_sim_time()
                                frame.handle_tx_complete()
                                frame = None
                                self.current_frame = None
                        else:
                            d_val |= XgmiiCtrl.IDLE << k * 8
                            c_val |= 1 << k

                    self.data.value = d_val
                    self.ctrl.value = c_val
                else:
                    self.data.value = self.idle_d
                    self.ctrl.value = self.idle_c
                    self.active = False
                    self.idle_event.set()
Пример #17
0
class MiiSink(Reset):
    def __init__(self,
                 data,
                 er,
                 dv,
                 clock,
                 reset=None,
                 enable=None,
                 reset_active_level=True,
                 *args,
                 **kwargs):
        self.log = logging.getLogger(f"cocotb.{data._path}")
        self.data = data
        self.er = er
        self.dv = dv
        self.clock = clock
        self.reset = reset
        self.enable = enable

        self.log.info("MII sink")
        self.log.info("cocotbext-eth version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-eth")

        super().__init__(*args, **kwargs)

        self.active = False
        self.queue = Queue()
        self.active_event = Event()

        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

        self.width = 4
        self.byte_width = 1

        assert len(self.data) == 4
        if self.er is not None:
            assert len(self.er) == 1
        if self.dv is not None:
            assert len(self.dv) == 1

        self._run_cr = None

        self._init_reset(reset, reset_active_level)

    def _recv(self, frame, compact=True):
        if self.queue.empty():
            self.active_event.clear()
        self.queue_occupancy_bytes -= len(frame)
        self.queue_occupancy_frames -= 1
        if compact:
            frame.compact()
        return frame

    async def recv(self, compact=True):
        frame = await self.queue.get()
        return self._recv(frame, compact)

    def recv_nowait(self, compact=True):
        frame = self.queue.get_nowait()
        return self._recv(frame, compact)

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def idle(self):
        return not self.active

    def clear(self):
        while not self.queue.empty():
            self.queue.get_nowait()
        self.active_event.clear()
        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

    async def wait(self, timeout=0, timeout_unit=None):
        if not self.empty():
            return
        if timeout:
            await First(self.active_event.wait(), Timer(timeout, timeout_unit))
        else:
            await self.active_event.wait()

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._run_cr is not None:
                self._run_cr.kill()
                self._run_cr = None

            self.active = False
        else:
            self.log.info("Reset de-asserted")
            if self._run_cr is None:
                self._run_cr = cocotb.start_soon(self._run())

    async def _run(self):
        frame = None
        self.active = False

        clock_edge_event = RisingEdge(self.clock)

        while True:
            await clock_edge_event

            if self.enable is None or self.enable.value:
                d_val = self.data.value.integer
                dv_val = self.dv.value.integer
                er_val = 0 if self.er is None else self.er.value.integer

                if frame is None:
                    if dv_val:
                        # start of frame
                        frame = GmiiFrame(bytearray(), [])
                        frame.sim_time_start = get_sim_time()
                else:
                    if not dv_val:
                        # end of frame
                        odd = True
                        sync = False
                        b = 0
                        be = 0
                        data = bytearray()
                        error = []
                        for n, e in zip(frame.data, frame.error):
                            odd = not odd
                            b = (n & 0x0F) << 4 | b >> 4
                            be |= e
                            if not sync and b == EthPre.SFD:
                                odd = True
                                sync = True
                            if odd:
                                data.append(b)
                                error.append(be)
                                be = 0
                        frame.data = data
                        frame.error = error

                        frame.compact()
                        frame.sim_time_end = get_sim_time()
                        self.log.info("RX frame: %s", frame)

                        self.queue_occupancy_bytes += len(frame)
                        self.queue_occupancy_frames += 1

                        self.queue.put_nowait(frame)
                        self.active_event.set()

                        frame = None

                if frame is not None:
                    if frame.sim_time_sfd is None and d_val == 0xD:
                        frame.sim_time_sfd = get_sim_time()

                    frame.data.append(d_val)
                    frame.error.append(er_val)
Пример #18
0
class XgmiiSink(Reset):
    def __init__(self,
                 data,
                 ctrl,
                 clock,
                 reset=None,
                 enable=None,
                 reset_active_level=True,
                 *args,
                 **kwargs):
        self.log = logging.getLogger(f"cocotb.{data._path}")
        self.data = data
        self.ctrl = ctrl
        self.clock = clock
        self.reset = reset
        self.enable = enable

        self.log.info("XGMII sink")
        self.log.info("cocotbext-eth version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-eth")

        super().__init__(*args, **kwargs)

        self.active = False
        self.queue = Queue()
        self.active_event = Event()

        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

        self.width = len(self.data)
        self.byte_size = 8
        self.byte_lanes = len(self.ctrl)

        assert self.width == self.byte_lanes * self.byte_size

        self.log.info("XGMII sink model configuration")
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width,
                      self.byte_lanes)

        self._run_cr = None

        self._init_reset(reset, reset_active_level)

    def _recv(self, frame, compact=True):
        if self.queue.empty():
            self.active_event.clear()
        self.queue_occupancy_bytes -= len(frame)
        self.queue_occupancy_frames -= 1
        if compact:
            frame.compact()
        return frame

    async def recv(self, compact=True):
        frame = await self.queue.get()
        return self._recv(frame, compact)

    def recv_nowait(self, compact=True):
        frame = self.queue.get_nowait()
        return self._recv(frame, compact)

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def idle(self):
        return not self.active

    def clear(self):
        while not self.queue.empty():
            self.queue.get_nowait()
        self.active_event.clear()
        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

    async def wait(self, timeout=0, timeout_unit=None):
        if not self.empty():
            return
        if timeout:
            await First(self.active_event.wait(), Timer(timeout, timeout_unit))
        else:
            await self.active_event.wait()

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._run_cr is not None:
                self._run_cr.kill()
                self._run_cr = None

            self.active = False
        else:
            self.log.info("Reset de-asserted")
            if self._run_cr is None:
                self._run_cr = cocotb.start_soon(self._run())

    async def _run(self):
        frame = None
        self.active = False

        clock_edge_event = RisingEdge(self.clock)

        while True:
            await clock_edge_event

            if self.enable is None or self.enable.value:
                for offset in range(self.byte_lanes):
                    d_val = (self.data.value.integer >> (offset * 8)) & 0xff
                    c_val = (self.ctrl.value.integer >> offset) & 1

                    if frame is None:
                        if c_val and d_val == XgmiiCtrl.START:
                            # start
                            frame = XgmiiFrame(bytearray([EthPre.PRE]), [0])
                            frame.sim_time_start = get_sim_time()
                            frame.start_lane = offset
                    else:
                        if c_val:
                            # got a control character; terminate frame reception
                            if d_val != XgmiiCtrl.TERM:
                                # store control character if it's not a termination
                                frame.data.append(d_val)
                                frame.ctrl.append(c_val)

                            frame.compact()
                            frame.sim_time_end = get_sim_time()
                            self.log.info("RX frame: %s", frame)

                            self.queue_occupancy_bytes += len(frame)
                            self.queue_occupancy_frames += 1

                            self.queue.put_nowait(frame)
                            self.active_event.set()

                            frame = None
                        else:
                            if frame.sim_time_sfd is None and d_val == EthPre.SFD:
                                frame.sim_time_sfd = get_sim_time()

                            frame.data.append(d_val)
                            frame.ctrl.append(c_val)
Пример #19
0
class S10PcieBase:

    _signal_widths = {"ready": 1}

    _valid_signal = "valid"
    _ready_signal = "ready"

    _transaction_obj = S10PcieTransaction
    _frame_obj = S10PcieFrame

    def __init__(self,
                 bus,
                 clock,
                 reset=None,
                 ready_latency=0,
                 *args,
                 **kwargs):
        self.bus = bus
        self.clock = clock
        self.reset = reset
        self.ready_latency = ready_latency
        self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")

        super().__init__(*args, **kwargs)

        self.active = False
        self.queue = Queue()
        self.dequeue_event = Event()
        self.idle_event = Event()
        self.idle_event.set()
        self.active_event = Event()

        self.pause = False
        self._pause_generator = None
        self._pause_cr = None

        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

        self.width = len(self.bus.data)
        self.byte_size = 32
        self.byte_lanes = self.width // self.byte_size
        self.byte_mask = 2**self.byte_size - 1

        self.seg_count = len(self.bus.valid)
        self.seg_width = self.width // self.seg_count
        self.seg_mask = 2**self.seg_width - 1
        self.seg_par_width = self.seg_width // 8
        self.seg_par_mask = 2**self.seg_par_width - 1
        self.seg_byte_lanes = self.byte_lanes // self.seg_count
        self.seg_empty_width = (self.seg_byte_lanes - 1).bit_length()
        self.seg_empty_mask = 2**self.seg_empty_width - 1

        assert self.width in {256, 512}

        assert len(self.bus.data) == self.seg_count * self.seg_width
        assert len(self.bus.sop) == self.seg_count
        assert len(self.bus.eop) == self.seg_count
        assert len(self.bus.valid) == self.seg_count

        if hasattr(self.bus, "empty"):
            assert len(self.bus.empty) == self.seg_count * self.seg_empty_width

        if hasattr(self.bus, "err"):
            assert len(self.bus.err) == self.seg_count
        if hasattr(self.bus, "bar_range"):
            assert len(self.bus.bar_range) == self.seg_count * 3

        if hasattr(self.bus, "vf_active"):
            assert len(self.bus.vf_active) == self.seg_count
        if hasattr(self.bus, "func_num"):
            assert len(self.bus.func_num) == self.seg_count * 2
        if hasattr(self.bus, "vf_num"):
            assert len(self.bus.vf_num) == self.seg_count * 11

        if hasattr(self.bus, "parity"):
            assert len(self.bus.parity) == self.seg_count * self.seg_width // 8

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def clear(self):
        while not self.queue.empty():
            self.queue.get_nowait()
        self.idle_event.set()
        self.active_event.clear()

    def idle(self):
        raise NotImplementedError()

    async def wait(self):
        raise NotImplementedError()

    def set_pause_generator(self, generator=None):
        if self._pause_cr is not None:
            self._pause_cr.kill()
            self._pause_cr = None

        self._pause_generator = generator

        if self._pause_generator is not None:
            self._pause_cr = cocotb.start_soon(self._run_pause())

    def clear_pause_generator(self):
        self.set_pause_generator(None)

    async def _run_pause(self):
        clock_edge_event = RisingEdge(self.clock)

        for val in self._pause_generator:
            self.pause = val
            await clock_edge_event
Пример #20
0
class AxiMasterRead(Reset):
    def __init__(self, bus, clock, reset=None, reset_active_level=True, max_burst_len=256):
        self.log = logging.getLogger(f"cocotb.{bus.ar._entity._name}.{bus.ar._name}")

        self.log.info("AXI master (read)")
        self.log.info("cocotbext-axi version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-axi")

        self.ar_channel = AxiARSource(bus.ar, clock, reset, reset_active_level)
        self.ar_channel.queue_occupancy_limit = 2
        self.r_channel = AxiRSink(bus.r, clock, reset, reset_active_level)
        self.r_channel.queue_occupancy_limit = 2

        self.read_command_queue = Queue()
        self.current_read_command = None

        self.id_count = 2**len(self.ar_channel.bus.arid)
        self.cur_id = 0
        self.active_id = Counter()

        self.int_read_resp_command_queue = [Queue() for k in range(self.id_count)]
        self.current_read_resp_command = [None for k in range(self.id_count)]
        self.int_read_resp_queue_list = [Queue() for k in range(self.id_count)]

        self.in_flight_operations = 0
        self._idle = Event()
        self._idle.set()

        self.width = len(self.r_channel.bus.rdata)
        self.byte_size = 8
        self.byte_width = self.width // self.byte_size

        self.max_burst_len = max(min(max_burst_len, 256), 1)
        self.max_burst_size = (self.byte_width-1).bit_length()

        self.log.info("AXI master configuration:")
        self.log.info("  Address width: %d bits", len(self.ar_channel.bus.araddr))
        self.log.info("  ID width: %d bits", len(self.ar_channel.bus.arid))
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width, self.byte_width)
        self.log.info("  Max burst size: %d (%d bytes)", self.max_burst_size, 2**self.max_burst_size)
        self.log.info("  Max burst length: %d cycles (%d bytes)",
            self.max_burst_len, self.max_burst_len*self.byte_width)

        assert self.byte_width * self.byte_size == self.width

        assert len(self.r_channel.bus.rid) == len(self.ar_channel.bus.arid)

        self._process_read_cr = None
        self._process_read_resp_cr = None
        self._process_read_resp_id_cr = None

        self._init_reset(reset, reset_active_level)

    def init_read(self, address, length, arid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, event=None):

        if event is None:
            event = Event()

        if not isinstance(event, Event):
            raise ValueError("Expected event object")

        if length < 0:
            raise ValueError("Read length must be positive")

        if arid is None or arid < 0:
            arid = None
        elif arid > self.id_count:
            raise ValueError("Requested ID exceeds maximum ID allowed for ID signal width")

        burst = AxiBurstType(burst)

        if size is None or size < 0:
            size = self.max_burst_size
        elif size > self.max_burst_size:
            raise ValueError("Requested burst size exceeds maximum burst size allowed for bus width")

        lock = AxiLockType(lock)
        prot = AxiProt(prot)

        self.in_flight_operations += 1
        self._idle.clear()

        cmd = AxiReadCmd(address, length, arid, burst, size, lock, cache, prot, qos, region, user, event)
        self.read_command_queue.put_nowait(cmd)

        return event

    def idle(self):
        return not self.in_flight_operations

    async def wait(self):
        while not self.idle():
            await self._idle.wait()

    async def read(self, address, length, arid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0):
        event = self.init_read(address, length, arid, burst, size, lock, cache, prot, qos, region, user)
        await event.wait()
        return event.data

    async def read_words(self, address, count, byteorder='little', ws=2, arid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0):
        data = await self.read(address, count*ws, arid, burst, size, lock, cache, prot, qos, region, user)
        words = []
        for k in range(count):
            words.append(int.from_bytes(data.data[ws*k:ws*(k+1)], byteorder))
        return words

    async def read_dwords(self, address, count, byteorder='little', arid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0):
        return await self.read_words(address, count, byteorder, 4, arid, burst, size,
            lock, cache, prot, qos, region, user)

    async def read_qwords(self, address, count, byteorder='little', arid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0):
        return await self.read_words(address, count, byteorder, 8, arid, burst, size,
            lock, cache, prot, qos, region, user)

    async def read_byte(self, address, arid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0):
        return (await self.read(address, 1, arid, burst, size, lock, cache, prot, qos, region, user)).data[0]

    async def read_word(self, address, byteorder='little', ws=2, arid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0):
        return (await self.read_words(address, 1, byteorder, ws, arid, burst, size,
            lock, cache, prot, qos, region, user))[0]

    async def read_dword(self, address, byteorder='little', arid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0):
        return (await self.read_dwords(address, 1, byteorder, arid, burst, size,
            lock, cache, prot, qos, region, user))[0]

    async def read_qword(self, address, byteorder='little', arid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0):
        return (await self.read_qwords(address, 1, byteorder, arid, burst, size,
            lock, cache, prot, qos, region, user))[0]

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._process_read_cr is not None:
                self._process_read_cr.kill()
                self._process_read_cr = None
            if self._process_read_resp_cr is not None:
                self._process_read_resp_cr.kill()
                self._process_read_resp_cr = None
            if self._process_read_resp_id_cr is not None:
                for cr in self._process_read_resp_id_cr:
                    cr.kill()
                self._process_read_resp_id_cr = None

            self.ar_channel.clear()
            self.r_channel.clear()

            def flush_cmd(cmd):
                self.log.warning("Flushed read operation during reset: %s", cmd)
                if cmd.event:
                    cmd.event.set(None)

            while not self.read_command_queue.empty():
                cmd = self.read_command_queue.get_nowait()
                flush_cmd(cmd)

            if self.current_read_command:
                cmd = self.current_read_command
                self.current_read_command = None
                flush_cmd(cmd)

            for q in self.int_read_resp_command_queue:
                while not q.empty():
                    cmd = q.get_nowait()
                    flush_cmd(cmd)

            for k in range(len(self.current_read_resp_command)):
                if self.current_read_resp_command[k]:
                    cmd = self.current_read_resp_command[k]
                    self.current_read_resp_command[k] = None
                    flush_cmd(cmd)

            for q in self.int_read_resp_queue_list:
                while not q.empty():
                    q.get_nowait()

            self.cur_id = 0
            self.active_id = Counter()

            self.in_flight_operations = 0
            self._idle.set()
        else:
            self.log.info("Reset de-asserted")
            if self._process_read_cr is None:
                self._process_read_cr = cocotb.fork(self._process_read())
            if self._process_read_resp_cr is None:
                self._process_read_resp_cr = cocotb.fork(self._process_read_resp())
            if self._process_read_resp_id_cr is None:
                self._process_read_resp_id_cr = [cocotb.fork(self._process_read_resp_id(i)) for i in range(self.id_count)]

    async def _process_read(self):
        while True:
            cmd = await self.read_command_queue.get()
            self.current_read_command = cmd

            num_bytes = 2**cmd.size

            aligned_addr = (cmd.address // num_bytes) * num_bytes

            cycles = (cmd.length + num_bytes-1 + (cmd.address % num_bytes)) // num_bytes

            burst_list = []

            cur_addr = aligned_addr
            n = 0

            burst_length = 0

            if cmd.arid is not None:
                arid = cmd.arid
            else:
                arid = self.cur_id
                self.cur_id = (self.cur_id+1) % self.id_count

            self.log.info("Read start addr: 0x%08x arid: 0x%x prot: %s", cmd.address, arid, cmd.prot)

            for k in range(cycles):

                n += 1
                if n >= burst_length:
                    n = 0

                    # split on burst length
                    burst_length = min(cycles-k, min(max(self.max_burst_len, 1), 256))
                    # split on 4k address boundary
                    burst_length = (min(burst_length*num_bytes, 0x1000-(cur_addr & 0xfff))+num_bytes-1)//num_bytes

                    burst_list.append(burst_length)

                    ar = self.r_channel._transaction_obj()
                    ar.arid = arid
                    ar.araddr = cur_addr
                    ar.arlen = burst_length-1
                    ar.arsize = cmd.size
                    ar.arburst = cmd.burst
                    ar.arlock = cmd.lock
                    ar.arcache = cmd.cache
                    ar.arprot = cmd.prot
                    ar.arqos = cmd.qos
                    ar.arregion = cmd.region
                    ar.aruser = cmd.user

                    self.active_id[arid] += 1
                    await self.ar_channel.send(ar)

                    self.log.info("Read burst start arid: 0x%x araddr: 0x%08x arlen: %d arsize: %d arprot: %s",
                        arid, cur_addr, burst_length-1, cmd.size, cmd.prot)

                cur_addr += num_bytes

            resp_cmd = AxiReadRespCmd(cmd.address, cmd.length, cmd.size, cycles, cmd.prot, burst_list, cmd.event)
            await self.int_read_resp_command_queue[arid].put(resp_cmd)

            self.current_read_command = None

    async def _process_read_resp(self):
        while True:
            r = await self.r_channel.recv()

            rid = int(r.rid)

            if self.active_id[rid] <= 0:
                raise Exception(f"Unexpected burst ID {rid}")

            await self.int_read_resp_queue_list[rid].put(r)

    async def _process_read_resp_id(self, rid):
        while True:
            cmd = await self.int_read_resp_command_queue[rid].get()
            self.current_read_resp_command[rid] = cmd

            num_bytes = 2**cmd.size

            aligned_addr = (cmd.address // num_bytes) * num_bytes
            word_addr = (cmd.address // self.byte_width) * self.byte_width

            start_offset = cmd.address % self.byte_width

            cycle_offset = aligned_addr - word_addr
            data = bytearray()

            resp = AxiResp.OKAY
            user = []

            first = True

            for burst_length in cmd.burst_list:
                for k in range(burst_length):
                    r = await self.int_read_resp_queue_list[rid].get()

                    cycle_id = int(r.rid)
                    cycle_data = int(r.rdata)
                    cycle_resp = AxiResp(r.rresp)
                    cycle_last = int(r.rlast)
                    cycle_user = int(r.ruser)

                    if cycle_resp != AxiResp.OKAY:
                        resp = cycle_resp

                    if cycle_user is not None:
                        user.append(cycle_user)

                    start = cycle_offset
                    stop = cycle_offset+num_bytes

                    if first:
                        start = start_offset

                    assert cycle_last == (k == burst_length - 1)

                    for j in range(start, stop):
                        data.append((cycle_data >> j*8) & 0xff)

                    cycle_offset = (cycle_offset + num_bytes) % self.byte_width

                    first = False

                if self.active_id[rid] <= 0:
                    raise Exception(f"Unexpected burst ID {rid}")

                self.active_id[rid] -= 1

                self.log.info("Read burst complete rid: 0x%x rresp: %s", cycle_id, resp)

            data = data[:cmd.length]

            self.log.info("Read complete addr: 0x%08x prot: %s resp: %s data: %s",
                cmd.address, cmd.prot, resp, ' '.join((f'{c:02x}' for c in data)))

            read_resp = AxiReadResp(cmd.address, data, resp, user)

            cmd.event.set(read_resp)

            self.current_read_resp_command[rid] = None

            self.in_flight_operations -= 1

            if self.in_flight_operations == 0:
                self._idle.set()
Пример #21
0
class StreamBase(Reset):

    _signals = ["data", "valid", "ready"]
    _optional_signals = []

    _signal_widths = {"valid": 1, "ready": 1}

    _init_x = False

    _valid_signal = "valid"
    _valid_init = None
    _ready_signal = "ready"
    _ready_init = None

    _transaction_obj = StreamTransaction
    _bus_obj = StreamBus

    def __init__(self,
                 bus,
                 clock,
                 reset=None,
                 reset_active_level=True,
                 *args,
                 **kwargs):
        self.bus = bus
        self.clock = clock
        self.reset = reset
        self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")

        super().__init__(*args, **kwargs)

        self.active = False

        self.queue = Queue()
        self.dequeue_event = Event()
        self.idle_event = Event()
        self.idle_event.set()
        self.active_event = Event()

        self.ready = None
        self.valid = None

        if self._ready_signal is not None and hasattr(self.bus,
                                                      self._ready_signal):
            self.ready = getattr(self.bus, self._ready_signal)
            if self._ready_init is not None:
                self.ready.setimmediatevalue(self._ready_init)

        if self._valid_signal is not None and hasattr(self.bus,
                                                      self._valid_signal):
            self.valid = getattr(self.bus, self._valid_signal)
            if self._valid_init is not None:
                self.valid.setimmediatevalue(self._valid_init)

        for sig in self._signals + self._optional_signals:
            if hasattr(self.bus, sig):
                if sig in self._signal_widths:
                    assert len(getattr(self.bus,
                                       sig)) == self._signal_widths[sig]
                if self._init_x and sig not in (self._valid_signal,
                                                self._ready_signal):
                    v = getattr(self.bus, sig).value
                    v.binstr = 'x' * len(v)
                    getattr(self.bus, sig).setimmediatevalue(v)

        self._run_cr = None

        self._init_reset(reset, reset_active_level)

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def clear(self):
        while not self.queue.empty():
            self.queue.get_nowait()
        self.dequeue_event.set()
        self.idle_event.set()
        self.active_event.clear()

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._run_cr is not None:
                self._run_cr.kill()
                self._run_cr = None

            self.active = False

            if self.queue.empty():
                self.idle_event.set()
        else:
            self.log.info("Reset de-asserted")
            if self._run_cr is None:
                self._run_cr = cocotb.start_soon(self._run())

    async def _run(self):
        raise NotImplementedError()
Пример #22
0
class AxiMasterWrite(Reset):
    def __init__(self, bus, clock, reset=None, reset_active_level=True, max_burst_len=256):
        self.log = logging.getLogger(f"cocotb.{bus.aw._entity._name}.{bus.aw._name}")

        self.log.info("AXI master (write)")
        self.log.info("cocotbext-axi version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-axi")

        self.aw_channel = AxiAWSource(bus.aw, clock, reset, reset_active_level)
        self.aw_channel.queue_occupancy_limit = 2
        self.w_channel = AxiWSource(bus.w, clock, reset, reset_active_level)
        self.w_channel.queue_occupancy_limit = 2
        self.b_channel = AxiBSink(bus.b, clock, reset, reset_active_level)
        self.b_channel.queue_occupancy_limit = 2

        self.write_command_queue = Queue()
        self.current_write_command = None

        self.id_count = 2**len(self.aw_channel.bus.awid)
        self.cur_id = 0
        self.active_id = Counter()

        self.int_write_resp_command_queue = [Queue() for k in range(self.id_count)]
        self.current_write_resp_command = [None for k in range(self.id_count)]
        self.int_write_resp_queue_list = [Queue() for k in range(self.id_count)]

        self.in_flight_operations = 0
        self._idle = Event()
        self._idle.set()

        self.width = len(self.w_channel.bus.wdata)
        self.byte_size = 8
        self.byte_width = self.width // self.byte_size
        self.strb_mask = 2**self.byte_width-1

        self.max_burst_len = max(min(max_burst_len, 256), 1)
        self.max_burst_size = (self.byte_width-1).bit_length()

        self.log.info("AXI master configuration:")
        self.log.info("  Address width: %d bits", len(self.aw_channel.bus.awaddr))
        self.log.info("  ID width: %d bits", len(self.aw_channel.bus.awid))
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width, self.byte_width)
        self.log.info("  Max burst size: %d (%d bytes)", self.max_burst_size, 2**self.max_burst_size)
        self.log.info("  Max burst length: %d cycles (%d bytes)",
            self.max_burst_len, self.max_burst_len*self.byte_width)

        assert self.byte_width == len(self.w_channel.bus.wstrb)
        assert self.byte_width * self.byte_size == self.width

        assert len(self.b_channel.bus.bid) == len(self.aw_channel.bus.awid)

        self._process_write_cr = None
        self._process_write_resp_cr = None
        self._process_write_resp_id_cr = None

        self._init_reset(reset, reset_active_level)

    def init_write(self, address, data, awid=None, burst=AxiBurstType.INCR, size=None, lock=AxiLockType.NORMAL,
            cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0, event=None):

        if event is None:
            event = Event()

        if not isinstance(event, Event):
            raise ValueError("Expected event object")

        if awid is None or awid < 0:
            awid = None
        elif awid > self.id_count:
            raise ValueError("Requested ID exceeds maximum ID allowed for ID signal width")

        burst = AxiBurstType(burst)

        if size is None or size < 0:
            size = self.max_burst_size
        elif size > self.max_burst_size:
            raise ValueError("Requested burst size exceeds maximum burst size allowed for bus width")

        lock = AxiLockType(lock)
        prot = AxiProt(prot)

        if wuser is None:
            wuser = 0
        elif isinstance(wuser, int):
            pass
        else:
            wuser = list(wuser)

        self.in_flight_operations += 1
        self._idle.clear()

        cmd = AxiWriteCmd(address, bytearray(data), awid, burst, size, lock,
            cache, prot, qos, region, user, wuser, event)
        self.write_command_queue.put_nowait(cmd)

        return event

    def idle(self):
        return not self.in_flight_operations

    async def wait(self):
        while not self.idle():
            await self._idle.wait()

    async def write(self, address, data, awid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0):
        event = self.init_write(address, data, awid, burst, size, lock, cache, prot, qos, region, user, wuser)
        await event.wait()
        return event.data

    async def write_words(self, address, data, byteorder='little', ws=2, awid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0):
        words = data
        data = bytearray()
        for w in words:
            data.extend(w.to_bytes(ws, byteorder))
        await self.write(address, data, awid, burst, size, lock, cache, prot, qos, region, user, wuser)

    async def write_dwords(self, address, data, byteorder='little', awid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0):
        await self.write_words(address, data, byteorder, 4, awid, burst, size,
            lock, cache, prot, qos, region, user, wuser)

    async def write_qwords(self, address, data, byteorder='little', awid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0):
        await self.write_words(address, data, byteorder, 8, awid, burst, size,
            lock, cache, prot, qos, region, user, wuser)

    async def write_byte(self, address, data, awid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0):
        await self.write(address, [data], awid, burst, size, lock, cache, prot, qos, region, user, wuser)

    async def write_word(self, address, data, byteorder='little', ws=2, awid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0):
        await self.write_words(address, [data], byteorder, ws, awid, burst, size,
            lock, cache, prot, qos, region, user, wuser)

    async def write_dword(self, address, data, byteorder='little', awid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0):
        await self.write_dwords(address, [data], byteorder, awid, burst, size,
            lock, cache, prot, qos, region, user, wuser)

    async def write_qword(self, address, data, byteorder='little', awid=None, burst=AxiBurstType.INCR, size=None,
            lock=AxiLockType.NORMAL, cache=0b0011, prot=AxiProt.NONSECURE, qos=0, region=0, user=0, wuser=0):
        await self.write_qwords(address, [data], byteorder, awid, burst, size,
            lock, cache, prot, qos, region, user, wuser)

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._process_write_cr is not None:
                self._process_write_cr.kill()
                self._process_write_cr = None
            if self._process_write_resp_cr is not None:
                self._process_write_resp_cr.kill()
                self._process_write_resp_cr = None
            if self._process_write_resp_id_cr is not None:
                for cr in self._process_write_resp_id_cr:
                    cr.kill()
                self._process_write_resp_id_cr = None

            self.aw_channel.clear()
            self.w_channel.clear()
            self.b_channel.clear()

            def flush_cmd(cmd):
                self.log.warning("Flushed write operation during reset: %s", cmd)
                if cmd.event:
                    cmd.event.set(None)

            while not self.write_command_queue.empty():
                cmd = self.write_command_queue.get_nowait()
                flush_cmd(cmd)

            if self.current_write_command:
                cmd = self.current_write_command
                self.current_write_command = None
                flush_cmd(cmd)

            for q in self.int_write_resp_command_queue:
                while not q.empty():
                    cmd = q.get_nowait()
                    flush_cmd(cmd)

            for k in range(len(self.current_write_resp_command)):
                if self.current_write_resp_command[k]:
                    cmd = self.current_write_resp_command[k]
                    self.current_write_resp_command[k] = None
                    flush_cmd(cmd)

            for q in self.int_write_resp_queue_list:
                while not q.empty():
                    q.get_nowait()

            self.cur_id = 0
            self.active_id = Counter()

            self.in_flight_operations = 0
            self._idle.set()
        else:
            self.log.info("Reset de-asserted")
            if self._process_write_cr is None:
                self._process_write_cr = cocotb.fork(self._process_write())
            if self._process_write_resp_cr is None:
                self._process_write_resp_cr = cocotb.fork(self._process_write_resp())
            if self._process_write_resp_id_cr is None:
                self._process_write_resp_id_cr = [cocotb.fork(self._process_write_resp_id(i)) for i in range(self.id_count)]

    async def _process_write(self):
        while True:
            cmd = await self.write_command_queue.get()
            self.current_write_command = cmd

            num_bytes = 2**cmd.size

            aligned_addr = (cmd.address // num_bytes) * num_bytes
            word_addr = (cmd.address // self.byte_width) * self.byte_width

            start_offset = cmd.address % self.byte_width
            end_offset = ((cmd.address + len(cmd.data) - 1) % self.byte_width) + 1

            cycles = (len(cmd.data) + (cmd.address % num_bytes) + num_bytes-1) // num_bytes

            cur_addr = aligned_addr
            offset = 0
            cycle_offset = aligned_addr-word_addr
            n = 0
            transfer_count = 0

            burst_list = []
            burst_length = 0

            if cmd.awid is not None:
                awid = cmd.awid
            else:
                awid = self.cur_id
                self.cur_id = (self.cur_id+1) % self.id_count

            wuser = cmd.wuser

            self.log.info("Write start addr: 0x%08x awid: 0x%x prot: %s data: %s",
                cmd.address, awid, cmd.prot, ' '.join((f'{c:02x}' for c in cmd.data)))

            for k in range(cycles):
                start = cycle_offset
                stop = cycle_offset+num_bytes

                if k == 0:
                    start = start_offset
                if k == cycles-1:
                    stop = end_offset

                strb = (self.strb_mask << start) & self.strb_mask & (self.strb_mask >> (self.byte_width - stop))

                val = 0
                for j in range(start, stop):
                    val |= cmd.data[offset] << j*8
                    offset += 1

                if n >= burst_length:
                    transfer_count += 1
                    n = 0

                    # split on burst length
                    burst_length = min(cycles-k, min(max(self.max_burst_len, 1), 256))
                    # split on 4k address boundary
                    burst_length = (min(burst_length*num_bytes, 0x1000-(cur_addr & 0xfff))+num_bytes-1)//num_bytes

                    burst_list.append(burst_length)

                    aw = self.aw_channel._transaction_obj()
                    aw.awid = awid
                    aw.awaddr = cur_addr
                    aw.awlen = burst_length-1
                    aw.awsize = cmd.size
                    aw.awburst = cmd.burst
                    aw.awlock = cmd.lock
                    aw.awcache = cmd.cache
                    aw.awprot = cmd.prot
                    aw.awqos = cmd.qos
                    aw.awregion = cmd.region
                    aw.awuser = cmd.user

                    self.active_id[awid] += 1
                    await self.aw_channel.send(aw)

                    self.log.info("Write burst start awid: 0x%x awaddr: 0x%08x awlen: %d awsize: %d awprot: %s",
                        awid, cur_addr, burst_length-1, cmd.size, cmd.prot)

                n += 1

                w = self.w_channel._transaction_obj()
                w.wdata = val
                w.wstrb = strb
                w.wlast = n >= burst_length

                if isinstance(wuser, int):
                    w.wuser = wuser
                else:
                    if wuser:
                        w.wuser = wuser.pop(0)
                    else:
                        w.wuser = 0

                await self.w_channel.send(w)

                cur_addr += num_bytes
                cycle_offset = (cycle_offset + num_bytes) % self.byte_width

            resp_cmd = AxiWriteRespCmd(cmd.address, len(cmd.data), cmd.size, cycles, cmd.prot, burst_list, cmd.event)
            await self.int_write_resp_command_queue[awid].put(resp_cmd)

            self.current_write_command = None

    async def _process_write_resp(self):
        while True:
            b = await self.b_channel.recv()

            bid = int(b.bid)

            if self.active_id[bid] <= 0:
                raise Exception(f"Unexpected burst ID {bid}")

            await self.int_write_resp_queue_list[bid].put(b)

    async def _process_write_resp_id(self, bid):
        while True:
            cmd = await self.int_write_resp_command_queue[bid].get()
            self.current_write_resp_command[bid] = cmd

            resp = AxiResp.OKAY
            user = []

            for burst_length in cmd.burst_list:
                b = await self.int_write_resp_queue_list[bid].get()

                burst_id = int(b.bid)
                burst_resp = AxiResp(b.bresp)
                burst_user = int(b.buser)

                if burst_resp != AxiResp.OKAY:
                    resp = burst_resp

                if burst_user is not None:
                    user.append(burst_user)

                if self.active_id[bid] <= 0:
                    raise Exception(f"Unexpected burst ID {bid}")

                self.active_id[bid] -= 1

                self.log.info("Write burst complete bid: 0x%x bresp: %s", burst_id, burst_resp)

            self.log.info("Write complete addr: 0x%08x prot: %s resp: %s length: %d",
                cmd.address, cmd.prot, resp, cmd.length)

            write_resp = AxiWriteResp(cmd.address, cmd.length, resp, user)

            cmd.event.set(write_resp)

            self.current_write_resp_command[bid] = None

            self.in_flight_operations -= 1

            if self.in_flight_operations == 0:
                self._idle.set()
Пример #23
0
class UsPcieBase:

    _signal_widths = {"tvalid": 1, "tready": 1}

    _valid_signal = "tvalid"
    _ready_signal = "tready"

    _transaction_obj = UsPcieTransaction
    _frame_obj = UsPcieFrame

    def __init__(self, bus, clock, reset=None, *args, **kwargs):
        self.bus = bus
        self.clock = clock
        self.reset = reset
        self.log = logging.getLogger(f"cocotb.{bus._entity._name}.{bus._name}")

        super().__init__(*args, **kwargs)

        self.active = False
        self.queue = Queue()
        self.dequeue_event = Event()
        self.idle_event = Event()
        self.idle_event.set()
        self.active_event = Event()

        self.pause = False
        self._pause_generator = None
        self._pause_cr = None

        self.queue_occupancy_bytes = 0
        self.queue_occupancy_frames = 0

        self.width = len(self.bus.tdata)
        self.byte_lanes = len(self.bus.tkeep)

        self.byte_size = self.width // self.byte_lanes
        self.byte_mask = 2**self.byte_size - 1

        assert self.width in [64, 128, 256, 512]
        assert self.byte_size == 32

    def _init(self):
        pass

    def count(self):
        return self.queue.qsize()

    def empty(self):
        return self.queue.empty()

    def clear(self):
        while not self.queue.empty():
            self.queue.get_nowait()
        self.idle_event.set()
        self.active_event.clear()

    def idle(self):
        raise NotImplementedError()

    async def wait(self):
        raise NotImplementedError()

    def set_pause_generator(self, generator=None):
        if self._pause_cr is not None:
            self._pause_cr.kill()
            self._pause_cr = None

        self._pause_generator = generator

        if self._pause_generator is not None:
            self._pause_cr = cocotb.fork(self._run_pause())

    def clear_pause_generator(self):
        self.set_pause_generator(None)

    async def _run_pause(self):
        for val in self._pause_generator:
            self.pause = val
            await RisingEdge(self.clock)
Пример #24
0
class AxiLiteMasterRead(Reset):
    def __init__(self, bus, clock, reset=None, reset_active_level=True):
        self.bus = bus
        self.clock = clock
        self.reset = reset
        self.log = logging.getLogger(
            f"cocotb.{bus.ar._entity._name}.{bus.ar._name}")

        self.log.info("AXI lite master (read)")
        self.log.info("cocotbext-axi version %s", __version__)
        self.log.info("Copyright (c) 2020 Alex Forencich")
        self.log.info("https://github.com/alexforencich/cocotbext-axi")

        self.ar_channel = AxiLiteARSource(bus.ar, clock, reset,
                                          reset_active_level)
        self.ar_channel.queue_occupancy_limit = 2
        self.r_channel = AxiLiteRSink(bus.r, clock, reset, reset_active_level)
        self.r_channel.queue_occupancy_limit = 2

        self.read_command_queue = Queue()
        self.current_read_command = None

        self.int_read_resp_command_queue = Queue()
        self.current_read_resp_command = None

        self.in_flight_operations = 0
        self._idle = Event()
        self._idle.set()

        self.width = len(self.r_channel.bus.rdata)
        self.byte_size = 8
        self.byte_lanes = self.width // self.byte_size

        self.arprot_present = hasattr(self.bus.ar, "arprot")

        self.log.info("AXI lite master configuration:")
        self.log.info("  Address width: %d bits",
                      len(self.ar_channel.bus.araddr))
        self.log.info("  Byte size: %d bits", self.byte_size)
        self.log.info("  Data width: %d bits (%d bytes)", self.width,
                      self.byte_lanes)

        self.log.info("AXI lite master signals:")
        for bus in (self.bus.ar, self.bus.r):
            for sig in sorted(
                    list(set().union(bus._signals, bus._optional_signals))):
                if hasattr(bus, sig):
                    self.log.info("  %s width: %d bits", sig,
                                  len(getattr(bus, sig)))
                else:
                    self.log.info("  %s: not present", sig)

        assert self.byte_lanes * self.byte_size == self.width

        self._process_read_cr = None
        self._process_read_resp_cr = None

        self._init_reset(reset, reset_active_level)

    def init_read(self, address, length, prot=AxiProt.NONSECURE, event=None):
        if event is None:
            event = Event()

        if not isinstance(event, Event):
            raise ValueError("Expected event object")

        if not self.arprot_present and prot != AxiProt.NONSECURE:
            raise ValueError(
                "arprot sideband signal value specified, but signal is not connected"
            )

        self.in_flight_operations += 1
        self._idle.clear()

        self.read_command_queue.put_nowait(
            AxiLiteReadCmd(address, length, prot, event))

        return event

    def idle(self):
        return not self.in_flight_operations

    async def wait(self):
        while not self.idle():
            await self._idle.wait()

    async def read(self, address, length, prot=AxiProt.NONSECURE):
        event = self.init_read(address, length, prot)
        await event.wait()
        return event.data

    async def read_words(self,
                         address,
                         count,
                         byteorder='little',
                         ws=2,
                         prot=AxiProt.NONSECURE):
        data = await self.read(address, count * ws, prot)
        words = []
        for k in range(count):
            words.append(
                int.from_bytes(data.data[ws * k:ws * (k + 1)], byteorder))
        return words

    async def read_dwords(self,
                          address,
                          count,
                          byteorder='little',
                          prot=AxiProt.NONSECURE):
        return await self.read_words(address, count, byteorder, 4, prot)

    async def read_qwords(self,
                          address,
                          count,
                          byteorder='little',
                          prot=AxiProt.NONSECURE):
        return await self.read_words(address, count, byteorder, 8, prot)

    async def read_byte(self, address, prot=AxiProt.NONSECURE):
        return (await self.read(address, 1, prot)).data[0]

    async def read_word(self,
                        address,
                        byteorder='little',
                        ws=2,
                        prot=AxiProt.NONSECURE):
        return (await self.read_words(address, 1, byteorder, ws, prot))[0]

    async def read_dword(self,
                         address,
                         byteorder='little',
                         prot=AxiProt.NONSECURE):
        return (await self.read_dwords(address, 1, byteorder, prot))[0]

    async def read_qword(self,
                         address,
                         byteorder='little',
                         prot=AxiProt.NONSECURE):
        return (await self.read_qwords(address, 1, byteorder, prot))[0]

    def _handle_reset(self, state):
        if state:
            self.log.info("Reset asserted")
            if self._process_read_cr is not None:
                self._process_read_cr.kill()
                self._process_read_cr = None
            if self._process_read_resp_cr is not None:
                self._process_read_resp_cr.kill()
                self._process_read_resp_cr = None

            self.ar_channel.clear()
            self.r_channel.clear()

            def flush_cmd(cmd):
                self.log.warning("Flushed read operation during reset: %s",
                                 cmd)
                if cmd.event:
                    cmd.event.set(None)

            while not self.read_command_queue.empty():
                cmd = self.read_command_queue.get_nowait()
                flush_cmd(cmd)

            if self.current_read_command:
                cmd = self.current_read_command
                self.current_read_command = None
                flush_cmd(cmd)

            while not self.int_read_resp_command_queue.empty():
                cmd = self.int_read_resp_command_queue.get_nowait()
                flush_cmd(cmd)

            if self.current_read_resp_command:
                cmd = self.current_read_resp_command
                self.current_read_resp_command = None
                flush_cmd(cmd)

            self.in_flight_operations = 0
            self._idle.set()
        else:
            self.log.info("Reset de-asserted")
            if self._process_read_cr is None:
                self._process_read_cr = cocotb.fork(self._process_read())
            if self._process_read_resp_cr is None:
                self._process_read_resp_cr = cocotb.fork(
                    self._process_read_resp())

    async def _process_read(self):
        while True:
            cmd = await self.read_command_queue.get()
            self.current_read_command = cmd

            word_addr = (cmd.address // self.byte_lanes) * self.byte_lanes

            cycles = (cmd.length + self.byte_lanes - 1 +
                      (cmd.address % self.byte_lanes)) // self.byte_lanes

            resp_cmd = AxiLiteReadRespCmd(cmd.address, cmd.length, cycles,
                                          cmd.prot, cmd.event)
            await self.int_read_resp_command_queue.put(resp_cmd)

            self.log.info("Read start addr: 0x%08x prot: %s length: %d",
                          cmd.address, cmd.prot, cmd.length)

            for k in range(cycles):
                ar = self.ar_channel._transaction_obj()
                ar.araddr = word_addr + k * self.byte_lanes
                ar.arprot = cmd.prot

                await self.ar_channel.send(ar)

            self.current_read_command = None

    async def _process_read_resp(self):
        while True:
            cmd = await self.int_read_resp_command_queue.get()
            self.current_read_resp_command = cmd

            start_offset = cmd.address % self.byte_lanes
            end_offset = ((cmd.address + cmd.length - 1) % self.byte_lanes) + 1

            data = bytearray()

            resp = AxiResp.OKAY

            for k in range(cmd.cycles):
                r = await self.r_channel.recv()

                cycle_data = int(r.rdata)
                cycle_resp = AxiResp(getattr(r, 'rresp', AxiResp.OKAY))

                if cycle_resp != AxiResp.OKAY:
                    resp = cycle_resp

                start = 0
                stop = self.byte_lanes

                if k == 0:
                    start = start_offset
                if k == cmd.cycles - 1:
                    stop = end_offset

                for j in range(start, stop):
                    data.extend(bytearray([(cycle_data >> j * 8) & 0xff]))

            if self.log.isEnabledFor(logging.INFO):
                self.log.info(
                    "Read complete addr: 0x%08x prot: %s resp: %s data: %s",
                    cmd.address, cmd.prot, resp, ' '.join(
                        (f'{c:02x}' for c in data)))

            read_resp = AxiLiteReadResp(cmd.address, data, resp)

            cmd.event.set(read_resp)

            self.current_read_resp_command = None

            self.in_flight_operations -= 1

            if self.in_flight_operations == 0:
                self._idle.set()