├── README.md ├── LICENSE ├── fundamentals_lib.vh └── fundamentals_lib.tlv /README.md: -------------------------------------------------------------------------------- 1 | # tlv_lib 2 | General-purpose TL-Verilog libraries. 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Steve Hoover 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /fundamentals_lib.vh: -------------------------------------------------------------------------------- 1 | /* 2 | BSD 3-Clause License 3 | 4 | Copyright (c) 2020, Steve Hoover 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | 10 | 1. Redistributions of source code must retain the above copyright notice, this 11 | list of conditions and the following disclaimer. 12 | 13 | 2. Redistributions in binary form must reproduce the above copyright notice, 14 | this list of conditions and the following disclaimer in the documentation 15 | and/or other materials provided with the distribution. 16 | 17 | 3. Neither the name of the copyright holder nor the names of its 18 | contributors may be used to endorse or promote products derived from 19 | this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 22 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 25 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 29 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | */ 32 | 33 | `ifndef FUNDAMENTALS_LIB_VH 34 | // Verilog macros similar to M4 macros in fundamentls_lib.tlv, but as SV macros. 35 | 36 | 37 | 38 | // For verification, logging, and instrumentation. 39 | 40 | `define DISPLAY(args) always_ff @(posedge clk) $display args; 41 | `define DISPLAY_IF(cond, args) always_ff @(posedge clk) if (cond) $display args; 42 | 43 | `define ASSERT(prop) assert property (@(posedge clk) reset || (prop)); 44 | 45 | 46 | `endif // FUNDAMENTALS_LIB_VH -------------------------------------------------------------------------------- /fundamentals_lib.tlv: -------------------------------------------------------------------------------- 1 | \m4_TLV_version 1d: tl-x.org 2 | \SV 3 | /* 4 | Copyright (c) 2018, Steven F. Hoover 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, 10 | this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | * The name of Steven F. Hoover 15 | may not be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | */ 29 | 30 | m4+definitions([' 31 | 32 | // ==================== 33 | // Generic Logic Macros 34 | // ==================== 35 | 36 | // Expand to a concatination. 37 | // Big endian concatinates {[0], [1], ...} 38 | // Little endian concatinates {..., [1], [0]} 39 | // Generally, little endian is unnecessary, as [*] references do the same thing, but we've come across tools that 40 | // can't handle the multiple packed dimensions [*] creates. 41 | // Params: 42 | // $1: Scope. 43 | // $2: The number of indices in the scope [n-1:0]. 44 | // $3: Signal to concatinate inside scope. 45 | m4_define(['m4_hier_concat_big_endian'], 46 | ['{m4_forloop(['m4_ind'], 0, $2, ['m4_ifelse(m4_ind, 0, [''], [', '])$1[m4_ind]$2'])}']) 47 | m4_define(['m4_hier_concat_little_endian'], 48 | ['{m4_forloop(['m4_ind'], 0, $2, ['m4_ifelse(m4_ind, 0, [''], [', '])$1[m4_eval($2 - m4_ind - 1)]$2'])}']) 49 | 50 | 51 | // ################################## 52 | // Single-line verif macros 53 | // (multi-line versions later) 54 | 55 | // ========== 56 | // Assertions 57 | // ========== 58 | 59 | // A shorthand for a concurrent assertion statement (which, of course can infer staging logic). 60 | m4_define(['m4_assert'], ['assert property (@(posedge *clk) *reset || ($1)) else \$error($2);']) 61 | 62 | 63 | // ======== 64 | // Printing 65 | // ======== 66 | // One-line versions of the corresponding multiline macros: 67 | m4_define(['m4_display'], ['always_ff @(posedge clk) \$display($1);']) 68 | m4_define(['m4_display_if'], ['always_ff @(posedge clk) if ($1) \$display($2);']) 69 | 70 | // For \TLV tree_redux: 71 | m4_define(['m4_tree_redux_uniquifier'], 0) 72 | ']) 73 | 74 | 75 | 76 | // This can be used to instantiate an m4_ macro (with any number of argumenets) that produces no new lines 77 | // using a multi-line instantiation. 78 | \TLV instantiate(_macro_name, _etc) 79 | m4_echo(['m4_']['_macro_name'])(m4_shift($@)) 80 | \TLV with(_etc) 81 | m4_push(tmp, m4_push_TLV_set(, $@)) 82 | m4+m4_tmp 83 | m4_pop_set()m4_pop(tmp) 84 | \TLV with_seq(_etc) 85 | m4_push(tmp, m4_push_TLV_set(_seq, $@)) 86 | m4+m4_tmp 87 | m4_pop_set()m4_pop(tmp) 88 | \TLV ifelse(_a, _b, _match_block, _mismatch_block, _etc) 89 | m4_ifelse(['_a'], ['_b'], ['m4+_match_block'], m4_eval($# == 4), 1, ['m4+_mismatch_block'], m4_eval($# > 4), 1, ['m4_push(_ifelse_args, ['m4_shift(m4_shift(m4_shift($@)))'])m4+ifelse(m4__ifelse_args)m4_pop(_ifelse_args)'], ['']) 90 | 91 | 92 | // forloop(_var, #from, #to, _block) 93 | // Repeat a block of TLV code. 94 | // _var: Loop variable (m4 macro, prepended with "m4_") 95 | // #from: Initial (low) value of ['m4_']_var. 96 | // #to: Non-inclusive high value of ['m4_']_var. 97 | // _block: The TLV code block to repeat. 98 | \TLV forloop(_var, #_from, #_to, _block) 99 | m4_push(['_var'], ['#_from'])// forloop(_var, #_from, #_to, ) 100 | m4+forloop_inner(['_var'], ['#_from'], ['#_to'], ['_block']) 101 | m4_pop(['_var']) 102 | \TLV forloop_inner(_var, #_from, #_to, _block) 103 | // forloop_inner(_var, #_from, #_to, ) 104 | m4+ifelse(m4_eval(#_from < #_to), 0, [''], 105 | \TLV 106 | m4+_block 107 | m4_push(['_var'], m4_eval(m4_echo(['m4_']_var) + 1)) 108 | m4+forloop_inner(['_var'], m4_eval(#_from + 1), ['#_to'], ['_block']) 109 | m4_pop(['_var']) 110 | ) 111 | 112 | // Apply an operation across a hierarchy in a tree. 113 | // /_hier[*]@_stage$_sig is reduced by applying _op in a tree, to produce @_stage$_sig. 114 | // The tree of redux logic can be distributed over pipeline stages. 115 | // Each level of reduction is provided its own hierarchy level instance to replicate the reduction, 116 | // with a lower index of 0. Each level fans in by exactly that level's _branching_factor, until the spread is enough 117 | // to cover #_MIN..#_MAX. If the spread, at this final (input) level exceeds #_MAX, a _default_value is used. 118 | // Many parameters can use $1, etc. substitutions. These must be passed in using m4_arg(n). 119 | // Params: 120 | // /_source_hier: The hierarchy level to which to apply the operation. 121 | // #_MAX, #_MIN: Max/min (inclusive) range of hierarchy to reduce. 122 | // @_stage: The stage at which to apply the operation, where $1 is the level number. 123 | // $_sig: The signal resulting from the operation, including its range, where $1 is substituted for the level number. 124 | // _branching_factor: _branching_factor-to-1 reduction into each level, where $1 is the level number. 125 | // _op: The operation to apply, where $1 and $2 represent the input $_sigs, eg ['$1 + $2'], where $3 is the level number. 126 | // _default_value: The value/expression to reduce at the last level beyond #_MAX. 127 | // Example: 128 | // m4+tree_redux(/top, /flat, 9, 0, @1, $sig[3:0], 3, m4_arg(1) + m4_arg(2), [''0']) 129 | \TLV tree_redux(/_top, /_source_hier, #_MAX, #_MIN, @_stage, $_sig, _branching_factor, _op, _default_value) 130 | m4+with( 131 | top, ['/_top'], 132 | max, ['#_MAX'], 133 | min, ['#_MIN'], 134 | stage, ['m4_strip_prefix(@_stage)'], 135 | sig, ['m4_strip_prefix($_sig)'], 136 | branching_factor, ['_branching_factor'], 137 | op, ['_op'], 138 | source_hier_name, m4_strip_prefix(/_source_hier), 139 | source_hier_cnt, m4_eval(#_MAX - #_MIN + 1), 140 | ['# Base name of intermediate-level hierarchies, given $1 = level.'], 141 | base_hier_name, ['m4_source_hier_name['_tree']m4_tree_redux_uniquifier['_level']'], 142 | level, 0, 143 | \TLV 144 | m4+tree_redux_inner(0, 1) 145 | @m4_stage 146 | $m4_sig = /m4_base_hier_name['0'][0]$m4_sig; // Pull $_sig out of level0 hierarchy scope. 147 | ) 148 | m4_def(tree_redux_uniquifier, m4_eval(m4_tree_redux_uniquifier + 1)) 149 | 150 | // For use by tree_redux only, using macros defined by \TLV tree_redux. 151 | // Params: 152 | // #_level: 0.., logic level of the reduduction. 153 | // #_spread: 1, m4_branching_factor(0), , the number of outputs to reduce at this level. 154 | \TLV tree_redux_inner(#_level, #_spread) 155 | m4+with_seq( 156 | level, ['#_level'], 157 | next_level, ['m4_eval(#_level + 1)'], 158 | in_spread, ['m4_eval(#_spread * m4_branching_factor(#_level))'], 159 | last_level, ['m4_eval(m4_in_spread >= m4_source_hier_cnt)'], 160 | level_has_leftover_terms, ['m4_eval(m4_in_spread > m4_source_hier_cnt)'], 161 | level_min, ['m4_ifexpr(m4_last_level, m4_min, 0)'], 162 | ['# Macro providing the name of a hierarchy level, given $1 = level.'], 163 | in_hier_scope, ['m4_top/m4_ifexpr(m4_last_level, ['m4_source_hier_name'], ['m4_base_hier_name['']m4_next_level'])'], 164 | fanin, ['m4_ifexpr(m4_last_level, m4_hier_cnt, m4_in_spread)'], 165 | ['# TLV expression for the next-level hier index for the first fanin signal.'], 166 | base_index_expr, ['#m4_base_hier_name['']#_level * m4_branching_factor(#_level) + m4_level_min'], 167 | \TLV 168 | // Recursively generate upstream logic. 169 | m4_ifexpr(m4_last_level, [''], ['m4+tree_redux_inner(m4_next_level, m4_in_spread)']) 170 | // Logic at this level. 171 | /m4_base_hier_name['']#_level[m4_eval(m4_min + #_spread - 1):m4_min] 172 | @m4_stage 173 | m4+with( 174 | ['# Define op_expr as a recursive macro returning the single-line redux expression, either a signal or the operation on the 175 | # signal and the remaining operations. 176 | # op_expr(cnt)'], 177 | op_expr, 178 | ['m4_with( 179 | op1, ['m4_in_hier_scope[m4_base_index_expr + m4_cnt]m4_with(level, m4_next_level, ['$m4_sig'])'], 180 | op2, ['m4_ifexpr(m4_cnt < m4_branching_factor(#_level) - 2, 181 | ['(m4_with(cnt, m4_eval(m4_cnt + 1), ['m4_op_expr['']']))'], 182 | ['m4_in_hier_scope[m4_base_index_expr + m4_eval(m4_cnt + 1)]m4_with(level, m4_next_level, ['$m4_sig'])'])'], 183 | ['m4_op['']'])'], 184 | \TLV 185 | $m4_sig = m4_with(cnt, 0, ['m4_op_expr']); 186 | ) 187 | ) 188 | 189 | // Reduction macro. 190 | // Performs an operation across all instances of a hierarchy and provides the result outside that hierarchy. 191 | // m4+redux($sum[7:0], /hier, max, min, $addend, '0, +) 192 | \TLV redux($_redux_sig,/_hier,#_MAX,#_MIN,$_sig,$_init_expr ,_op) 193 | \always_comb 194 | $['']$_redux_sig = $_init_expr ; 195 | for (int i = #_MIN; i <= #_MAX; i++) 196 | $_redux_sig = $_redux_sig _op /_hier[i]$_sig; 197 | 198 | 199 | // Similar to m4+redux, but each element is conditioned. 200 | // Performs an operation across all instances of a hierarchy and provides the result outside that hierarchy. 201 | // m4+redux_cond($selected_value[7:0], /hier, max, min, $value, '0, |, $select) 202 | \TLV redux_cond($_redux_sig,/_hier,#_MAX,#_MIN,$_sig,$_init_expr ,_op,$_cond_expr) 203 | /_hier[*] 204 | $_sig['']_cond = $_cond_expr ? $_sig : $_init_expr ; 205 | \always_comb 206 | $['']$_redux_sig = $_init_expr ; 207 | for (int i = #_MIN; i <= #_MAX; i++) 208 | $_redux_sig = $_redux_sig _op /_hier[i]$_sig; 209 | 210 | 211 | // Select across a hierarchy (MUX) with a decoded select. Works for $pipe_signals and $ANY. 212 | // m4+select($selected_value[7:0], /top, /hier, ...fix) 213 | //$_redux_sig The resulting signal, including bit range. 214 | // /_top Base scope for references. 215 | // /_hier use /_top['']/_hier[*]/_subhier 216 | // /_subhier Replicated logic is created under m4_hier[*]. 217 | //m4_pushdef(['m4_MAX'], ['$8']) 218 | //m4_pushdef(['m4_MIN'], ['$9']) 219 | // /_sel_sig_subhier /_top['']/_hier[*]$_sel_sig_subhier['']$_sel_sig selects an input. 220 | // $_redux_sig_cond When condition for redux sig. [''] for none (produces '0 if no select); ['-'] or ['-$signame'] to generate as "| /_top['']/_hier[*]/_sel_sig_subhier['']/_sel_sig"; ['$signame'] to use given signal. 221 | \TLV select($_redux_sig, /_top, /_hier, /_subhier, /_sel_sig_subhier, $_sel_sig, $_sig, $_redux_sig_cond) 222 | 223 | m4_pushdef(['m4_hier_index'], ['m4_substr(/_hier, 1)']) 224 | m4_pushdef(['m4_assign_redux_sig_cond'], m4_ifelse(m4_substr(m4_redux_sig_cond, 0, 1), ['-'], ['true'], [''])) // Has '-'. 225 | m4_pushdef(['S_redux_sig_cond'], m4_ifelse(m4_substr($_redux_sig_cond, 0, 1), ['-'], m4_substr($_redux_sig_cond, 1), $_redux_sig_cond)) // Remove '-'. 226 | m4_define(['S_redux_sig_cond'], m4_ifelse(S_redux_sig_cond, [''], $_sel_sig['']_cond, S_redux_sig_cond)) // Make up a signal name if not provided. 227 | 228 | // This is a suboptimal implementation for simulation. 229 | // It does AND/OR reduction. It would be better in simulation to simply index the desired value, 230 | // but this is not currently supported in SandPiper as it is not legal across generate loops. 231 | /_hier[*] 232 | /accum 233 | \always_comb 234 | if (m4_hier_index == \$low(/_top['']/_hier[*]/_sel_sig_subhier['']$_sel_sig)) 235 | $['']$_sig = /_top['']/_hier/_sel_sig_subhier['']$_sel_sig ? /_top['']/_hier['']/_subhier['']$_sig : '0; 236 | else 237 | $_sig = /_top['']/_hier['']/_sel_sig_subhier['']$_sel_sig ? /_top['']/_hier['']/_subhier$_sig : /_hier[m4_hier_index-1]/accum$_sig; 238 | m4_ifelse($_redux_sig_cond,['-'],[' 239 | $_redux_sig = /_hier[\$high(/_top['']/_hier[*]/_sel_sig_subhier['']$_sel_sig)]/accum$_sig; 240 | '], [' 241 | m4_ifelse(m4_assign_redux_sig_cond, [''], [''], S_redux_sig_cond = | m/_top/_hier[*]/_sel_sig_subhier['']$_sel_sig;) 242 | m4_ifelse(S_redux_sig_cond, [''], [''], ?S_redux_sig_cond) 243 | m4_ifelse(S_redux_sig_cond, [''], [''], [' '])$_redux_sig = /_hier[\$high(/_top['']/_hier[*]/_sel_sig_subhier['']$_sel_sig)]/accum$_sig;']) 244 | /* Old way: 245 | \always_comb 246 | $m4_redux_sig = m4_init; 247 | for (int i = m4_MIN; i <= m4_MAX; i++) 248 | m4_redux_sig = m4_redux_sig | (m4_hier[i]m4_index_sig['']_match ? m4_hier[i]m4_sig : '0); 249 | */ 250 | 251 | m4_popdef(['S_redux_sig_cond']) 252 | m4_popdef(['m4_hier_index']) 253 | m4_popdef(['m4_assign_redux_sig_cond']) 254 | 255 | 256 | 257 | 258 | // ================================================================================ 259 | // A TL-Verilog M4 library of macros for verification, logging, and instrumentation 260 | // ================================================================================ 261 | // (Similar to verif.vh, but as M4 macros.) 262 | 263 | 264 | // ################################## 265 | // Multi-line macros 266 | 267 | 268 | // ======== 269 | // Printing 270 | // ======== 271 | 272 | // A macro for printing every clk cycle. 273 | // Params: 274 | // args: The arguments for the \$display call (the print string and variables). 275 | \TLV display(args) 276 | \SV_plus 277 | always_ff @(posedge clk) \$display(args); 278 | 279 | // A macro for printing under a condition. 280 | // Params: 281 | // $cond: The condition under which to display (often the signal of the associated $when condition). 282 | // args: The arguments for the \$display call (the print string and variables). 283 | \TLV display_if($cond, args) 284 | \SV_plus 285 | always_ff @(posedge clk) begin 286 | if ($cond) \$display(args); 287 | end 288 | --------------------------------------------------------------------------------