137 lines
4.7 KiB
Python
137 lines
4.7 KiB
Python
|
# Copyright lowRISC contributors.
|
||
|
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||
|
# SPDX-License-Identifier: Apache-2.0
|
||
|
"""Generate SystemVerilog designs from IpBlock object"""
|
||
|
|
||
|
import logging as log
|
||
|
import os
|
||
|
from typing import Dict, Optional, Tuple
|
||
|
|
||
|
from mako import exceptions # type: ignore
|
||
|
from mako.template import Template # type: ignore
|
||
|
from pkg_resources import resource_filename
|
||
|
|
||
|
from .ip_block import IpBlock
|
||
|
from .multi_register import MultiRegister
|
||
|
from .reg_base import RegBase
|
||
|
from .register import Register
|
||
|
|
||
|
|
||
|
def escape_name(name: str) -> str:
|
||
|
return name.lower().replace(' ', '_')
|
||
|
|
||
|
|
||
|
def make_box_quote(msg: str, indent: str = ' ') -> str:
|
||
|
hr = indent + ('/' * (len(msg) + 6))
|
||
|
middle = indent + '// ' + msg + ' //'
|
||
|
return '\n'.join([hr, middle, hr])
|
||
|
|
||
|
|
||
|
def _get_awparam_name(iface_name: Optional[str]) -> str:
|
||
|
return (iface_name or 'Iface').capitalize() + 'Aw'
|
||
|
|
||
|
|
||
|
def get_addr_widths(block: IpBlock) -> Dict[Optional[str], Tuple[str, int]]:
|
||
|
'''Return the address widths for the device interfaces
|
||
|
|
||
|
Returns a dictionary keyed by interface name whose values are pairs:
|
||
|
(paramname, width) where paramname is IfaceAw for an unnamed interface and
|
||
|
FooAw for an interface called foo. This is constructed in the same order as
|
||
|
block.reg_blocks.
|
||
|
|
||
|
If there is a single device interface and that interface is unnamed, use
|
||
|
the more general parameter name "BlockAw".
|
||
|
|
||
|
'''
|
||
|
assert block.reg_blocks
|
||
|
if len(block.reg_blocks) == 1 and None in block.reg_blocks:
|
||
|
return {None: ('BlockAw', block.reg_blocks[None].get_addr_width())}
|
||
|
|
||
|
return {name: (_get_awparam_name(name), rb.get_addr_width())
|
||
|
for name, rb in block.reg_blocks.items()}
|
||
|
|
||
|
|
||
|
def get_type_name_pfx(block: IpBlock, iface_name: Optional[str]) -> str:
|
||
|
return block.name.lower() + ('' if iface_name is None
|
||
|
else '_{}'.format(iface_name.lower()))
|
||
|
|
||
|
|
||
|
def get_r0(reg: RegBase) -> Register:
|
||
|
'''Get a Register representing an entry in the RegBase'''
|
||
|
if isinstance(reg, Register):
|
||
|
return reg
|
||
|
else:
|
||
|
assert isinstance(reg, MultiRegister)
|
||
|
return reg.reg
|
||
|
|
||
|
|
||
|
def get_iface_tx_type(block: IpBlock,
|
||
|
iface_name: Optional[str],
|
||
|
hw2reg: bool) -> str:
|
||
|
x2x = 'hw2reg' if hw2reg else 'reg2hw'
|
||
|
pfx = get_type_name_pfx(block, iface_name)
|
||
|
return '_'.join([pfx, x2x, 't'])
|
||
|
|
||
|
|
||
|
def get_reg_tx_type(block: IpBlock, reg: RegBase, hw2reg: bool) -> str:
|
||
|
'''Get the name of the hw2reg or reg2hw type for reg'''
|
||
|
if isinstance(reg, Register):
|
||
|
r0 = reg
|
||
|
type_suff = 'reg_t'
|
||
|
else:
|
||
|
assert isinstance(reg, MultiRegister)
|
||
|
r0 = reg.reg
|
||
|
type_suff = 'mreg_t'
|
||
|
|
||
|
x2x = 'hw2reg' if hw2reg else 'reg2hw'
|
||
|
return '_'.join([block.name.lower(),
|
||
|
x2x,
|
||
|
r0.name.lower(),
|
||
|
type_suff])
|
||
|
|
||
|
|
||
|
def gen_rtl(block: IpBlock, outdir: str) -> int:
|
||
|
# Read Register templates
|
||
|
reg_top_tpl = Template(
|
||
|
filename=resource_filename('reggen', 'reg_top.sv.tpl'))
|
||
|
reg_pkg_tpl = Template(
|
||
|
filename=resource_filename('reggen', 'reg_pkg.sv.tpl'))
|
||
|
|
||
|
# Generate <block>_reg_pkg.sv
|
||
|
#
|
||
|
# This defines the various types used to interface between the *_reg_top
|
||
|
# module(s) and the block itself.
|
||
|
reg_pkg_path = os.path.join(outdir, block.name.lower() + "_reg_pkg.sv")
|
||
|
with open(reg_pkg_path, 'w', encoding='UTF-8') as fout:
|
||
|
try:
|
||
|
fout.write(reg_pkg_tpl.render(block=block))
|
||
|
except: # noqa F722 for template Exception handling
|
||
|
log.error(exceptions.text_error_template().render())
|
||
|
return 1
|
||
|
|
||
|
# Generate the register block implementation(s). For a device interface
|
||
|
# with no name we generate the register module "<block>_reg_top" (writing
|
||
|
# to <block>_reg_top.sv). In any other case, we also need the interface
|
||
|
# name, giving <block>_<ifname>_reg_top.
|
||
|
lblock = block.name.lower()
|
||
|
for if_name, rb in block.reg_blocks.items():
|
||
|
if if_name is None:
|
||
|
mod_base = lblock
|
||
|
else:
|
||
|
mod_base = lblock + '_' + if_name.lower()
|
||
|
|
||
|
mod_name = mod_base + '_reg_top'
|
||
|
reg_top_path = os.path.join(outdir, mod_name + '.sv')
|
||
|
with open(reg_top_path, 'w', encoding='UTF-8') as fout:
|
||
|
try:
|
||
|
fout.write(reg_top_tpl.render(block=block,
|
||
|
mod_base=mod_base,
|
||
|
mod_name=mod_name,
|
||
|
if_name=if_name,
|
||
|
rb=rb))
|
||
|
except: # noqa F722 for template Exception handling
|
||
|
log.error(exceptions.text_error_template().render())
|
||
|
return 1
|
||
|
|
||
|
return 0
|