Newer
Older
"""
Module for code generation of VHDL architectures.
"""
from io import TextIOWrapper

Mikael Henriksson
committed
from typing import Set, cast
from b_asic.codegen import vhdl
from b_asic.codegen.vhdl import VHDL_TAB
from b_asic.process import MemoryVariable, PlainMemoryVariable

Mikael Henriksson
committed
from b_asic.resources import ProcessCollection, _ForwardBackwardTable
def write_memory_based_storage(
f: TextIOWrapper,
assignment: Set[ProcessCollection],
entity_name: str,
word_length: int,
read_ports: int,
write_ports: int,
total_ports: int,

Mikael Henriksson
committed
input_sync: bool = True,
):
"""
Generate the VHDL architecture for a memory based architecture from a process collection of memory variables.
Parameters
----------
assignment : dict
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 : int
Word length of the memory variable objects.
Number of read ports.
write_ports : int
Number of write ports.
total_ports : int
Total concurrent memory accesses possible.

Mikael Henriksson
committed
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
f.write(f'architecture {architecture_name} of {entity_name} is\n\n')
#
# Architecture declerative region begin
#
f.write(f'{VHDL_TAB}-- HDL memory description\n')
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)'
)

Mikael Henriksson
committed
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

Mikael Henriksson
committed
f.write(f'\n{VHDL_TAB}-- Schedule counter\n')
vhdl.common.write_signal_decl(
f,
name='schedule_cnt',
type=f'integer range 0 to {schedule_time}-1',
name_pad=14,
)

Mikael Henriksson
committed
# Input sync signals
if input_sync:
f.write(f'\n{VHDL_TAB}-- Input synchronization\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
)
#
# Architecture body begin
#

Mikael Henriksson
committed
f.write(f'\nbegin\n\n')
f.write(f'{VHDL_TAB}-- Schedule counter\n')
vhdl.common.write_synchronous_process(
f=f,
name='schedule_cnt_proc',
clk='clk',
body=(

Mikael Henriksson
committed
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'
),
)

Mikael Henriksson
committed
if input_sync:
f.write(f'\n{VHDL_TAB}-- Input synchronization\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.common.write_synchronous_process_epilogue(
f=f,
name='input_sync_proc',
clk='clk',
)

Mikael Henriksson
committed
f.write(f'\n{VHDL_TAB}-- Memory\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)
},
)

Mikael Henriksson
committed
# Write address generation
f.write(f'\n{VHDL_TAB}-- Memory write address generation\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"
)
f.write(f'{3*VHDL_TAB}case schedule_cnt is\n')
for i, collection in enumerate(assignment):
for mv in collection:
mv = cast(MemoryVariable, mv)
if mv.execution_time:

Mikael Henriksson
committed
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')
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"
)

Mikael Henriksson
committed
# Read address generation
f.write(f'\n{VHDL_TAB}-- Memory read address generation\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')
for i, collection in enumerate(assignment):
for mv in collection:
mv = cast(PlainMemoryVariable, mv)

Mikael Henriksson
committed
f.write(f'{4*VHDL_TAB}-- {mv!r}\n')
for read_time in mv.reads.values():
f.write(

Mikael Henriksson
committed
f'{4*VHDL_TAB}when'
f' {(mv.start_time+read_time-int(not(input_sync))) % schedule_time} =>\n'

Mikael Henriksson
committed
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.common.write_synchronous_process_epilogue(
f, clk="clk", name="mem_read_address_proc"
)

Mikael Henriksson
committed
f.write(f'\n{1*VHDL_TAB}-- Input and output assignment\n')
if input_sync:
f.write(f'{1*VHDL_TAB}write_port_0 <= p_0_in_sync;\n')
else:
f.write(f'{1*VHDL_TAB}write_port_0 <= p_0_in;\n')
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',
)
f.write(f'{3*VHDL_TAB}case schedule_cnt is\n')
for p in p_zero_exec:

Mikael Henriksson
committed
if input_sync:
f.write(
f'{4*VHDL_TAB}when {(p.start_time+1)%schedule_time} => p_0_out <='
' p_0_in_sync;\n'
)
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')
vhdl.common.write_synchronous_process_epilogue(
f,
clk='clk',
name='output_reg_proc',
)

Mikael Henriksson
committed
f.write(f'\nend architecture {architecture_name};')
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
def write_register_based_storage(
f: TextIOWrapper,
forward_backward_table: _ForwardBackwardTable,
entity_name: str,
word_length: int,
read_ports: int,
write_ports: int,
total_ports: int,
):
architecture_name = "rtl"
schedule_time = len(forward_backward_table)
reg_cnt = len(forward_backward_table[0].regs)
#
# Architecture declerative region begin
#
# Write architecture header
f.write(f'architecture {architecture_name} of {entity_name} is\n\n')
# Schedule time counter
f.write(f'{VHDL_TAB}-- Schedule counter\n')
vhdl.common.write_signal_decl(
f,
name='schedule_cnt',
type=f'integer range 0 to {schedule_time}-1',
name_pad=14,
default_value='0',
)
# Shift register

Mikael Henriksson
committed
f.write(f'\n{VHDL_TAB}-- Shift register\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',
name_pad=14,
)

Mikael Henriksson
committed
# 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,
)
#
# Architecture body begin
#
f.write(f'begin\n\n')
f.write(f'{VHDL_TAB}-- Schedule counter\n')
vhdl.common.write_synchronous_process(
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'
),
)

Mikael Henriksson
committed
# Shift register multiplexer logic
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
f.write(f'\n{VHDL_TAB}-- Multiplexers for shift register\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')
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')
f.write(f'{4*VHDL_TAB}when others => null;\n')
f.write(f'{3*VHDL_TAB}end case;\n')
vhdl.common.write_synchronous_process_epilogue(
f,
clk='clk',
name='shift_reg_proc',
)

Mikael Henriksson
committed
# Output multiplexer logic
f.write(f'\n{VHDL_TAB}-- Output muliplexer\n')

Mikael Henriksson
committed
f.write(f'\n{VHDL_TAB}-- {output_regs}\n')
f.write(f'\n{VHDL_TAB}-- { list(range(len(output_regs))) }\n')
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
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}end case;\n')
vhdl.common.write_synchronous_process_epilogue(
f,
clk='clk',
name='out_mux_proc',
)
f.write(f'end architecture {architecture_name};')