Skip to content
Snippets Groups Projects
Commit 3292e158 authored by Mikael Henriksson's avatar Mikael Henriksson :runner:
Browse files

codegen: add pipelined back-edge mux decode logic to register based storage

parent b991d33e
No related branches found
No related tags found
1 merge request!250Changes to code generation as a result of FPL-2023
......@@ -2,7 +2,34 @@
Module for basic VHDL code generation.
"""
from io import TextIOWrapper
# VHDL code generation tab length
VHDL_TAB = r" "
def write(
f: TextIOWrapper,
indent_level: int,
text: str,
end: str = '\n',
):
"""
Base VHDL code generation utility. `f'{VHDL_TAB*indent_level}'` is first written to the :class:`io.TextIOWrapper`
object `f`. Immediatly after the indentation, `text` is written to `f`. Finally, `text` is also written to `f`.
Parameters
----------
f : :class:`io.TextIOWrapper`
The file object to emit the VHDL code to.
indent_level : int
Indentation level to use. Exactly `f'{VHDL_TAB*indent_level}` is written before the text is written.
text : str
The text to write to.
end : str, default: '\n'
Text to write exactly after `text` is written to `f`.
"""
f.write(f'{VHDL_TAB*indent_level}{text}{end}')
from b_asic.codegen.vhdl import architecture, common, entity
......@@ -2,7 +2,7 @@
Module for code generation of VHDL architectures.
"""
from io import TextIOWrapper
from typing import Set, cast
from typing import Dict, List, Set, Tuple, cast
from b_asic.codegen import vhdl
from b_asic.codegen.vhdl import VHDL_TAB
......@@ -263,8 +263,29 @@ def write_register_based_storage(
):
architecture_name = "rtl"
schedule_time = len(forward_backward_table)
# Number of registers in this design
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
#
......@@ -277,7 +298,7 @@ def write_register_based_storage(
f,
name='schedule_cnt',
type=f'integer range 0 to {schedule_time}-1',
name_pad=14,
name_pad=18,
default_value='0',
)
......@@ -292,17 +313,25 @@ def write_register_based_storage(
f,
name='shift_reg',
type='shift_reg_type',
name_pad=14,
name_pad=18,
)
# Back edge mux decoder
f.write(f'\n{VHDL_TAB}-- Back-edge mux select signal\n')
vhdl.common.write_signal_decl(
f,
name='back_edge_mux_sel',
type=f'integer range 0 to {len(back_edges)}',
name_pad=18,
)
# Output mux selector
f.write(f'\n{VHDL_TAB}-- Output mux select signal\n')
output_regs = {entry.outputs_from for entry in forward_backward_table.table}
vhdl.common.write_signal_decl(
f,
name='out_mux_sel',
type=f'integer range 0 to {len(output_regs)-1}',
name_pad=14,
name_pad=18,
)
#
......@@ -326,6 +355,31 @@ def write_register_based_storage(
),
)
# Shift register back-edge decoding
f.write(f'\n{VHDL_TAB}-- Shift register back-edge decoding\n')
vhdl.common.write_synchronous_process_prologue(
f,
clk='clk',
name='shift_reg_back_edge_decode_proc',
)
vhdl.write(f, 3, f'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(f, 4, f'when {(time-1)%schedule_time} =>')
vhdl.write(f, 5, f'-- ({src} -> {dst})')
vhdl.write(f, 5, f'back_edge_mux_sel <= {mux_idx};')
vhdl.write(f, 4, f'when others =>')
vhdl.write(f, 5, f'back_edge_mux_sel <= 0;')
vhdl.write(f, 3, f'end case;')
vhdl.common.write_synchronous_process_epilogue(
f,
clk='clk',
name='shift_reg_back_edge_decode_proc',
)
# Shift register multiplexer logic
f.write(f'\n{VHDL_TAB}-- Multiplexers for shift register\n')
vhdl.common.write_synchronous_process_prologue(
......@@ -337,13 +391,16 @@ def write_register_based_storage(
f.write(f'{3*VHDL_TAB}shift_reg(0) <= p_0_in;\n')
for reg_idx in range(1, reg_cnt):
f.write(f'{3*VHDL_TAB}shift_reg({reg_idx}) <= shift_reg({reg_idx-1});\n')
f.write(f'{3*VHDL_TAB}case schedule_cnt is\n')
for i, entry in enumerate(forward_backward_table):
if entry.back_edge_from:
f.write(f'{4*VHDL_TAB} when {schedule_time-1 if (i-1)<0 else (i-1)} =>\n')
for dst, src in entry.back_edge_from.items():
f.write(f'{5*VHDL_TAB} shift_reg({dst}) <= shift_reg({src});\n')
vhdl.write(f, 3, f'case back_edge_mux_sel is')
for edge, mux_sel in back_edge_table.items():
vhdl.write(f, 4, f'when {mux_sel} =>')
vhdl.write(f, 5, f'shift_reg({edge[1]}) <= shift_reg({edge[0]});')
# f.write(f'{3*VHDL_TAB}case schedule_cnt is\n')
# for i, entry in enumerate(forward_backward_table):
# if entry.back_edge_from:
# f.write(f'{4*VHDL_TAB} when {schedule_time-1 if (i-1)<0 else (i-1)} =>\n')
# for dst, src in entry.back_edge_from.items():
# f.write(f'{5*VHDL_TAB} shift_reg({dst}) <= shift_reg({src});\n')
f.write(f'{4*VHDL_TAB}when others => null;\n')
f.write(f'{3*VHDL_TAB}end case;\n')
......@@ -353,29 +410,37 @@ def write_register_based_storage(
name='shift_reg_proc',
)
# Output multiplexer decoding logic
f.write(f'\n{VHDL_TAB}-- Output muliplexer decoding logic\n')
vhdl.common.write_synchronous_process_prologue(
f, clk='clk', name='out_mux_decode_proc'
)
f.write(f'{3*VHDL_TAB}case schedule_cnt is\n')
for i, entry in enumerate(forward_backward_table):
if entry.outputs_from is not None:
f.write(f'{4*VHDL_TAB}when {(i-1)%schedule_time} =>\n')
f.write(
f'{5*VHDL_TAB}out_mux_sel <= {output_mux_table[entry.outputs_from]};\n'
)
f.write(f'{3*VHDL_TAB}end case;\n')
vhdl.common.write_synchronous_process_epilogue(
f, clk='clk', name='out_mux_decode_proc'
)
# Output multiplexer logic
f.write(f'\n{VHDL_TAB}-- Output muliplexer\n')
f.write(f'\n{VHDL_TAB}-- {output_regs}\n')
f.write(f'\n{VHDL_TAB}-- { list(range(len(output_regs))) }\n')
vhdl.common.write_synchronous_process_prologue(
f,
clk='clk',
name='out_mux_proc',
)
f.write(f'{3*VHDL_TAB}-- Default case\n')
f.write(f'{3*VHDL_TAB}p_0_out <= shift_reg({reg_cnt-1});\n')
f.write(f'{3*VHDL_TAB}case schedule_cnt is\n')
for i, entry in enumerate(forward_backward_table):
if entry.outputs_from is not None:
if entry.outputs_from != reg_cnt - 1:
f.write(f'{4*VHDL_TAB} when {i} =>\n')
if entry.outputs_from < 0:
f.write(f'{5*VHDL_TAB} p_0_out <= p_{-1-entry.outputs_from}_in;\n')
else:
f.write(
f'{5*VHDL_TAB} p_0_out <= shift_reg({entry.outputs_from});\n'
)
f.write(f'{4*VHDL_TAB}when others => null;\n')
f.write(f'{3*VHDL_TAB}case out_mux_sel is\n')
for reg_i, mux_i in output_mux_table.items():
f.write(f'{4*VHDL_TAB}when {mux_i} =>\n')
if reg_i < 0:
f.write(f'{5*VHDL_TAB}p_0_out <= p_{-1-reg_i}_in;\n')
else:
f.write(f'{5*VHDL_TAB}p_0_out <= shift_reg({reg_i});\n')
f.write(f'{3*VHDL_TAB}end case;\n')
vhdl.common.write_synchronous_process_epilogue(
f,
......
......@@ -7,6 +7,7 @@ from io import TextIOWrapper
from subprocess import PIPE, Popen
from typing import Any, Optional, Set, Tuple
from b_asic.codegen import vhdl
from b_asic.codegen.vhdl import VHDL_TAB
......@@ -26,13 +27,13 @@ def write_b_asic_vhdl_preamble(f: TextIOWrapper):
git_commit_id = process.communicate()[0].decode('utf-8').strip()
except:
pass
f.write(f'--\n')
f.write(f'-- This code was automatically generated by the B-ASIC toolbox.\n')
f.write(f'-- Code generation timestamp: ({datetime.now()})\n')
vhdl.write(f, 0, f'--')
vhdl.write(f, 0, f'-- This code was automatically generated by the B-ASIC toolbox.')
vhdl.write(f, 0, f'-- Code generation timestamp: ({datetime.now()})')
if git_commit_id:
f.write(f'-- B-ASIC short commit hash: {git_commit_id}\n')
f.write(f'-- URL: https://gitlab.liu.se/da/B-ASIC\n')
f.write(f'--\n\n')
vhdl.write(f, 0, f'-- B-ASIC short commit hash: {git_commit_id}')
vhdl.write(f, 0, f'-- URL: https://gitlab.liu.se/da/B-ASIC')
vhdl.write(f, 0, f'--', end='\n\n')
def write_ieee_header(
......@@ -52,12 +53,12 @@ def write_ieee_header(
numeric_std : bool, default: True
Include the numeric_std header.
"""
f.write('library ieee;\n')
vhdl.write(f, 0, 'library ieee;')
if std_logic_1164:
f.write('use ieee.std_logic_1164.all;\n')
vhdl.write(f, 0, 'use ieee.std_logic_1164.all;')
if numeric_std:
f.write('use ieee.numeric_std.all;\n')
f.write('\n')
vhdl.write(f, 0, 'use ieee.numeric_std.all;')
vhdl.write(f, 0, '')
def write_signal_decl(
......@@ -95,21 +96,19 @@ def write_signal_decl(
"""
# Spacing of VHDL signals declaration always with a single tab
name_pad = 0 if name_pad is None else name_pad
f.write(f'{VHDL_TAB}signal {name:<{name_pad}} : {type}')
vhdl.write(f, 1, f'signal {name:<{name_pad}} : {type}', end='')
if default_value is not None:
f.write(f' := {default_value}')
f.write(f';\n')
vhdl.write(f, 0, f' := {default_value}', end='')
vhdl.write(f, 0, ';')
if vivado_ram_style:
f.write(f'{VHDL_TAB}attribute ram_style : string;\n')
f.write(
f'{VHDL_TAB}attribute ram_style of {name} : signal is'
f' "{vivado_ram_style}";\n'
vhdl.write(f, 1, f'attribute ram_style : string;')
vhdl.write(
f, 1, f'attribute ram_style of {name} : signal is "{vivado_ram_style}";'
)
if quartus_ram_style:
f.write(f'{VHDL_TAB}attribute ramstyle : string;\n')
f.write(
f'{VHDL_TAB}attribute ramstyle of {name} : signal is'
f' "{quartus_ram_style}";\n'
vhdl.write(f, 1, f'attribute ramstyle : string;')
vhdl.write(
f, 1, f'attribute ramstyle of {name} : signal is "{quartus_ram_style}";'
)
......@@ -138,7 +137,7 @@ def write_constant_decl(
An optional left padding value applied to the name.
"""
name_pad = 0 if name_pad is None else name_pad
f.write(f'{VHDL_TAB}constant {name:<{name_pad}} : {type} := {str(value)};\n')
vhdl.write(f, 1, f'constant {name:<{name_pad}} : {type} := {str(value)};')
def write_type_decl(
......@@ -158,7 +157,7 @@ def write_type_decl(
alias : str
The type to tie the new name to.
"""
f.write(f'{VHDL_TAB}type {name} is {alias};\n')
vhdl.write(f, 1, f'type {name} is {alias};')
def write_process_prologue(
......
......@@ -171,7 +171,9 @@ class _ForwardBackwardEntry:
regs : List[Optional[Process]], optional
regs
back_edge_to : dict, optional
back_edge_from : List[Optional[Process]], optional
Dictionary containing back edges of this entry to registers in the next entry.
back_edge_from : dict, optional
Dictionary containing the back edge of the previous entry to registers in this entry.
outputs_from : int, optional
"""
self.inputs: List[Process] = [] if inputs is None else inputs
......@@ -304,6 +306,7 @@ class _ForwardBackwardTable:
if nreg is None:
next_entry.regs[nreg_idx] = reg
entry.back_edge_to[cols - 1] = nreg_idx
next_entry.back_edge_from[nreg_idx] = cols - 1
return
# All passes failed, raise exception...
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment