blob: 14c9b4ba37c077bf09596d5372cc518288ae0554 [file] [log] [blame]
lowRISC Contributors802543a2019-08-31 12:12:56 +01001# Copyright lowRISC contributors.
2# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3# SPDX-License-Identifier: Apache-2.0
Rupert Swarbrick200d8b42021-03-08 12:32:11 +00004"""Generate SystemVerilog designs from IpBlock object"""
lowRISC Contributors802543a2019-08-31 12:12:56 +01005
6import logging as log
Rupert Swarbrick200d8b42021-03-08 12:32:11 +00007import os
8from typing import Dict, Optional, Tuple
lowRISC Contributors802543a2019-08-31 12:12:56 +01009
Rupert Swarbrick200d8b42021-03-08 12:32:11 +000010from mako import exceptions # type: ignore
11from mako.template import Template # type: ignore
lowRISC Contributors802543a2019-08-31 12:12:56 +010012from pkg_resources import resource_filename
13
Rupert Swarbrick269bb3d2021-02-23 15:41:56 +000014from .ip_block import IpBlock
Rupert Swarbrick200d8b42021-03-08 12:32:11 +000015from .multi_register import MultiRegister
16from .reg_base import RegBase
17from .register import Register
lowRISC Contributors802543a2019-08-31 12:12:56 +010018
Eunchan Kim51461cd2019-09-18 14:00:49 -070019
Rupert Swarbrick200d8b42021-03-08 12:32:11 +000020def escape_name(name: str) -> str:
lowRISC Contributors802543a2019-08-31 12:12:56 +010021 return name.lower().replace(' ', '_')
22
23
Rupert Swarbrick200d8b42021-03-08 12:32:11 +000024def make_box_quote(msg: str, indent: str = ' ') -> str:
25 hr = indent + ('/' * (len(msg) + 6))
26 middle = indent + '// ' + msg + ' //'
27 return '\n'.join([hr, middle, hr])
28
29
30def _get_awparam_name(iface_name: Optional[str]) -> str:
31 return (iface_name or 'Iface').capitalize() + 'Aw'
32
33
34def get_addr_widths(block: IpBlock) -> Dict[Optional[str], Tuple[str, int]]:
35 '''Return the address widths for the device interfaces
36
37 Returns a dictionary keyed by interface name whose values are pairs:
38 (paramname, width) where paramname is IfaceAw for an unnamed interface and
39 FooAw for an interface called foo. This is constructed in the same order as
40 block.reg_blocks.
41
42 If there is a single device interface and that interface is unnamed, use
43 the more general parameter name "BlockAw".
44
45 '''
46 assert block.reg_blocks
47 if len(block.reg_blocks) == 1 and None in block.reg_blocks:
48 return {None: ('BlockAw', block.reg_blocks[None].get_addr_width())}
49
50 return {name: (_get_awparam_name(name), rb.get_addr_width())
51 for name, rb in block.reg_blocks.items()}
52
53
54def get_type_name_pfx(block: IpBlock, iface_name: Optional[str]) -> str:
55 return block.name.lower() + ('' if iface_name is None
56 else '_{}'.format(iface_name.lower()))
57
58
59def get_r0(reg: RegBase) -> Register:
60 '''Get a Register representing an entry in the RegBase'''
61 if isinstance(reg, Register):
62 return reg
63 else:
64 assert isinstance(reg, MultiRegister)
65 return reg.reg
66
67
68def get_iface_tx_type(block: IpBlock,
69 iface_name: Optional[str],
70 hw2reg: bool) -> str:
71 x2x = 'hw2reg' if hw2reg else 'reg2hw'
72 pfx = get_type_name_pfx(block, iface_name)
73 return '_'.join([pfx, x2x, 't'])
74
75
76def get_reg_tx_type(block: IpBlock, reg: RegBase, hw2reg: bool) -> str:
77 '''Get the name of the hw2reg or reg2hw type for reg'''
78 if isinstance(reg, Register):
79 r0 = reg
80 type_suff = 'reg_t'
81 else:
82 assert isinstance(reg, MultiRegister)
83 r0 = reg.reg
84 type_suff = 'mreg_t'
85
86 x2x = 'hw2reg' if hw2reg else 'reg2hw'
87 return '_'.join([block.name.lower(),
88 x2x,
89 r0.name.lower(),
90 type_suff])
91
92
93def gen_rtl(block: IpBlock, outdir: str) -> int:
lowRISC Contributors802543a2019-08-31 12:12:56 +010094 # Read Register templates
95 reg_top_tpl = Template(
Michael Schaffnerc7039362019-10-22 16:16:06 -070096 filename=resource_filename('reggen', 'reg_top.sv.tpl'))
lowRISC Contributors802543a2019-08-31 12:12:56 +010097 reg_pkg_tpl = Template(
Michael Schaffnerc7039362019-10-22 16:16:06 -070098 filename=resource_filename('reggen', 'reg_pkg.sv.tpl'))
lowRISC Contributors802543a2019-08-31 12:12:56 +010099
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000100 # Generate <block>_reg_pkg.sv
101 #
102 # This defines the various types used to interface between the *_reg_top
103 # module(s) and the block itself.
104 reg_pkg_path = os.path.join(outdir, block.name.lower() + "_reg_pkg.sv")
105 with open(reg_pkg_path, 'w', encoding='UTF-8') as fout:
Eunchan Kimcb28a172019-10-08 16:35:48 -0700106 try:
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000107 fout.write(reg_pkg_tpl.render(block=block))
Rupert Swarbrick6880e212021-02-10 16:10:00 +0000108 except: # noqa F722 for template Exception handling
Eunchan Kimcb28a172019-10-08 16:35:48 -0700109 log.error(exceptions.text_error_template().render())
Rupert Swarbrick6880e212021-02-10 16:10:00 +0000110 return 1
lowRISC Contributors802543a2019-08-31 12:12:56 +0100111
Rupert Swarbrick200d8b42021-03-08 12:32:11 +0000112 # Generate the register block implementation(s). For a device interface
113 # with no name we generate the register module "<block>_reg_top" (writing
114 # to <block>_reg_top.sv). In any other case, we also need the interface
115 # name, giving <block>_<ifname>_reg_top.
116 lblock = block.name.lower()
117 for if_name, rb in block.reg_blocks.items():
118 if if_name is None:
119 mod_base = lblock
120 else:
121 mod_base = lblock + '_' + if_name.lower()
122
123 mod_name = mod_base + '_reg_top'
124 reg_top_path = os.path.join(outdir, mod_name + '.sv')
125 with open(reg_top_path, 'w', encoding='UTF-8') as fout:
126 try:
127 fout.write(reg_top_tpl.render(block=block,
128 mod_base=mod_base,
129 mod_name=mod_name,
130 if_name=if_name,
131 rb=rb))
132 except: # noqa F722 for template Exception handling
133 log.error(exceptions.text_error_template().render())
134 return 1
Rupert Swarbrick6880e212021-02-10 16:10:00 +0000135
136 return 0