Skip to content
Snippets Groups Projects
architecture.py 16.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • """
    Module for code generation of VHDL architectures.
    """
    from io import TextIOWrapper
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    from typing import TYPE_CHECKING, Dict, Set, Tuple, cast
    
    
    from b_asic.codegen import vhdl
    from b_asic.process import MemoryVariable, PlainMemoryVariable
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
    
    if TYPE_CHECKING:
        from b_asic.resources import ProcessCollection, _ForwardBackwardTable
    
    def write_memory_based_storage(
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        assignment: Set["ProcessCollection"],
    
        word_length: int,
        read_ports: int,
        write_ports: int,
        total_ports: int,
    
    ):
        """
        Generate the VHDL architecture for a memory based architecture from a process collection of memory variables.
    
        Parameters
        ----------
    
            A possible cell assignment to use when generating the memory based storage.
            The cell assignment is a dictionary int to ProcessCollection where the integer
            corresponds to the cell to assign all MemoryVariables in corresponding process
            collection.
            If unset, each MemoryVariable will be assigned to a unique cell.
        f : TextIOWrapper
            File object (or other TextIOWrapper object) to write the architecture onto.
    
            Word length of the memory variable objects.
    
            Number of write ports.
    
            Total concurrent memory accesses possible.
    
        input_sync : bool, default: True
            Add registers to the input signals (enable signal and data input signals).
            Adding registers to the inputs allow pipelining of address generation (which is added automatically).
            For large interleavers, this can improve timing significantly.
    
        """
    
        # Code settings
        mem_depth = len(assignment)
        architecture_name = "rtl"
        schedule_time = next(iter(assignment))._schedule_time
    
        # Write architecture header
    
        vhdl.write(
            f, 0, f'architecture {architecture_name} of {entity_name} is', end='\n\n'
        )
    
    
        #
        # Architecture declerative region begin
        #
    
        vhdl.write(f, 1, '-- HDL memory description')
    
        vhdl.common.write_constant_decl(
            f, name='MEM_WL', type='integer', value=word_length, name_pad=12
        )
        vhdl.common.write_constant_decl(
            f, name='MEM_DEPTH', type='integer', value=mem_depth, name_pad=12
        )
        vhdl.common.write_type_decl(
            f, 'mem_type', 'array(0 to MEM_DEPTH-1) of std_logic_vector(MEM_WL-1 downto 0)'
        )
    
        vhdl.common.write_signal_decl(
            f, name='memory', type='mem_type', name_pad=14, vivado_ram_style='distributed'
        )
    
        for i in range(read_ports):
            vhdl.common.write_signal_decl(
                f, f'read_port_{i}', 'std_logic_vector(MEM_WL-1 downto 0)', name_pad=14
            )
            vhdl.common.write_signal_decl(
                f, f'read_adr_{i}', f'integer range 0 to {schedule_time}-1', name_pad=14
            )
            vhdl.common.write_signal_decl(f, f'read_en_{i}', 'std_logic', name_pad=14)
        for i in range(write_ports):
            vhdl.common.write_signal_decl(
                f, f'write_port_{i}', 'std_logic_vector(MEM_WL-1 downto 0)', name_pad=14
            )
            vhdl.common.write_signal_decl(
                f, f'write_adr_{i}', f'integer range 0 to {schedule_time}-1', name_pad=14
            )
            vhdl.common.write_signal_decl(f, f'write_en_{i}', 'std_logic', name_pad=14)
    
        # Schedule time counter
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Schedule counter', start='\n')
    
        vhdl.common.write_signal_decl(
            f,
            name='schedule_cnt',
            type=f'integer range 0 to {schedule_time}-1',
            name_pad=14,
        )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            vhdl.write(f, 1, '-- Input synchronization', start='\n')
    
            for i in range(read_ports):
                vhdl.common.write_signal_decl(
                    f, f'p_{i}_in_sync', 'std_logic_vector(WL-1 downto 0)', name_pad=14
                )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 0, 'begin', start='\n', end='\n\n')
        vhdl.write(f, 1, '-- Schedule counter')
    
        vhdl.common.write_synchronous_process_prologue(
            f=f,
            name='schedule_cnt_proc',
            clk='clk',
        )
        vhdl.write_lines(
            f,
            [
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                (3, 'if rst = \'1\' then'),
                (4, 'schedule_cnt <= 0;'),
                (3, 'else'),
                (4, 'if en = \'1\' then'),
    
                (5, f'if schedule_cnt = {schedule_time-1} then'),
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                (6, 'schedule_cnt <= 0;'),
                (5, 'else'),
                (6, 'schedule_cnt <= schedule_cnt + 1;'),
                (5, 'end if;'),
                (4, 'end if;'),
                (3, 'end if;'),
    
            ],
        )
        vhdl.common.write_synchronous_process_epilogue(
    
            f=f,
            name='schedule_cnt_proc',
            clk='clk',
        )
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            vhdl.write(f, 1, '-- Input synchronization', start='\n')
    
            vhdl.common.write_synchronous_process_prologue(
                f=f,
                name='input_sync_proc',
                clk='clk',
            )
            for i in range(read_ports):
    
                vhdl.write(f, 3, f'p_{i}_in_sync <= p_{i}_in;')
    
            vhdl.common.write_synchronous_process_epilogue(
                f=f,
                name='input_sync_proc',
                clk='clk',
            )
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Memory', start='\n')
    
        vhdl.common.write_asynchronous_read_memory(
    
            f=f,
            clk='clk',
            name=f'mem_{0}_proc',
            read_ports={
                (f'read_port_{i}', f'read_adr_{i}', f'read_en_{i}')
                for i in range(read_ports)
            },
            write_ports={
                (f'write_port_{i}', f'write_adr_{i}', f'write_en_{i}')
                for i in range(write_ports)
            },
        )
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Memory write address generation', start='\n')
    
        if input_sync:
            vhdl.common.write_synchronous_process_prologue(
                f, clk="clk", name="mem_write_address_proc"
            )
        else:
            vhdl.common.write_process_prologue(
                f, sensitivity_list="schedule_cnt", name="mem_write_address_proc"
            )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 3, 'case schedule_cnt is')
    
        for i, collection in enumerate(assignment):
            for mv in collection:
                mv = cast(MemoryVariable, mv)
    
                    vhdl.write_lines(
                        f,
                        [
                            (4, f'-- {mv!r}'),
                            (4, f'when {(mv.start_time) % schedule_time} =>'),
                            (5, f'write_adr_0 <= {i};'),
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                            (5, 'write_en_0 <= \'1\';'),
    
                        ],
                    )
        vhdl.write_lines(
            f,
            [
                (4, 'when others =>'),
                (5, 'write_adr_0 <= 0;'),
                (5, 'write_en_0 <= \'0\';'),
                (3, 'end case;'),
            ],
        )
    
        if input_sync:
            vhdl.common.write_synchronous_process_epilogue(
                f, clk="clk", name="mem_write_address_proc"
            )
        else:
            vhdl.common.write_process_epilogue(
                f, sensitivity_list="clk", name="mem_write_address_proc"
            )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Memory read address generation', start='\n')
    
        vhdl.common.write_synchronous_process_prologue(
            f, clk="clk", name="mem_read_address_proc"
        )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 3, 'case schedule_cnt is')
    
        for i, collection in enumerate(assignment):
            for mv in collection:
                mv = cast(PlainMemoryVariable, mv)
    
                for read_time in mv.reads.values():
    
                    vhdl.write(
                        f,
                        4,
                        'when'
                        f' {(mv.start_time+read_time-int(not(input_sync))) % schedule_time} =>',
                    )
                    vhdl.write_lines(
                        f,
                        [
                            (5, f'read_adr_0 <= {i};'),
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                            (5, 'read_en_0 <= \'1\';'),
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                (4, 'when others =>'),
                (5, 'read_adr_0 <= 0;'),
                (5, 'read_en_0 <= \'0\';'),
                (3, 'end case;'),
    
        vhdl.common.write_synchronous_process_epilogue(
            f, clk="clk", name="mem_read_address_proc"
        )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Input and output assignmentn', start='\n')
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            vhdl.write(f, 1, 'write_port_0 <= p_0_in_sync;')
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            vhdl.write(f, 1, 'write_port_0 <= p_0_in;')
    
        p_zero_exec = filter(
            lambda p: p.execution_time == 0, (p for pc in assignment for p in pc)
        )
        vhdl.common.write_synchronous_process_prologue(
            f,
            clk='clk',
            name='output_reg_proc',
        )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 3, 'case schedule_cnt is')
    
                write_time = (p.start_time + 1) % schedule_time
                vhdl.write(f, 4, f'when {write_time} => p_0_out <= p_0_in_sync;')
    
                write_time = (p.start_time) % schedule_time
                vhdl.write(f, 4, f'when {write_time} => p_0_out <= p_0_in;')
        vhdl.write_lines(
            f,
            [
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                (4, 'when others => p_0_out <= read_port_0;'),
                (3, 'end case;'),
    
        vhdl.common.write_synchronous_process_epilogue(
            f,
            clk='clk',
            name='output_reg_proc',
        )
    
        vhdl.write(f, 0, f'end architecture {architecture_name};', start='\n')
    
    
    
    def write_register_based_storage(
        f: TextIOWrapper,
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        forward_backward_table: "_ForwardBackwardTable",
    
        entity_name: str,
        word_length: int,
        read_ports: int,
        write_ports: int,
        total_ports: int,
    
        sync_rst: bool = False,
        async_rst: bool = False,
    
    ):
        architecture_name = "rtl"
        schedule_time = len(forward_backward_table)
    
        reg_cnt = len(forward_backward_table[0].regs)
    
    
        # Set of the register indices to output from
        output_regs = {entry.outputs_from for entry in forward_backward_table.table}
        if None in output_regs:
            output_regs.remove(None)
        output_regs = cast(Set[int], output_regs)
    
        # Table with mapping: register to output multiplexer index
        output_mux_table = {reg: i for i, reg in enumerate(output_regs)}
    
        # Back-edge register indices
        back_edges: Set[Tuple[int, int]] = {
            (frm, to)
            for entry in forward_backward_table
            for frm, to in entry.back_edge_to.items()
        }
        back_edge_table: Dict[Tuple[int, int], int] = {
            edge: i + 1 for i, edge in enumerate(back_edges)
        }
    
    
        #
        # Architecture declerative region begin
        #
        # Write architecture header
    
        vhdl.write(
            f, 0, f'architecture {architecture_name} of {entity_name} is', end='\n\n'
        )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Schedule counter')
    
        vhdl.common.write_signal_decl(
            f,
            name='schedule_cnt',
            type=f'integer range 0 to {schedule_time}-1',
    
            default_value='0',
        )
    
        # Shift register
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Shift register', start='\n')
    
        vhdl.common.write_type_decl(
            f,
            name='shift_reg_type',
            alias=f'array(0 to {reg_cnt}-1) of std_logic_vector(WL-1 downto 0)',
        )
        vhdl.common.write_signal_decl(
            f,
            name='shift_reg',
            type='shift_reg_type',
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Back-edge mux select signal', start='\n')
    
        vhdl.common.write_signal_decl(
            f,
            name='back_edge_mux_sel',
            type=f'integer range 0 to {len(back_edges)}',
            name_pad=18,
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Output mux select signal', start='\n')
    
        vhdl.common.write_signal_decl(
            f,
            name='out_mux_sel',
            type=f'integer range 0 to {len(output_regs)-1}',
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 0, 'begin', start='\n', end='\n\n')
        vhdl.write(f, 1, '-- Schedule counter')
    
        vhdl.common.write_synchronous_process_prologue(
            f=f,
            name='schedule_cnt_proc',
            clk='clk',
        )
        vhdl.write_lines(
            f,
            [
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                (4, 'if en = \'1\' then'),
    
                (5, f'if schedule_cnt = {schedule_time}-1 then'),
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                (6, 'schedule_cnt <= 0;'),
                (5, 'else'),
                (6, 'schedule_cnt <= schedule_cnt + 1;'),
                (5, 'end if;'),
                (4, 'end if;'),
    
            ],
        )
        vhdl.common.write_synchronous_process_epilogue(
    
            f=f,
            name='schedule_cnt_proc',
            clk='clk',
        )
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Shift register back-edge decoding', start='\n')
    
        vhdl.common.write_synchronous_process_prologue(
            f,
            clk='clk',
            name='shift_reg_back_edge_decode_proc',
        )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 3, 'case schedule_cnt is')
    
        for time, entry in enumerate(forward_backward_table):
            if entry.back_edge_to:
                assert len(entry.back_edge_to) == 1
                for src, dst in entry.back_edge_to.items():
                    mux_idx = back_edge_table[(src, dst)]
    
                    vhdl.write_lines(
                        f,
                        [
                            (4, f'when {(time-1)%schedule_time} =>'),
                            (5, f'-- ({src} -> {dst})'),
                            (5, f'back_edge_mux_sel <= {mux_idx};'),
                        ],
                    )
        vhdl.write_lines(
            f,
            [
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                (4, 'when others =>'),
                (5, 'back_edge_mux_sel <= 0;'),
                (3, 'end case;'),
    
        vhdl.common.write_synchronous_process_epilogue(
            f,
            clk='clk',
            name='shift_reg_back_edge_decode_proc',
        )
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Multiplexers for shift register', start='\n')
    
        vhdl.common.write_synchronous_process_prologue(
            f,
            clk='clk',
            name='shift_reg_proc',
        )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            vhdl.write(f, 3, 'if rst = \'1\' then')
    
            for reg_idx in range(reg_cnt):
                vhdl.write(f, 4, f'shift_reg({reg_idx}) <= (others => \'0\');')
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            vhdl.write(f, 3, 'else')
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                (3, '-- Default case'),
                (3, 'shift_reg(0) <= p_0_in;'),
    
        for reg_idx in range(1, reg_cnt):
    
            vhdl.write(f, 3, f'shift_reg({reg_idx}) <= shift_reg({reg_idx-1});')
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 3, 'case back_edge_mux_sel is')
    
        for edge, mux_sel in back_edge_table.items():
    
            vhdl.write_lines(
                f,
                [
                    (4, f'when {mux_sel} =>'),
                    (5, f'shift_reg({edge[1]}) <= shift_reg({edge[0]});'),
                ],
            )
        vhdl.write_lines(
            f,
            [
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
                (4, 'when others => null;'),
                (3, 'end case;'),
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
            vhdl.write(f, 3, 'end if;')
    
    
        vhdl.common.write_synchronous_process_epilogue(
            f,
            clk='clk',
            name='shift_reg_proc',
        )
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Output muliplexer decoding logic', start='\n')
    
        vhdl.common.write_synchronous_process_prologue(
            f, clk='clk', name='out_mux_decode_proc'
        )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 3, 'case schedule_cnt is')
    
        for i, entry in enumerate(forward_backward_table):
            if entry.outputs_from is not None:
    
                sel = output_mux_table[entry.outputs_from]
                vhdl.write(f, 4, f'when {(i-1)%schedule_time} =>')
                vhdl.write(f, 5, f'out_mux_sel <= {sel};')
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 3, 'end case;')
    
        vhdl.common.write_synchronous_process_epilogue(
            f, clk='clk', name='out_mux_decode_proc'
        )
    
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 1, '-- Output muliplexer', start='\n')
    
        vhdl.common.write_synchronous_process_prologue(
            f,
            clk='clk',
            name='out_mux_proc',
        )
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 3, 'case out_mux_sel is')
    
        for reg_i, mux_i in output_mux_table.items():
    
            vhdl.write(f, 4, f'when {mux_i} =>')
    
                vhdl.write(f, 5, f'p_0_out <= p_{-1-reg_i}_in;')
    
                vhdl.write(f, 5, f'p_0_out <= shift_reg({reg_i});')
    
    Oscar Gustafsson's avatar
    Oscar Gustafsson committed
        vhdl.write(f, 3, 'end case;')
    
        vhdl.common.write_synchronous_process_epilogue(
            f,
            clk='clk',
            name='out_mux_proc',
        )
    
    
        vhdl.write(f, 0, f'end architecture {architecture_name};', start='\n')