From 9db003b56366fbc2ae17fd2404db27bc67a3b0e5 Mon Sep 17 00:00:00 2001 From: Mikael Henriksson <mike.zx@hotmail.com> Date: Fri, 17 Mar 2023 17:06:46 +0100 Subject: [PATCH] codegen: add functions 'write' and 'write_lines', reformat code --- b_asic/codegen/vhdl/__init__.py | 39 +++- b_asic/codegen/vhdl/architecture.py | 297 +++++++++++++++++----------- b_asic/codegen/vhdl/common.py | 132 ++++++++----- b_asic/codegen/vhdl/entity.py | 33 ++-- 4 files changed, 324 insertions(+), 177 deletions(-) diff --git a/b_asic/codegen/vhdl/__init__.py b/b_asic/codegen/vhdl/__init__.py index e6976531..168593c1 100644 --- a/b_asic/codegen/vhdl/__init__.py +++ b/b_asic/codegen/vhdl/__init__.py @@ -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 diff --git a/b_asic/codegen/vhdl/architecture.py b/b_asic/codegen/vhdl/architecture.py index 0ab01d1c..94948285 100644 --- a/b_asic/codegen/vhdl/architecture.py +++ b/b_asic/codegen/vhdl/architecture.py @@ -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') diff --git a/b_asic/codegen/vhdl/common.py b/b_asic/codegen/vhdl/common.py index 6dbcb397..7925b6f3 100644 --- a/b_asic/codegen/vhdl/common.py +++ b/b_asic/codegen/vhdl/common.py @@ -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});') diff --git a/b_asic/codegen/vhdl/entity.py b/b_asic/codegen/vhdl/entity.py index 9da241ce..1e0cba8f 100644 --- a/b_asic/codegen/vhdl/entity.py +++ b/b_asic/codegen/vhdl/entity.py @@ -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') -- GitLab