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 @@ ...@@ -2,7 +2,34 @@
Module for basic VHDL code generation. Module for basic VHDL code generation.
""" """
from io import TextIOWrapper
# VHDL code generation tab length # VHDL code generation tab length
VHDL_TAB = r" " 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 from b_asic.codegen.vhdl import architecture, common, entity
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
Module for code generation of VHDL architectures. Module for code generation of VHDL architectures.
""" """
from io import TextIOWrapper 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 import vhdl
from b_asic.codegen.vhdl import VHDL_TAB from b_asic.codegen.vhdl import VHDL_TAB
...@@ -263,8 +263,29 @@ def write_register_based_storage( ...@@ -263,8 +263,29 @@ def write_register_based_storage(
): ):
architecture_name = "rtl" architecture_name = "rtl"
schedule_time = len(forward_backward_table) schedule_time = len(forward_backward_table)
# Number of registers in this design
reg_cnt = len(forward_backward_table[0].regs) 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 # Architecture declerative region begin
# #
...@@ -277,7 +298,7 @@ def write_register_based_storage( ...@@ -277,7 +298,7 @@ def write_register_based_storage(
f, f,
name='schedule_cnt', name='schedule_cnt',
type=f'integer range 0 to {schedule_time}-1', type=f'integer range 0 to {schedule_time}-1',
name_pad=14, name_pad=18,
default_value='0', default_value='0',
) )
...@@ -292,17 +313,25 @@ def write_register_based_storage( ...@@ -292,17 +313,25 @@ def write_register_based_storage(
f, f,
name='shift_reg', name='shift_reg',
type='shift_reg_type', 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 # Output mux selector
f.write(f'\n{VHDL_TAB}-- Output mux select signal\n') 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( vhdl.common.write_signal_decl(
f, f,
name='out_mux_sel', name='out_mux_sel',
type=f'integer range 0 to {len(output_regs)-1}', 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( ...@@ -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 # Shift register multiplexer logic
f.write(f'\n{VHDL_TAB}-- Multiplexers for shift register\n') f.write(f'\n{VHDL_TAB}-- Multiplexers for shift register\n')
vhdl.common.write_synchronous_process_prologue( vhdl.common.write_synchronous_process_prologue(
...@@ -337,13 +391,16 @@ def write_register_based_storage( ...@@ -337,13 +391,16 @@ def write_register_based_storage(
f.write(f'{3*VHDL_TAB}shift_reg(0) <= p_0_in;\n') f.write(f'{3*VHDL_TAB}shift_reg(0) <= p_0_in;\n')
for reg_idx in range(1, reg_cnt): 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}shift_reg({reg_idx}) <= shift_reg({reg_idx-1});\n')
vhdl.write(f, 3, f'case back_edge_mux_sel is')
f.write(f'{3*VHDL_TAB}case schedule_cnt is\n') for edge, mux_sel in back_edge_table.items():
for i, entry in enumerate(forward_backward_table): vhdl.write(f, 4, f'when {mux_sel} =>')
if entry.back_edge_from: vhdl.write(f, 5, f'shift_reg({edge[1]}) <= shift_reg({edge[0]});')
f.write(f'{4*VHDL_TAB} when {schedule_time-1 if (i-1)<0 else (i-1)} =>\n') # f.write(f'{3*VHDL_TAB}case schedule_cnt is\n')
for dst, src in entry.back_edge_from.items(): # for i, entry in enumerate(forward_backward_table):
f.write(f'{5*VHDL_TAB} shift_reg({dst}) <= shift_reg({src});\n') # 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'{4*VHDL_TAB}when others => null;\n')
f.write(f'{3*VHDL_TAB}end case;\n') f.write(f'{3*VHDL_TAB}end case;\n')
...@@ -353,29 +410,37 @@ def write_register_based_storage( ...@@ -353,29 +410,37 @@ def write_register_based_storage(
name='shift_reg_proc', 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 # Output multiplexer logic
f.write(f'\n{VHDL_TAB}-- Output muliplexer\n') 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( vhdl.common.write_synchronous_process_prologue(
f, f,
clk='clk', clk='clk',
name='out_mux_proc', name='out_mux_proc',
) )
f.write(f'{3*VHDL_TAB}-- Default case\n') f.write(f'{3*VHDL_TAB}case out_mux_sel is\n')
f.write(f'{3*VHDL_TAB}p_0_out <= shift_reg({reg_cnt-1});\n') for reg_i, mux_i in output_mux_table.items():
f.write(f'{3*VHDL_TAB}case schedule_cnt is\n') f.write(f'{4*VHDL_TAB}when {mux_i} =>\n')
for i, entry in enumerate(forward_backward_table): if reg_i < 0:
if entry.outputs_from is not None: f.write(f'{5*VHDL_TAB}p_0_out <= p_{-1-reg_i}_in;\n')
if entry.outputs_from != reg_cnt - 1: else:
f.write(f'{4*VHDL_TAB} when {i} =>\n') f.write(f'{5*VHDL_TAB}p_0_out <= shift_reg({reg_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}end case;\n') f.write(f'{3*VHDL_TAB}end case;\n')
vhdl.common.write_synchronous_process_epilogue( vhdl.common.write_synchronous_process_epilogue(
f, f,
......
...@@ -7,6 +7,7 @@ from io import TextIOWrapper ...@@ -7,6 +7,7 @@ from io import TextIOWrapper
from subprocess import PIPE, Popen from subprocess import PIPE, Popen
from typing import Any, Optional, Set, Tuple from typing import Any, Optional, Set, Tuple
from b_asic.codegen import vhdl
from b_asic.codegen.vhdl import VHDL_TAB from b_asic.codegen.vhdl import VHDL_TAB
...@@ -26,13 +27,13 @@ def write_b_asic_vhdl_preamble(f: TextIOWrapper): ...@@ -26,13 +27,13 @@ def write_b_asic_vhdl_preamble(f: TextIOWrapper):
git_commit_id = process.communicate()[0].decode('utf-8').strip() git_commit_id = process.communicate()[0].decode('utf-8').strip()
except: except:
pass pass
f.write(f'--\n') vhdl.write(f, 0, f'--')
f.write(f'-- This code was automatically generated by the B-ASIC toolbox.\n') vhdl.write(f, 0, f'-- This code was automatically generated by the B-ASIC toolbox.')
f.write(f'-- Code generation timestamp: ({datetime.now()})\n') vhdl.write(f, 0, f'-- Code generation timestamp: ({datetime.now()})')
if git_commit_id: if git_commit_id:
f.write(f'-- B-ASIC short commit hash: {git_commit_id}\n') vhdl.write(f, 0, f'-- B-ASIC short commit hash: {git_commit_id}')
f.write(f'-- URL: https://gitlab.liu.se/da/B-ASIC\n') vhdl.write(f, 0, f'-- URL: https://gitlab.liu.se/da/B-ASIC')
f.write(f'--\n\n') vhdl.write(f, 0, f'--', end='\n\n')
def write_ieee_header( def write_ieee_header(
...@@ -52,12 +53,12 @@ def write_ieee_header( ...@@ -52,12 +53,12 @@ def write_ieee_header(
numeric_std : bool, default: True numeric_std : bool, default: True
Include the numeric_std header. Include the numeric_std header.
""" """
f.write('library ieee;\n') vhdl.write(f, 0, 'library ieee;')
if std_logic_1164: 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: if numeric_std:
f.write('use ieee.numeric_std.all;\n') vhdl.write(f, 0, 'use ieee.numeric_std.all;')
f.write('\n') vhdl.write(f, 0, '')
def write_signal_decl( def write_signal_decl(
...@@ -95,21 +96,19 @@ def write_signal_decl( ...@@ -95,21 +96,19 @@ def write_signal_decl(
""" """
# Spacing of VHDL signals declaration always with a single tab # Spacing of VHDL signals declaration always with a single tab
name_pad = 0 if name_pad is None else name_pad 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: if default_value is not None:
f.write(f' := {default_value}') vhdl.write(f, 0, f' := {default_value}', end='')
f.write(f';\n') vhdl.write(f, 0, ';')
if vivado_ram_style: if vivado_ram_style:
f.write(f'{VHDL_TAB}attribute ram_style : string;\n') vhdl.write(f, 1, f'attribute ram_style : string;')
f.write( vhdl.write(
f'{VHDL_TAB}attribute ram_style of {name} : signal is' f, 1, f'attribute ram_style of {name} : signal is "{vivado_ram_style}";'
f' "{vivado_ram_style}";\n'
) )
if quartus_ram_style: if quartus_ram_style:
f.write(f'{VHDL_TAB}attribute ramstyle : string;\n') vhdl.write(f, 1, f'attribute ramstyle : string;')
f.write( vhdl.write(
f'{VHDL_TAB}attribute ramstyle of {name} : signal is' f, 1, f'attribute ramstyle of {name} : signal is "{quartus_ram_style}";'
f' "{quartus_ram_style}";\n'
) )
...@@ -138,7 +137,7 @@ def write_constant_decl( ...@@ -138,7 +137,7 @@ def write_constant_decl(
An optional left padding value applied to the name. An optional left padding value applied to the name.
""" """
name_pad = 0 if name_pad is None else name_pad 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( def write_type_decl(
...@@ -158,7 +157,7 @@ def write_type_decl( ...@@ -158,7 +157,7 @@ def write_type_decl(
alias : str alias : str
The type to tie the new name to. 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( def write_process_prologue(
......
...@@ -171,7 +171,9 @@ class _ForwardBackwardEntry: ...@@ -171,7 +171,9 @@ class _ForwardBackwardEntry:
regs : List[Optional[Process]], optional regs : List[Optional[Process]], optional
regs regs
back_edge_to : dict, optional 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 outputs_from : int, optional
""" """
self.inputs: List[Process] = [] if inputs is None else inputs self.inputs: List[Process] = [] if inputs is None else inputs
...@@ -304,6 +306,7 @@ class _ForwardBackwardTable: ...@@ -304,6 +306,7 @@ class _ForwardBackwardTable:
if nreg is None: if nreg is None:
next_entry.regs[nreg_idx] = reg next_entry.regs[nreg_idx] = reg
entry.back_edge_to[cols - 1] = nreg_idx entry.back_edge_to[cols - 1] = nreg_idx
next_entry.back_edge_from[nreg_idx] = cols - 1
return return
# All passes failed, raise exception... # 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