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

codegen: add functions 'write' and 'write_lines', reformat code

parent 3292e158
No related branches found
No related tags found
1 merge request!250Changes to code generation as a result of FPL-2023
Pipeline #91739 passed
......@@ -3,6 +3,7 @@ Module for basic VHDL code generation.
"""
from io import TextIOWrapper
from typing import List, Optional, Tuple, Union
# VHDL code generation tab length
VHDL_TAB = r" "
......@@ -12,7 +13,9 @@ def write(
f: TextIOWrapper,
indent_level: int,
text: str,
*,
end: str = '\n',
start: Optional[str] = None,
):
"""
Base VHDL code generation utility. `f'{VHDL_TAB*indent_level}'` is first written to the :class:`io.TextIOWrapper`
......@@ -21,15 +24,45 @@ def write(
Parameters
----------
f : :class:`io.TextIOWrapper`
The file object to emit the VHDL code to.
The file object to emit VHDL code to.
indent_level : int
Indentation level to use. Exactly `f'{VHDL_TAB*indent_level}` is written before the text is written.
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`.
Text to write exactly after *text* is written to *f*.
start : str, optional
Text to write before both indentation and *text*.
"""
if start is not None:
f.write(start)
f.write(f'{VHDL_TAB*indent_level}{text}{end}')
def write_lines(
f: TextIOWrapper, lines: List[Union[Tuple[int, str], Tuple[int, str, str]]]
):
"""
Multiline VHDL code generation utility. Each tuple (int, str, [int]) in the list `lines` is written to the
:class:`io.TextIOWrapper` object `f` using the :function:`vhdl.write` function.
Parameters
----------
f : :class:`io.TextIOWrapper`
The file object to emit VHDL code to.
lines : list of tuple (int,str) [1], or list of tuple (int,str,str) [2]
[1]: The first `int` of the tuple is used as indentation level for the line and
the second `str` of the tuple is the content of the line.
[2]: Same as [1], but the third `str` of the tuple is passed to parameter `end` when calling
:function:`vhdl.write`.
"""
for tpl in lines:
if len(tpl) == 2:
write(f, indent_level=tpl[0], text=tpl[1])
elif len(tpl) == 3:
write(f, indent_level=tpl[0], text=tpl[1], end=tpl[2])
else:
raise ValueError('All tuples in list `lines` must have length 2 or 3')
from b_asic.codegen.vhdl import architecture, common, entity
......@@ -53,12 +53,14 @@ def write_memory_based_storage(
schedule_time = next(iter(assignment))._schedule_time
# Write architecture header
f.write(f'architecture {architecture_name} of {entity_name} is\n\n')
vhdl.write(
f, 0, f'architecture {architecture_name} of {entity_name} is', end='\n\n'
)
#
# Architecture declerative region begin
#
f.write(f'{VHDL_TAB}-- HDL memory description\n')
vhdl.write(f, 1, '-- HDL memory description')
vhdl.common.write_constant_decl(
f, name='MEM_WL', type='integer', value=word_length, name_pad=12
)
......@@ -89,7 +91,7 @@ def write_memory_based_storage(
vhdl.common.write_signal_decl(f, f'write_en_{i}', 'std_logic', name_pad=14)
# Schedule time counter
f.write(f'\n{VHDL_TAB}-- Schedule counter\n')
vhdl.write(f, 1, f'-- Schedule counter', start='\n')
vhdl.common.write_signal_decl(
f,
name='schedule_cnt',
......@@ -99,7 +101,7 @@ def write_memory_based_storage(
# Input sync signals
if input_sync:
f.write(f'\n{VHDL_TAB}-- Input synchronization\n')
vhdl.write(f, 1, f'-- 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
......@@ -108,36 +110,44 @@ def write_memory_based_storage(
#
# Architecture body begin
#
f.write(f'\nbegin\n\n')
f.write(f'{VHDL_TAB}-- Schedule counter\n')
vhdl.common.write_synchronous_process(
vhdl.write(f, 0, f'begin', start='\n', end='\n\n')
vhdl.write(f, 1, f'-- Schedule counter')
vhdl.common.write_synchronous_process_prologue(
f=f,
name='schedule_cnt_proc',
clk='clk',
)
vhdl.write_lines(
f,
[
(3, f'if rst = \'1\' then'),
(4, f'schedule_cnt <= 0;'),
(3, f'else'),
(4, f'if en = \'1\' then'),
(5, f'if schedule_cnt = {schedule_time-1} then'),
(6, f'schedule_cnt <= 0;'),
(5, f'else'),
(6, f'schedule_cnt <= schedule_cnt + 1;'),
(5, f'end if;'),
(4, f'end if;'),
(3, f'end if;'),
],
)
vhdl.common.write_synchronous_process_epilogue(
f=f,
name='schedule_cnt_proc',
clk='clk',
body=(
f'{0*VHDL_TAB}if rst = \'1\' then\n'
f'{1*VHDL_TAB}schedule_cnt <= 0;\n'
f'{0*VHDL_TAB}else\n'
f'{1*VHDL_TAB}if en = \'1\' then\n'
f'{2*VHDL_TAB}if schedule_cnt = {schedule_time-1} then\n'
f'{3*VHDL_TAB}schedule_cnt <= 0;\n'
f'{2*VHDL_TAB}else\n'
f'{3*VHDL_TAB}schedule_cnt <= schedule_cnt + 1;\n'
f'{2*VHDL_TAB}end if;\n'
f'{1*VHDL_TAB}end if;\n'
f'{0*VHDL_TAB}end if;\n'
),
)
if input_sync:
f.write(f'\n{VHDL_TAB}-- Input synchronization\n')
vhdl.write(f, 1, f'-- Input synchronization', start='\n')
vhdl.common.write_synchronous_process_prologue(
f=f,
name='input_sync_proc',
clk='clk',
)
for i in range(read_ports):
f.write(f'{3*VHDL_TAB}p_{i}_in_sync <= p_{i}_in;\n')
vhdl.write(f, 3, f'p_{i}_in_sync <= p_{i}_in;')
vhdl.common.write_synchronous_process_epilogue(
f=f,
name='input_sync_proc',
......@@ -145,7 +155,7 @@ def write_memory_based_storage(
)
# Infer memory
f.write(f'\n{VHDL_TAB}-- Memory\n')
vhdl.write(f, 1, f'-- Memory', start='\n')
vhdl.common.write_asynchronous_read_memory(
f=f,
clk='clk',
......@@ -161,7 +171,7 @@ def write_memory_based_storage(
)
# Write address generation
f.write(f'\n{VHDL_TAB}-- Memory write address generation\n')
vhdl.write(f, 1, f'-- Memory write address generation', start='\n')
if input_sync:
vhdl.common.write_synchronous_process_prologue(
f, clk="clk", name="mem_write_address_proc"
......@@ -170,19 +180,29 @@ def write_memory_based_storage(
vhdl.common.write_process_prologue(
f, sensitivity_list="schedule_cnt", name="mem_write_address_proc"
)
f.write(f'{3*VHDL_TAB}case schedule_cnt is\n')
vhdl.write(f, 3, f'case schedule_cnt is')
for i, collection in enumerate(assignment):
for mv in collection:
mv = cast(MemoryVariable, mv)
if mv.execution_time:
f.write(f'{4*VHDL_TAB}-- {mv!r}\n')
f.write(f'{4*VHDL_TAB}when {(mv.start_time) % schedule_time} =>\n')
f.write(f'{5*VHDL_TAB}write_adr_0 <= {i};\n')
f.write(f'{5*VHDL_TAB}write_en_0 <= \'1\';\n')
f.write(f'{4*VHDL_TAB}when others =>\n')
f.write(f'{5*VHDL_TAB}write_adr_0 <= 0;\n')
f.write(f'{5*VHDL_TAB}write_en_0 <= \'0\';\n')
f.write(f'{3*VHDL_TAB}end case;\n')
vhdl.write_lines(
f,
[
(4, f'-- {mv!r}'),
(4, f'when {(mv.start_time) % schedule_time} =>'),
(5, f'write_adr_0 <= {i};'),
(5, f'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"
......@@ -193,35 +213,47 @@ def write_memory_based_storage(
)
# Read address generation
f.write(f'\n{VHDL_TAB}-- Memory read address generation\n')
vhdl.write(f, 1, f'-- Memory read address generation', start='\n')
vhdl.common.write_synchronous_process_prologue(
f, clk="clk", name="mem_read_address_proc"
)
f.write(f'{3*VHDL_TAB}case schedule_cnt is\n')
vhdl.write(f, 3, f'case schedule_cnt is')
for i, collection in enumerate(assignment):
for mv in collection:
mv = cast(PlainMemoryVariable, mv)
f.write(f'{4*VHDL_TAB}-- {mv!r}\n')
vhdl.write(f, 4, f'-- {mv!r}')
for read_time in mv.reads.values():
f.write(
f'{4*VHDL_TAB}when'
f' {(mv.start_time+read_time-int(not(input_sync))) % schedule_time} =>\n'
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};'),
(5, f'read_en_0 <= \'1\';'),
],
)
f.write(f'{5*VHDL_TAB}read_adr_0 <= {i};\n')
f.write(f'{5*VHDL_TAB}read_en_0 <= \'1\';\n')
f.write(f'{4*VHDL_TAB}when others =>\n')
f.write(f'{5*VHDL_TAB}read_adr_0 <= 0;\n')
f.write(f'{5*VHDL_TAB}read_en_0 <= \'0\';\n')
f.write(f'{3*VHDL_TAB}end case;\n')
vhdl.write_lines(
f,
[
(4, f'when others =>'),
(5, f'read_adr_0 <= 0;'),
(5, f'read_en_0 <= \'0\';'),
(3, f'end case;'),
],
)
vhdl.common.write_synchronous_process_epilogue(
f, clk="clk", name="mem_read_address_proc"
)
f.write(f'\n{1*VHDL_TAB}-- Input and output assignment\n')
vhdl.write(f, 1, f'-- Input and output assignmentn', start='\n')
if input_sync:
f.write(f'{1*VHDL_TAB}write_port_0 <= p_0_in_sync;\n')
vhdl.write(f, 1, f'write_port_0 <= p_0_in_sync;')
else:
f.write(f'{1*VHDL_TAB}write_port_0 <= p_0_in;\n')
vhdl.write(f, 1, f'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)
)
......@@ -230,26 +262,27 @@ def write_memory_based_storage(
clk='clk',
name='output_reg_proc',
)
f.write(f'{3*VHDL_TAB}case schedule_cnt is\n')
vhdl.write(f, 3, f'case schedule_cnt is')
for p in p_zero_exec:
if input_sync:
f.write(
f'{4*VHDL_TAB}when {(p.start_time+1)%schedule_time} => p_0_out <='
' p_0_in_sync;\n'
)
write_time = (p.start_time + 1) % schedule_time
vhdl.write(f, 4, f'when {write_time} => p_0_out <= p_0_in_sync;')
else:
f.write(
f'{4*VHDL_TAB}when {(p.start_time)%schedule_time} => p_0_out <='
' p_0_in;\n'
)
f.write(f'{4*VHDL_TAB}when others => p_0_out <= read_port_0;\n')
f.write(f'{3*VHDL_TAB}end case;\n')
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,
[
(4, f'when others => p_0_out <= read_port_0;'),
(3, f'end case;'),
],
)
vhdl.common.write_synchronous_process_epilogue(
f,
clk='clk',
name='output_reg_proc',
)
f.write(f'\nend architecture {architecture_name};')
vhdl.write(f, 0, f'end architecture {architecture_name};', start='\n')
def write_register_based_storage(
......@@ -260,6 +293,8 @@ def write_register_based_storage(
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)
......@@ -290,10 +325,12 @@ def write_register_based_storage(
# Architecture declerative region begin
#
# Write architecture header
f.write(f'architecture {architecture_name} of {entity_name} is\n\n')
vhdl.write(
f, 0, f'architecture {architecture_name} of {entity_name} is', end='\n\n'
)
# Schedule time counter
f.write(f'{VHDL_TAB}-- Schedule counter\n')
vhdl.write(f, 1, f'-- Schedule counter')
vhdl.common.write_signal_decl(
f,
name='schedule_cnt',
......@@ -303,7 +340,7 @@ def write_register_based_storage(
)
# Shift register
f.write(f'\n{VHDL_TAB}-- Shift register\n')
vhdl.write(f, 1, f'-- Shift register', start='\n')
vhdl.common.write_type_decl(
f,
name='shift_reg_type',
......@@ -317,7 +354,7 @@ def write_register_based_storage(
)
# Back edge mux decoder
f.write(f'\n{VHDL_TAB}-- Back-edge mux select signal\n')
vhdl.write(f, 1, f'-- Back-edge mux select signal', start='\n')
vhdl.common.write_signal_decl(
f,
name='back_edge_mux_sel',
......@@ -326,7 +363,7 @@ def write_register_based_storage(
)
# Output mux selector
f.write(f'\n{VHDL_TAB}-- Output mux select signal\n')
vhdl.write(f, 1, f'-- Output mux select signal', start='\n')
vhdl.common.write_signal_decl(
f,
name='out_mux_sel',
......@@ -337,26 +374,33 @@ def write_register_based_storage(
#
# Architecture body begin
#
f.write(f'begin\n\n')
f.write(f'{VHDL_TAB}-- Schedule counter\n')
vhdl.common.write_synchronous_process(
vhdl.write(f, 0, f'begin', start='\n', end='\n\n')
vhdl.write(f, 1, f'-- Schedule counter')
vhdl.common.write_synchronous_process_prologue(
f=f,
name='schedule_cnt_proc',
clk='clk',
)
vhdl.write_lines(
f,
[
(4, f'if en = \'1\' then'),
(5, f'if schedule_cnt = {schedule_time}-1 then'),
(6, f'schedule_cnt <= 0;'),
(5, f'else'),
(6, f'schedule_cnt <= schedule_cnt + 1;'),
(5, f'end if;'),
(4, f'end if;'),
],
)
vhdl.common.write_synchronous_process_epilogue(
f=f,
name='schedule_cnt_proc',
clk='clk',
body=(
f'{0*VHDL_TAB}if en = \'1\' then\n'
f'{1*VHDL_TAB}if schedule_cnt = {schedule_time}-1 then\n'
f'{2*VHDL_TAB}schedule_cnt <= 0;\n'
f'{1*VHDL_TAB}else\n'
f'{2*VHDL_TAB}schedule_cnt <= schedule_cnt + 1;\n'
f'{1*VHDL_TAB}end if;\n'
f'{0*VHDL_TAB}end if;\n'
),
)
# Shift register back-edge decoding
f.write(f'\n{VHDL_TAB}-- Shift register back-edge decoding\n')
vhdl.write(f, 1, f'-- Shift register back-edge decoding', start='\n')
vhdl.common.write_synchronous_process_prologue(
f,
clk='clk',
......@@ -368,12 +412,22 @@ def write_register_based_storage(
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.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,
[
(4, f'when others =>'),
(5, f'back_edge_mux_sel <= 0;'),
(3, f'end case;'),
],
)
vhdl.common.write_synchronous_process_epilogue(
f,
clk='clk',
......@@ -381,28 +435,46 @@ def write_register_based_storage(
)
# Shift register multiplexer logic
f.write(f'\n{VHDL_TAB}-- Multiplexers for shift register\n')
vhdl.write(f, 1, f'-- Multiplexers for shift register', start='\n')
vhdl.common.write_synchronous_process_prologue(
f,
clk='clk',
name='shift_reg_proc',
)
f.write(f'{3*VHDL_TAB}-- Default case\n')
f.write(f'{3*VHDL_TAB}shift_reg(0) <= p_0_in;\n')
if sync_rst:
vhdl.write(f, 3, f'if rst = \'1\' then')
for reg_idx in range(reg_cnt):
vhdl.write(f, 4, f'shift_reg({reg_idx}) <= (others => \'0\');')
vhdl.write(f, 3, f'else')
vhdl.write_lines(
f,
[
(3, f'-- Default case'),
(3, f'shift_reg(0) <= p_0_in;'),
],
)
for reg_idx in range(1, reg_cnt):
f.write(f'{3*VHDL_TAB}shift_reg({reg_idx}) <= shift_reg({reg_idx-1});\n')
vhdl.write(f, 3, f'shift_reg({reg_idx}) <= shift_reg({reg_idx-1});')
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')
vhdl.write_lines(
f,
[
(4, f'when {mux_sel} =>'),
(5, f'shift_reg({edge[1]}) <= shift_reg({edge[0]});'),
],
)
vhdl.write_lines(
f,
[
(4, f'when others => null;'),
(3, f'end case;'),
],
)
if sync_rst:
vhdl.write(f, 3, f'end if;')
vhdl.common.write_synchronous_process_epilogue(
f,
......@@ -411,41 +483,40 @@ def write_register_based_storage(
)
# Output multiplexer decoding logic
f.write(f'\n{VHDL_TAB}-- Output muliplexer decoding logic\n')
vhdl.write(f, 1, f'-- Output muliplexer decoding logic', start='\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')
vhdl.write(f, 3, f'case schedule_cnt is')
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')
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};')
vhdl.write(f, 3, f'end case;')
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')
vhdl.write(f, 1, f'-- Output muliplexer', start='\n')
vhdl.common.write_synchronous_process_prologue(
f,
clk='clk',
name='out_mux_proc',
)
f.write(f'{3*VHDL_TAB}case out_mux_sel is\n')
vhdl.write(f, 3, f'case out_mux_sel is')
for reg_i, mux_i in output_mux_table.items():
f.write(f'{4*VHDL_TAB}when {mux_i} =>\n')
vhdl.write(f, 4, f'when {mux_i} =>')
if reg_i < 0:
f.write(f'{5*VHDL_TAB}p_0_out <= p_{-1-reg_i}_in;\n')
vhdl.write(f, 5, f'p_0_out <= p_{-1-reg_i}_in;')
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.write(f, 5, f'p_0_out <= shift_reg({reg_i});')
vhdl.write(f, 3, f'end case;')
vhdl.common.write_synchronous_process_epilogue(
f,
clk='clk',
name='out_mux_proc',
)
f.write(f'end architecture {architecture_name};')
vhdl.write(f, 0, f'end architecture {architecture_name};', start='\n')
......@@ -8,7 +8,6 @@ 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
def write_b_asic_vhdl_preamble(f: TextIOWrapper):
......@@ -27,13 +26,23 @@ def write_b_asic_vhdl_preamble(f: TextIOWrapper):
git_commit_id = process.communicate()[0].decode('utf-8').strip()
except:
pass
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()})')
vhdl.write_lines(
f,
[
(0, f'--'),
(0, f'-- This code was automatically generated by the B-ASIC toolbox.'),
(0, f'-- Code generation timestamp: ({datetime.now()})'),
],
)
if git_commit_id:
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')
vhdl.write_lines(
f,
[
(0, f'-- URL: https://gitlab.liu.se/da/B-ASIC'),
(0, f'--', '\n\n'),
],
)
def write_ieee_header(
......@@ -95,20 +104,26 @@ def write_signal_decl(
If set, exactly one of: "M4K", "M9K", "M10K", "M20K", "M144K", "MLAB" or "logic".
"""
# Spacing of VHDL signals declaration always with a single tab
name_pad = 0 if name_pad is None else name_pad
name_pad = name_pad or 0
vhdl.write(f, 1, f'signal {name:<{name_pad}} : {type}', end='')
if default_value is not None:
vhdl.write(f, 0, f' := {default_value}', end='')
vhdl.write(f, 0, ';')
if vivado_ram_style:
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 vivado_ram_style is not None:
vhdl.write_lines(
f,
[
(1, f'attribute ram_style : string;'),
(1, f'attribute ram_style of {name} : signal is "{vivado_ram_style}";'),
],
)
if quartus_ram_style:
vhdl.write(f, 1, f'attribute ramstyle : string;')
vhdl.write(
f, 1, f'attribute ramstyle of {name} : signal is "{quartus_ram_style}";'
if quartus_ram_style is not None:
vhdl.write_lines(
f,
[
(1, f'attribute ramstyle : string;'),
(1, f'attribute ramstyle of {name} : signal is "{quartus_ram_style}";'),
],
)
......@@ -163,12 +178,12 @@ def write_type_decl(
def write_process_prologue(
f: TextIOWrapper,
sensitivity_list: str,
indent: str = VHDL_TAB,
indent: int = 1,
name: Optional[str] = None,
):
"""
Write only the prologue of a regular VHDL process with a user provided sensitivity list.
This method should almost always guarantely be followed by a write_asynchronous_process_epilogue.
This method should almost always guarantely be followed by a write_process_epilogue.
Parameters
----------
......@@ -176,22 +191,22 @@ def write_process_prologue(
The TextIOWrapper object to write the type declaration to.
sensitivity_list : str
Content of the process sensitivity list.
indent : str, default: 1*VHDL_TAB
Indentation used in the process. This string is applied to the first written line of all output.
indent : int, default: 1
Indentation level to use for this process.
name : Optional[str]
An optional name for the process.
"""
if name is not None:
f.write(f'{indent}{name}: process({sensitivity_list})\n')
vhdl.write(f, indent, f'{name}: process({sensitivity_list})')
else:
f.write(f'{indent}process({sensitivity_list})\n')
f.write(f'{indent}begin\n')
vhdl.write(f, indent, f'process({sensitivity_list})')
vhdl.write(f, indent, f'begin')
def write_process_epilogue(
f: TextIOWrapper,
sensitivity_list: Optional[str] = None,
indent: str = VHDL_TAB,
indent: int = 1,
name: Optional[str] = None,
):
"""
......@@ -201,22 +216,24 @@ def write_process_epilogue(
The TextIOWrapper object to write the type declaration to.
sensitivity_list : str
Content of the process sensitivity list. Not needed when writing the epligoue.
indent : str, default: 1*VHDL_TAB
Indentation used in the process. This string is applied to the first written line of all output.
indent : int, default: 1
Indentation level to use for this process.
indent : int, default: 1
Indentation level to use for this process.
name : Optional[str]
An optional name of the ending process.
"""
_ = sensitivity_list
f.write(f'{indent}end process')
vhdl.write(f, indent, f'end process', end="")
if name is not None:
f.write(' ' + name)
f.write(';\n')
vhdl.write(f, 0, ' ' + name, end="")
vhdl.write(f, 0, ';')
def write_synchronous_process_prologue(
f: TextIOWrapper,
clk: str,
indent: str = VHDL_TAB,
indent: int = 1,
name: Optional[str] = None,
):
"""
......@@ -230,19 +247,19 @@ def write_synchronous_process_prologue(
The TextIOWrapper to write the VHDL code onto.
clk : str
Name of the clock.
indent : str, default: VHDL_TAB
Indentation used in the process. This string is applied to the first written line of all output.
indent : int, default: 1
Indentation level to use for this process.
name : Optional[str]
An optional name for the process.
"""
write_process_prologue(f, sensitivity_list=clk, indent=indent, name=name)
f.write(f'{indent}{VHDL_TAB}if rising_edge(clk) then\n')
vhdl.write(f, indent + 1, f'if rising_edge(clk) then')
def write_synchronous_process_epilogue(
f: TextIOWrapper,
clk: Optional[str],
indent: str = VHDL_TAB,
indent: int = 1,
name: Optional[str] = None,
):
"""
......@@ -256,13 +273,13 @@ def write_synchronous_process_epilogue(
The TextIOWrapper to write the VHDL code onto.
clk : str
Name of the clock.
indent : str, default: VHDL_TAB
Indent this process block with `indent` columns
indent : int, default: 1
Indentation level to use for this process.
name : Optional[str]
An optional name for the process
"""
_ = clk
f.write(f'{indent}{VHDL_TAB}end if;\n')
vhdl.write(f, indent + 1, f'end if;')
write_process_epilogue(f, sensitivity_list=clk, indent=indent, name=name)
......@@ -270,7 +287,7 @@ def write_synchronous_process(
f: TextIOWrapper,
clk: str,
body: str,
indent: str = VHDL_TAB,
indent: int = 1,
name: Optional[str] = None,
):
"""
......@@ -285,15 +302,15 @@ def write_synchronous_process(
Name of the clock.
body : str
Body of the `if rising_edge(clk) then` block.
indent : int, default: VHDL_TAB
Indent this process block with `indent` columns
indent : int, default: 1
Indentation level to use for this process.
name : Optional[str]
An optional name for the process
"""
write_synchronous_process_prologue(f, clk, indent, name)
for line in body.split('\n'):
if len(line):
f.write(f'{indent}{2*VHDL_TAB}{line}\n')
vhdl.write(f, indent + 2, f'{line}')
write_synchronous_process_epilogue(f, clk, indent, name)
......@@ -324,13 +341,23 @@ def write_synchronous_memory(
assert len(write_ports) >= 1
write_synchronous_process_prologue(f, clk=clk, name=name)
for read_name, address, re in read_ports:
f.write(f'{3*VHDL_TAB}if {re} = \'1\' then\n')
f.write(f'{4*VHDL_TAB}{read_name} <= memory({address});\n')
f.write(f'{3*VHDL_TAB}end if;\n')
vhdl.write_lines(
f,
[
(3, f'if {re} = \'1\' then'),
(4, f'{read_name} <= memory({address});'),
(3, f'end if;'),
],
)
for write_name, address, we in write_ports:
f.write(f'{3*VHDL_TAB}if {we} = \'1\' then\n')
f.write(f'{4*VHDL_TAB}memory({address}) <= {write_name};\n')
f.write(f'{3*VHDL_TAB}end if;\n')
vhdl.write_lines(
f,
[
(3, f'if {we} = \'1\' then'),
(4, f'memory({address}) <= {write_name};'),
(3, f'end if;'),
],
)
write_synchronous_process_epilogue(f, clk=clk, name=name)
......@@ -361,9 +388,14 @@ def write_asynchronous_read_memory(
assert len(write_ports) >= 1
write_synchronous_process_prologue(f, clk=clk, name=name)
for write_name, address, we in write_ports:
f.write(f'{3*VHDL_TAB}if {we} = \'1\' then\n')
f.write(f'{4*VHDL_TAB}memory({address}) <= {write_name};\n')
f.write(f'{3*VHDL_TAB}end if;\n')
vhdl.write_lines(
f,
[
(3, f'if {we} = \'1\' then'),
(4, f'memory({address}) <= {write_name};'),
(3, f'end if;'),
],
)
write_synchronous_process_epilogue(f, clk=clk, name=name)
for read_name, address, _ in read_ports:
f.write(f'{1*VHDL_TAB}{read_name} <= memory({address});\n')
vhdl.write(f, 1, f'{read_name} <= memory({address});')
......@@ -4,6 +4,7 @@ Module for code generation of VHDL entity declarations
from io import TextIOWrapper
from typing import Set
from b_asic.codegen import vhdl
from b_asic.codegen.vhdl import VHDL_TAB
from b_asic.port import Port
from b_asic.process import MemoryVariable, PlainMemoryVariable
......@@ -28,19 +29,29 @@ def write_memory_based_storage(
entity_name = entity_name
# Write the entity header
f.write(f'entity {entity_name} is\n')
f.write(f'{VHDL_TAB}generic(\n')
f.write(f'{2*VHDL_TAB}-- Data word length\n')
f.write(f'{2*VHDL_TAB}WL : integer := {word_length}\n')
f.write(f'{VHDL_TAB});\n')
f.write(f'{VHDL_TAB}port(\n')
vhdl.write_lines(
f,
[
(0, f'entity {entity_name} is'),
(1, f'generic('),
(2, f'-- Data word length'),
(2, f'WL : integer := {word_length}'),
(1, f');'),
(1, f'port('),
],
)
# Write the clock and reset signal
f.write(f'{2*VHDL_TAB}-- Clock, synchronous reset and enable signals\n')
f.write(f'{2*VHDL_TAB}clk : in std_logic;\n')
f.write(f'{2*VHDL_TAB}rst : in std_logic;\n')
f.write(f'{2*VHDL_TAB}en : in std_logic;\n')
f.write(f'\n')
vhdl.write_lines(
f,
[
(0, f'-- Clock, synchronous reset and enable signals'),
(2, f'clk : in std_logic;'),
(2, f'rst : in std_logic;'),
(2, f'en : in std_logic;'),
(0, f''),
],
)
# Write the input port specification
f.write(f'{2*VHDL_TAB}-- Memory port I/O\n')
......
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