Kestrel-3

Artifact [b7cd4b6657]
Login

Artifact b7cd4b66572d80708cde97e018d5078e99c2dd8e5a57b43bc1b72d6020c6de3e:


#!/usr/bin/env python
#
# Instruction Fetch Unit for the KCP53000B.

from nmigen import Elaboratable, Signal, Module, Cat, Const, ResetSignal
from nmigen.cli import main


class IFU(Elaboratable):

    def __init__(self, reset_pc=0):
        self.reset_pc = reset_pc

        self.o_ia_address = Signal(64)
        self.o_ia_mask = Signal(8)
        self.o_ia_valid = Signal()
        self.i_ia_ready = Signal()
        self.o_ia_priv = Signal()

        self.o_id_ready = Signal()
        self.i_id_valid = Signal()
        self.i_id_data = Signal(64)
        self.i_id_error = Signal()

        self.i_iq_ready = Signal()
        self.o_iq_valid = Signal()
        self.i_iq_vacancy = Signal()
        self.o_iq_instruction = Signal(32)
        self.o_iq_instr_access_fault = Signal()
        self.o_iq_inst_addr = Signal(64)
        self.o_iq_flush = Signal()

        self.i_ifu_jump_req = Signal()
        self.o_ifu_jump_ack = Signal()
        self.i_ifu_pc = Signal(64)

        self.i_ifu_priv = Signal()

    @property
    def port_list(self):
        return [
            self.o_ia_address,
            self.o_ia_mask,
            self.o_ia_valid,
            self.i_ia_ready,
            self.o_ia_priv,

            self.o_id_ready,
            self.i_id_valid,
            self.i_id_data,
            self.i_id_error,

            self.i_iq_ready,
            self.o_iq_valid,
            self.i_iq_vacancy,
            self.o_iq_instruction,
            self.o_iq_instr_access_fault,
            self.o_iq_inst_addr,
            self.o_iq_flush,

            self.i_ifu_jump_req,
            self.o_ifu_jump_ack,
            self.i_ifu_pc,

            self.i_ifu_priv,
        ]

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

        #
        # The A channel of the I port
        #

        # The fetch counter is where we want to read the next
        # instruction from.  On hard reset, it is set to the
        # reset_pc value.  Unaligned instructions are not
        # supported in the KCP53000B, so the low 2 bits are
        # hardwired 0.
        #
        # Assuming we're not branching somewhere, the fetch counter
        # will increment after we're done with each address phase.

        fetch_counter = Signal(62, reset=(self.reset_pc >> 2))
        instruction_address = Signal(62)

        with m.If(self.o_ia_valid & self.i_ia_ready):
            m.d.sync += [
                instruction_address.eq(fetch_counter),
                fetch_counter.eq((fetch_counter + 1)[0:62]),
            ]

        m.d.comb += self.o_ia_address.eq(Cat(Const(0, 2), fetch_counter))

        # We're only ever going to address 32-bit aligned words from
        # memory, so reflect this in the byte select mask.

        with m.If(fetch_counter[0]):
            m.d.comb += self.o_ia_mask.eq(Const(0xF0, 8))

        with m.If(~fetch_counter[0]):
            m.d.comb += self.o_ia_mask.eq(Const(0x0F, 8))

        # The o_ia_valid signal is asserted if and only if the IFU is
        # driving valid information on the remainder of the o_ia_xxx
        # vector.  Since it's driven by the state machine backing the
        # IFU, you can find its drive below in the state machine
        # section of the code.

        #
        # I-port state machine (also drives the D channel logic)
        #
        # o_ia_valid o_id_ready State
        #     0          0      Idle
        #     1          0      Address phase
        #     0          1      Data phase
        #     1          1      Illegal
        #
        # Note that the o_iq_valid pin is a *pulsed* signal
        # and does not conform to the usual "wait until both
        # ready and valid are asserted" protocol.  In actual
        # practice, this is not expected to cause problems,
        # as the i_iq_ready and o_iq_valid waveforms will more
        # or less resemble every other interface.  But, beware,
        # this is a side-effect of the implementation, and not
        # by design.

        with m.If(
            ~self.o_ia_valid & ~self.o_id_ready &
            self.i_iq_ready
        ):
            m.d.sync += [
                self.o_ia_valid.eq(1),
                self.o_ia_priv.eq(self.i_ifu_priv),
            ]

        with m.If(self.o_ia_valid & self.i_ia_ready):
            m.d.sync += [
                self.o_ia_valid.eq(0),
                self.o_id_ready.eq(1),
            ]

        m.d.sync += self.o_iq_valid.eq(0)
        with m.If(self.o_id_ready & self.i_id_valid):
            m.d.sync += [
                self.o_iq_inst_addr.eq(Cat(Const(0, 2), instruction_address)),
                self.o_iq_valid.eq(1),
            ]

            with m.If(instruction_address[0]):
                m.d.sync += self.o_iq_instruction.eq(self.i_id_data[32:64])
            with m.If(~instruction_address[0]):
                m.d.sync += self.o_iq_instruction.eq(self.i_id_data[0:32])
            m.d.sync += self.o_iq_instr_access_fault.eq(self.i_id_error)

            with m.If(~self.i_iq_vacancy):
                m.d.sync += [
                    self.o_ia_valid.eq(0),
                    self.o_id_ready.eq(0),
                ]

            with m.If(self.i_iq_vacancy):
                m.d.sync += [
                    self.o_ia_valid.eq(1),
                    self.o_id_ready.eq(0),
                    self.o_ia_priv.eq(self.i_ifu_priv),
                ]

        # A control transfer is requested by the i_iq_jump_req signal.
        # It's sampled only at the end of the current memory fetch.
        # However, when the control flow is acknowledged, the
        # o_iq_jump_ack signal is asserted in response.

        m.d.sync += self.o_ifu_jump_ack.eq(0)
        m.d.comb += self.o_iq_flush.eq(self.o_ifu_jump_ack)
        with m.If(self.o_id_ready & self.i_id_valid & self.i_ifu_jump_req):
            m.d.sync += [
                self.o_ifu_jump_ack.eq(1),
                fetch_counter.eq(self.i_ifu_pc[2:64]),
            ]

        return m


if __name__ == '__main__':
    ifu = IFU()
    main(ifu, ports=ifu.port_list)