├── .github └── workflows │ ├── regression.yml │ └── release.yml ├── .gitignore ├── LICENSE ├── MANIFEST.in ├── README.md ├── codecov.yml ├── msdsl ├── __init__.py ├── assignment.py ├── circuit.py ├── eqn │ ├── __init__.py │ ├── cases.py │ ├── deriv.py │ ├── eqn_list.py │ ├── eqn_sys.py │ └── lds.py ├── expr │ ├── __init__.py │ ├── analyze.py │ ├── compression.py │ ├── expr.py │ ├── extras.py │ ├── format.py │ ├── signals.py │ ├── simplify.py │ ├── svreal.py │ └── table.py ├── files.py ├── function.py ├── generator │ ├── __init__.py │ ├── case_statement.py │ ├── generator.py │ ├── svreal.py │ ├── tree_op.py │ └── verilog.py ├── interp │ ├── ctle.py │ ├── interp.py │ ├── lds.py │ └── nonlin.py ├── lfsr.py ├── model.py ├── msdsl.sv ├── plugin │ ├── __init__.py │ └── msdsl.py ├── rf.py ├── templates │ ├── __init__.py │ ├── channel.py │ ├── lds.py │ ├── oscillator.py │ ├── saturation.py │ └── uniform.py └── util.py ├── random ├── adc.py ├── adcdac.py ├── afe.py ├── analyze.py ├── arith.py ├── buck.py ├── casting.py ├── cdf.py ├── diffeq.py ├── diffeqcond.py ├── digital_const.py ├── func.py ├── func2.py ├── func3.py ├── gaussian.py ├── logbits.py ├── noise1.py ├── noise2.py ├── rccirc.py ├── rccirc2.py ├── rccirc3.py ├── rcdisc.py ├── rcnoise.py ├── rctf.py ├── swrc.py └── swrc2.py ├── regress.sh ├── setup.py └── tests ├── __init__.py ├── adc ├── __init__.py ├── test_adc.py └── test_adc.sv ├── arb_noise ├── __init__.py ├── test_arb_noise.py └── test_arb_noise.sv ├── binding ├── __init__.py ├── test_binding.py └── test_binding.sv ├── chan_interp ├── __init__.py ├── test_chan_interp.py └── test_chan_interp.sv ├── circuit_amp ├── __init__.py ├── test_circuit_amp.py └── test_circuit_amp.sv ├── circuit_ind_sw ├── __init__.py ├── test_circuit_ind_sw.py └── test_circuit_ind_sw.sv ├── circuit_rc ├── __init__.py ├── test_circuit_rc.py └── test_circuit_rc.sv ├── circuit_sw ├── __init__.py ├── test_circuit_sw.py └── test_circuit_sw.sv ├── common.py ├── comparator ├── __init__.py ├── test_comparator.py └── test_comparator.sv ├── ctle_interp ├── __init__.py ├── test_ctle_interp.py └── test_ctle_interp.sv ├── ctle_interp2 ├── __init__.py ├── test_ctle_interp2.py └── test_ctle_interp2.sv ├── dac ├── __init__.py ├── test_dac.py └── test_dac.sv ├── det ├── __init__.py ├── test_det.py └── test_det.sv ├── eqn_no_dyn ├── __init__.py ├── test_eqn_no_dyn.py └── test_eqn_no_dyn.sv ├── func_sim ├── __init__.py ├── test_func_sim.py └── test_func_sim.sv ├── gauss_inv_cdf ├── __init__.py ├── test_gauss_inv_cdf.py └── test_gauss_inv_cdf.sv ├── gaussian_noise ├── __init__.py ├── test_gaussian_noise.py └── test_gaussian_noise.sv ├── lfsr_sim ├── __init__.py └── test_lfsr_sim.py ├── lowlevel ├── __init__.py ├── test_check_format.py ├── test_ctle.py ├── test_ctle2.py ├── test_distribute_mult.py ├── test_func.py ├── test_lds.py ├── test_lfsr.py ├── test_multi_func.py ├── test_nonlin.py ├── test_piecewise.py ├── test_ranges.py ├── test_rf.py ├── test_simplify.py ├── test_table.py └── test_width.py ├── mt19937 ├── __init__.py └── test_mt19937.py ├── multi_func_sim ├── __init__.py ├── test_multi_func_sim.py └── test_multi_func_sim.sv ├── osc_model ├── __init__.py ├── test_osc_model.py └── test_osc_model.sv ├── parameters ├── __init__.py ├── test_parameters.py └── test_parameters.sv ├── placeholder ├── __init__.py ├── test_placeholder.py └── test_placeholder.sv ├── rc ├── __init__.py ├── test_rc.py └── test_rc.sv ├── rlc ├── __init__.py ├── test_rlc.py └── test_rlc.sv ├── sub ├── __init__.py ├── test_sub.py └── test_sub.sv ├── table_sim ├── __init__.py ├── test_table_sim.py └── test_table_sim.sv ├── tanh ├── __init__.py ├── test_tanh.py └── test_tanh.sv ├── tf ├── __init__.py ├── test_tf.py └── test_tf.sv ├── uniform ├── __init__.py ├── test_uniform.py └── test_uniform.sv └── var_timestep ├── __init__.py ├── test_var_timestep.sv └── test_var_timstep.py /.github/workflows/regression.yml: -------------------------------------------------------------------------------- 1 | name: Regression 2 | 3 | on: 4 | push: 5 | pull_request: 6 | 7 | jobs: 8 | linux: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | - name: Set up Python 3.7 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: 3.7 17 | - name: Update pip 18 | run: python -m pip install --upgrade pip 19 | - name: Install dependencies 20 | run: sudo apt-get install libgmp-dev libmpfr-dev libmpc-dev iverilog 21 | - name: Run regression test 22 | run: source regress.sh 23 | env: 24 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 25 | 26 | mac: 27 | runs-on: macOS-latest 28 | steps: 29 | - name: Checkout 30 | uses: actions/checkout@v2 31 | - name: Set up Python 3.7 32 | uses: actions/setup-python@v2 33 | with: 34 | python-version: 3.7 35 | - name: Update pip 36 | run: python -m pip install --upgrade pip 37 | - name: Install dependencies 38 | run: | 39 | brew install icarus-verilog coreutils 40 | - name: Run regression test 41 | run: source regress.sh 42 | env: 43 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 44 | 45 | # Windows testing doesn't look like it's going to work, at least for now, because 46 | # some of the packages used by fault don't support Windows. 47 | 48 | # windows: 49 | # runs-on: windows-latest 50 | # steps: 51 | # - name: Checkout 52 | # uses: actions/checkout@v2 53 | # - name: Set up Python 3.7 54 | # uses: actions/setup-python@v2 55 | # with: 56 | # python-version: 3.7 57 | # - name: Install dependencies 58 | # run: | 59 | # curl -L https://github.com/sgherbst/anasymod/releases/download/bogus/iverilog-v11-20201123-x64.tar.gz > iverilog-v11-20201123-x64.tar.gz 60 | # tar xzvf iverilog-v11-20201123-x64.tar.gz 61 | # echo `realpath iverilog/bin` >> $GITHUB_PATH 62 | # shell: bash 63 | # - name: Run regression test 64 | # run: | 65 | # source regress.sh 66 | # env: 67 | # CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 68 | # shell: bash 69 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | jobs: 8 | linux: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout 12 | uses: actions/checkout@v2 13 | - name: Set up Python 3.7 14 | uses: actions/setup-python@v2 15 | with: 16 | python-version: 3.7 17 | - name: Update pip 18 | run: python -m pip install --upgrade pip 19 | - name: Install dependencies 20 | run: sudo apt-get install libgmp-dev libmpfr-dev libmpc-dev iverilog 21 | - name: Run regression test 22 | env: 23 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 24 | run: source regress.sh 25 | - name: Build source distribution 26 | run: python setup.py sdist 27 | - name: Publish source distribution to PyPI 28 | env: 29 | TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }} 30 | TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} 31 | run: | 32 | pip install twine 33 | twine upload dist/* 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | msdsl.egg-info 4 | __pycache__ 5 | tests/*/build 6 | dist/ 7 | HardFloat*/ 8 | install_hardfloat.sh 9 | *.s4p 10 | *.csv -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Stanford University 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include msdsl/msdsl.sv 2 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | # configuration related to pull request comments 2 | comment: no # do not comment PR with the result 3 | 4 | coverage: 5 | range: 50..90 # coverage lower than 50 is red, higher than 90 green, between color code 6 | 7 | status: 8 | project: # settings affecting project coverage 9 | default: 10 | target: auto # auto % coverage target 11 | threshold: 5% # allow for 5% reduction of coverage without failing 12 | 13 | # do not run coverage on patch nor changes 14 | patch: false -------------------------------------------------------------------------------- /msdsl/__init__.py: -------------------------------------------------------------------------------- 1 | from .files import get_msdsl_header 2 | from .expr.svreal import RangeOf 3 | from .expr.signals import (AnalogSignal, DigitalOutput, DigitalInput, AnalogInput, 4 | AnalogOutput, DigitalSignal) 5 | from .expr.simplify import distribute_mult 6 | from .expr.compression import apply_compression, invert_compression 7 | from .model import MixedSignalModel 8 | from .generator.verilog import VerilogGenerator 9 | from .eqn.deriv import Deriv 10 | from .eqn.cases import eqn_case 11 | from .expr.expr import (to_real, to_sint, to_uint, min_op, max_op, sum_op, 12 | clamp_op, compress_uint, mt19937, lcg_op) 13 | from .expr.table import Table, RealTable, SIntTable, UIntTable 14 | from .function import Function, MultiFunction -------------------------------------------------------------------------------- /msdsl/assignment.py: -------------------------------------------------------------------------------- 1 | from msdsl.expr.expr import ModelExpr 2 | from msdsl.expr.signals import Signal, DigitalSignal, AnalogSignal 3 | from msdsl.expr.format import RealFormat 4 | from msdsl.expr.table import Table 5 | 6 | class Assignment: 7 | def __init__(self, signal: Signal, expr: ModelExpr, check_format=True): 8 | self.signal = signal 9 | self.expr = expr 10 | self.check_format = check_format 11 | 12 | class BindingAssignment(Assignment): 13 | pass 14 | 15 | class ThisCycleAssignment(Assignment): 16 | pass 17 | 18 | class NextCycleAssignment(Assignment): 19 | def __init__(self, *args, clk=None, rst=None, ce=None, **kwargs): 20 | self.clk = clk 21 | self.rst = rst 22 | self.ce = ce 23 | super().__init__(*args, **kwargs) 24 | 25 | class SyncRomAssignment(Assignment): 26 | def __init__(self, signal: Signal, table: Table, addr: ModelExpr, 27 | clk=None, ce=None, should_bind=False): 28 | self.table = table 29 | self.clk = clk 30 | self.ce = ce 31 | self.should_bind = should_bind 32 | super().__init__(signal=signal, expr=addr) 33 | 34 | class SyncRamAssignment(Assignment): 35 | def __init__(self, signal: AnalogSignal, format_: RealFormat, addr: ModelExpr, 36 | clk: Signal=None, ce: Signal=None, we: Signal=None, 37 | din: Signal=None, should_bind=False): 38 | self.format_ = format_ 39 | self.clk = clk 40 | self.ce = ce 41 | self.we = we 42 | self.din = din 43 | self.should_bind = should_bind 44 | super().__init__(signal=signal, expr=addr) 45 | -------------------------------------------------------------------------------- /msdsl/eqn/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/msdsl/eqn/__init__.py -------------------------------------------------------------------------------- /msdsl/eqn/cases.py: -------------------------------------------------------------------------------- 1 | from numbers import Integral 2 | from typing import List 3 | 4 | from msdsl.expr.format import RealFormat, UIntFormat 5 | from msdsl.expr.expr import wrap_constants, promote_operands, EqualTo, Sum, Product, prod_op, sum_op, ModelExpr 6 | from msdsl.expr.signals import DigitalSignal, Signal 7 | from msdsl.expr.svreal import UndefinedRange 8 | 9 | def subst_case(expr, sel_bit_settings): 10 | if isinstance(expr, EqnCase): 11 | # select the appropriate case 12 | case = expr.get_case(sel_bit_settings) 13 | 14 | # apply case substitution again (allows for nested cases) 15 | case = subst_case(case, sel_bit_settings) 16 | 17 | # return result 18 | return case 19 | elif isinstance(expr, EqualTo): 20 | return EqualTo(subst_case(expr.lhs, sel_bit_settings), subst_case(expr.rhs, sel_bit_settings)) 21 | elif isinstance(expr, Sum): 22 | return sum_op(subst_case(operand, sel_bit_settings) for operand in expr.operands) 23 | elif isinstance(expr, Product): 24 | return prod_op(subst_case(operand, sel_bit_settings) for operand in expr.operands) 25 | else: 26 | return expr 27 | 28 | def address_to_settings(address, sel_bits): 29 | # sanity checks 30 | assert isinstance(address, Integral), 'Address must be an integer.' 31 | assert 0 <= address <= (1 << len(sel_bits)) - 1, f'The address {address} cannot be represented using {len(sel_bits)} sel_bits.' 32 | 33 | # build up the dictionary of settings 34 | sel_bit_settings = {} 35 | for idx, sel_bit in enumerate(sel_bits[::-1]): 36 | sel_bit_settings[sel_bit.name] = (address >> idx) & 1 37 | 38 | # return the settings 39 | return sel_bit_settings 40 | 41 | def eqn_case(cases, sel_bits: List[DigitalSignal]): 42 | """ 43 | Add a EqnCase object to a MixedSignalModel object of MSDSL. The EqnCase object was populated by cases and sel_bits 44 | that were provided to this function. 45 | 46 | :param cases: equations for each case that is part of the case statement. 47 | :param sel_bits: list of bits that is used to evaluate the case statement. 48 | :return: EqnCase object 49 | """ 50 | # wrap constants and promote them to RealFormat 51 | cases = wrap_constants(cases) 52 | cases = promote_operands(cases, RealFormat) 53 | 54 | # sanity check 55 | assert all(isinstance(sel_bit, Signal) and isinstance(sel_bit.format_, UIntFormat) and sel_bit.format_.width == 1 56 | for sel_bit in sel_bits), 'Selection bits for an EqnCase must all be 1-bit unsigned digital signals.' 57 | assert len(cases) == (1< ModelExpr: 6 | """ 7 | Limit checking. Check if a list of ModelExpr objects provided in *x* is larger than *lo* and smaller than *hi*. 8 | 9 | :param x: List of ModelExpr that are to be checked 10 | :param lo: Lower limit 11 | :param hi: Upper limit 12 | :return: boolean, 1 if x is within limits, 0 otherwise 13 | """ 14 | return BitwiseAnd([between(elem, lo, hi) for elem in x]) 15 | 16 | def between(x: ModelExpr, lo: Union[Number, ModelExpr], hi: Union[Number, ModelExpr]) -> ModelExpr: 17 | """ 18 | Limit checking. Check if a ModelExpr object provided in *x* is larger than *lo* and smaller than *hi*. 19 | 20 | :param x: ModelExpr that is to be checked 21 | :param lo: Lower limit 22 | :param hi: Upper limit 23 | :return: boolean, 1 if x is within limits, 0 otherwise 24 | """ 25 | return (lo <= x) & (x <= hi) 26 | 27 | def replicate(x: ModelExpr, n: Integral): 28 | return concatenate([x]*n) 29 | 30 | def if_(condition, then, else_): 31 | """ 32 | Conditional statement. Condition *condition* is evaluated and if result is true, action *then* is executed, otherwise 33 | action *else_*. 34 | 35 | :param condition: Conditional expression that is to be evaluated 36 | :param then: Action to be executed for True case 37 | :param else_: Action to be executed for False case 38 | :return: Boolean 39 | """ 40 | return array([else_, then], condition) -------------------------------------------------------------------------------- /msdsl/expr/simplify.py: -------------------------------------------------------------------------------- 1 | from collections import OrderedDict 2 | 3 | from msdsl.expr.expr import Constant, Sum, sum_op, Product 4 | from msdsl.expr.signals import Signal 5 | 6 | def distribute_mult(expr): 7 | if isinstance(expr, Sum): 8 | # distribute multiplication in a bottom-up fashion 9 | operands = [distribute_mult(operand) for operand in expr.operands] 10 | return sum_op(operands) 11 | elif isinstance(expr, Product) and len(expr.operands) == 2: 12 | # distribute multiplication in a bottom-up fashion 13 | operands = [distribute_mult(operand) for operand in expr.operands] 14 | 15 | if isinstance(operands[0], Constant) and isinstance(operands[1], Sum): 16 | const, sum_operands = operands[0], operands[1].operands 17 | elif isinstance(operands[1], Constant) and isinstance(operands[0], Sum): 18 | const, sum_operands = operands[1], operands[0].operands 19 | else: 20 | return expr 21 | 22 | # distribute constant multiplication into summation 23 | return sum_op([const*sum_operand for sum_operand in sum_operands]) 24 | else: 25 | return expr 26 | 27 | def extract_coeffs(expr: Sum): 28 | # initialize 29 | pairs, others = [], [] 30 | 31 | # extract coefficients for all terms 32 | if isinstance(expr, Signal): 33 | pairs.append((1, expr)) 34 | elif isinstance(expr, Product): 35 | result = extract_coeffs_from_product(expr) 36 | if result is not None: 37 | pairs.append(result) 38 | else: 39 | others.append(expr) 40 | else: 41 | for operand in expr.operands: 42 | print(operand, type(operand)) 43 | if isinstance(operand, Signal): 44 | pairs.append((1, operand)) 45 | elif isinstance(operand, Product) and len(operand.operands) == 2: 46 | result = extract_coeffs_from_product(operand) 47 | if result is not None: 48 | pairs.append(result) 49 | else: 50 | others.append(operand) 51 | else: 52 | others.append(operand) 53 | 54 | # return result 55 | return pairs, others 56 | 57 | def extract_coeffs_from_product(p: Product): 58 | if isinstance(p.operands[0], Constant) and isinstance(p.operands[1], Signal): 59 | return (p.operands[0].value, p.operands[1]) 60 | elif isinstance(p.operands[1], Constant) and isinstance(p.operands[0], Signal): 61 | return (p.operands[1].value, p.operands[0]) 62 | else: 63 | return None 64 | 65 | def collect_terms(expr): 66 | # only apply this operation to Sum expressions 67 | if not isinstance(expr, Sum): 68 | return expr 69 | 70 | # extract pairs of coefficients and signal names 71 | pairs, others = extract_coeffs(expr) 72 | signals = {} 73 | 74 | # collect terms with same coefficient 75 | coeffs = OrderedDict() 76 | for coeff, signal in pairs: 77 | if signal.name not in coeffs: 78 | coeffs[signal.name] = 0 79 | signals[signal.name] = signal 80 | coeffs[signal.name] += coeff 81 | 82 | # construct full expression 83 | operands = [signals[key]*val for key, val in coeffs.items()] 84 | operands += others 85 | 86 | # return the result 87 | return sum_op(operands) 88 | 89 | def main(): 90 | from msdsl.expr.signals import AnalogSignal 91 | 92 | a = AnalogSignal('a') 93 | b = AnalogSignal('b') 94 | c = AnalogSignal('c') 95 | d = AnalogSignal('d') 96 | e = AnalogSignal('e') 97 | 98 | print(distribute_mult(1*a+2*b+3*(4+5*(6+7*c)))) 99 | 100 | pairs, others = extract_coeffs(a+2*b+3*c) 101 | print('pairs: ' + str({k: v.name for k, v in pairs})) 102 | print('others: ' + str([str(other) for other in others])) 103 | 104 | def simplify(expr): 105 | return collect_terms(distribute_mult(expr)) 106 | 107 | print(simplify(1*a+2*b+3*c+4*d+7*(c+(d+e)*(4+5)))) 108 | print(simplify((2*a+2*b)/2)) 109 | print(simplify(a/2+(a+2*b)/2)) 110 | print(simplify(a+b-b)) 111 | print(simplify(a+2*(1-b)+1*(2*b-a)-2)) 112 | 113 | if __name__ == '__main__': 114 | main() 115 | -------------------------------------------------------------------------------- /msdsl/files.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | PACK_DIR = Path(__file__).resolve().parent 4 | 5 | def get_msdsl_header(): 6 | return PACK_DIR / 'msdsl.sv' -------------------------------------------------------------------------------- /msdsl/generator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/msdsl/generator/__init__.py -------------------------------------------------------------------------------- /msdsl/generator/case_statement.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from msdsl.generator.generator import CodeGenerator 3 | 4 | def case_statment(gen: CodeGenerator, sel, var, values: List, default=None): 5 | gen.writeln('always @(*) begin') 6 | gen.indent() 7 | 8 | gen.writeln(f'case ({sel})') 9 | gen.indent() 10 | 11 | for k, value in enumerate(values): 12 | gen.writeln(f'{k}: {var} = {value};') 13 | 14 | if default is not None: 15 | gen.writeln(f'default: {var} = {default};') 16 | 17 | gen.dedent() 18 | gen.writeln('endcase') 19 | 20 | gen.dedent() 21 | gen.writeln('end') 22 | 23 | def main(): 24 | gen = CodeGenerator() 25 | 26 | case_statment(gen, 'sel', 'var', ['a', 'b', 'c', 'd'], 'e') 27 | 28 | print(gen.text) 29 | 30 | if __name__ == '__main__': 31 | main() -------------------------------------------------------------------------------- /msdsl/generator/generator.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from numbers import Number 3 | 4 | from msdsl.expr.signals import Signal, DigitalSignal, AnalogSignal 5 | from msdsl.expr.expr import ModelExpr 6 | from msdsl.expr.table import Table 7 | from msdsl.expr.format import RealFormat 8 | from msdsl.util import Namer 9 | 10 | class CodeGenerator: 11 | def __init__(self, tab_string: str=None, line_ending: str=None, namer: Namer=None): 12 | # save settings 13 | self.tab_string = tab_string if tab_string is not None else ' ' 14 | self.line_ending = line_ending if line_ending is not None else '\n' 15 | self.namer = namer if namer is not None else Namer() 16 | 17 | # initialize variables 18 | self.tab_level = 0 19 | self.text = '' 20 | 21 | # concrete functions 22 | 23 | def indent(self): 24 | self.tab_level += 1 25 | 26 | def dedent(self): 27 | self.tab_level -= 1 28 | assert self.tab_level >= 0 29 | 30 | def write(self, string=''): 31 | self.text += string 32 | 33 | def writeln(self, line=''): 34 | self.write(self.tab_level * self.tab_string + line + self.line_ending) 35 | 36 | def write_to_file(self, filename): 37 | with open(filename, 'w') as f: 38 | f.write(self.text) 39 | 40 | ############################### 41 | # abstract methods 42 | ############################### 43 | 44 | def make_section(self, label): 45 | raise NotImplementedError 46 | 47 | def make_signal(self, s: Signal): 48 | raise NotImplementedError 49 | 50 | def make_probe(self, s: Signal): 51 | raise NotImplementedError 52 | 53 | def make_assign(self, input_: Signal, output: Signal, check_format=True): 54 | raise NotImplementedError 55 | 56 | def make_mem(self, next_: Signal, curr: Signal, init: Number=0, clk: Signal=None, 57 | rst: Signal=None, ce: Signal=None, check_format=True): 58 | raise NotImplementedError 59 | 60 | def make_sync_rom(self, signal: Signal, table: Table, addr: Signal, 61 | clk: Signal=None, ce: Signal=None): 62 | raise NotImplementedError 63 | 64 | def make_sync_ram(self, signal: AnalogSignal, format_: RealFormat, addr: DigitalSignal, 65 | clk: DigitalSignal=None, ce: DigitalSignal=None, we: DigitalSignal=None, 66 | din: DigitalSignal=None): 67 | raise NotImplementedError 68 | 69 | def expr_to_signal(self, expr: ModelExpr): 70 | raise NotImplementedError 71 | 72 | def start_module(self, name: str, ios: List[Signal], real_params: List, digital_params: List=None): 73 | raise NotImplementedError 74 | 75 | def end_module(self): 76 | raise NotImplementedError 77 | 78 | def main(): 79 | gen = CodeGenerator() 80 | gen.writeln('1. Outer') 81 | gen.indent() 82 | gen.writeln('1.1 Inner') 83 | gen.writeln('1.2 Inner') 84 | gen.dedent() 85 | gen.writeln('2. Outer') 86 | print(gen.text) 87 | 88 | if __name__ == '__main__': 89 | main() -------------------------------------------------------------------------------- /msdsl/generator/svreal.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | from numbers import Number 4 | 5 | from msdsl.expr.svreal import RangeExpr, RangeOf, RangeMax, RangeSum, RangeProduct, WidthOf, WidthExpr, ExponentExpr, \ 6 | ExponentOf, RangeOperator, ParamRange 7 | from msdsl.generator.tree_op import tree_op 8 | 9 | def max_op(a, b): 10 | if a is not None: 11 | if b is not None: 12 | return f'`MAX_MATH({a}, {b})' 13 | else: 14 | return a 15 | else: 16 | if b is not None: 17 | return b 18 | else: 19 | raise ValueError('Cannot compute the maximum when both arguments are None.') 20 | 21 | def compile_range_expr(expr: Union[RangeExpr, Number]): 22 | if expr is None: 23 | return None 24 | elif isinstance(expr, Number): 25 | return str(expr) 26 | elif isinstance(expr, RangeOf): 27 | return f'`RANGE_PARAM_REAL({expr.name})' 28 | elif isinstance(expr, ParamRange): 29 | return f'`CONST_RANGE_REAL({expr.name})' 30 | 31 | # otherwise it must be a RangeOperator 32 | assert isinstance(expr, RangeOperator), 'Expected a RangeOperator here.' 33 | operands = [compile_range_expr(operand) for operand in expr.operands] 34 | 35 | if isinstance(expr, RangeSum): 36 | return '(' + '+'.join(operands) + ')' 37 | elif isinstance(expr, RangeProduct): 38 | return '(' + '*'.join(operands) + ')' 39 | elif isinstance(expr, RangeMax): 40 | return tree_op(operands=operands, operator=max_op) 41 | else: 42 | raise Exception('Range expression not handled: ' + expr.__class__.__name__) 43 | 44 | def compile_width_expr(expr: Union[WidthExpr, Number]): 45 | if expr is None: 46 | return None 47 | elif isinstance(expr, Number): 48 | return str(expr) 49 | elif isinstance(expr, WidthOf): 50 | return f'`WIDTH_PARAM_REAL({expr.name})' 51 | else: 52 | raise Exception('Width expression not handled: ' + expr.__class__.__name__) 53 | 54 | def compile_exponent_expr(expr: Union[ExponentExpr, Number]): 55 | if expr is None: 56 | return None 57 | elif isinstance(expr, Number): 58 | return str(expr) 59 | elif isinstance(expr, ExponentOf): 60 | return f'`EXPONENT_PARAM_REAL({expr.name})' 61 | else: 62 | raise Exception('Exponent expression not handled: ' + expr.__class__.__name__) 63 | 64 | def main(): 65 | from msdsl.expr.svreal import range_max 66 | a = RangeOf('a') 67 | b = RangeOf('b') 68 | c = RangeOf('c') 69 | d = RangeOf('d') 70 | 71 | print(compile_range_expr((a+b+1)*4*2+5+7)) 72 | print(compile_range_expr(range_max([a, b, c, d]))) 73 | 74 | if __name__ == '__main__': 75 | main() -------------------------------------------------------------------------------- /msdsl/generator/tree_op.py: -------------------------------------------------------------------------------- 1 | def tree_op(operands, operator): 2 | if len(operands) == 1: 3 | return operands[0] 4 | elif len(operands) > 1: 5 | a = tree_op(operands[:len(operands) // 2], operator=operator) 6 | b = tree_op(operands[len(operands) // 2:], operator=operator) 7 | return operator(a, b) 8 | else: 9 | raise Exception('Tree operation cannot be applied to an empty list.') 10 | 11 | def main(): 12 | # tree_op tests 13 | op = lambda a, b: a+b 14 | 15 | print(tree_op([1], operator=op)) 16 | print(tree_op([1, 2], operator=op)) 17 | print(tree_op([1, 2, 3], operator=op)) 18 | print(tree_op([1, 2, 3, 4], operator=op)) 19 | print(tree_op([1, 2, 3, 4, 5], operator=op)) 20 | 21 | if __name__ == '__main__': 22 | main() -------------------------------------------------------------------------------- /msdsl/interp/ctle.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import scipy.signal 3 | 4 | 5 | def calc_ctle_abcd(fz, fp1, fp2=None, gbw=None): 6 | num, den = calc_ctle_num_den(fz=fz, fp1=fp1, fp2=fp2, gbw=gbw) 7 | return scipy.signal.tf2ss(num, den) 8 | 9 | 10 | def calc_ctle_num_den(fz, fp1, fp2=None, gbw=None): 11 | # calculate fp2 if needed 12 | if fp2 is None: 13 | if gbw is not None: 14 | fp2 = gbw/(fp1/fz) 15 | else: 16 | raise Exception('Must specify fp2 or gbw.') 17 | 18 | # compute angular frequencies 19 | wz = 2*np.pi * fz 20 | wp1 = 2*np.pi * fp1 21 | wp2 = 2*np.pi * fp2 22 | 23 | # calculate numerator and denominator 24 | num = (1/wz, 1) 25 | den = (1/wp1 * 1/wp2, 1/wp1 + 1/wp2, 1) 26 | 27 | # return numerator and denominator 28 | return num, den 29 | -------------------------------------------------------------------------------- /msdsl/interp/interp.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.interpolate import interp1d 3 | 4 | 5 | # convenience function: return interpolator that extends boundary values 6 | def myinterp(x, y): 7 | return interp1d(x, y, bounds_error=False, fill_value=(y[0], y[-1])) 8 | 9 | 10 | # calculate coefficients of a polynomial that fits x, y points 11 | # the polynomial order is len(x)-1 (e.g., 3 points yields quadratic) 12 | def calc_piecewise_poly(u, order=3, W=None, strategy='overlap'): 13 | # solve the linear system of equations 14 | if W is None: 15 | W = calc_interp_w(npts=len(u), order=order, strategy=strategy) 16 | 17 | # generate coefficients 18 | U = (W*u).sum(axis=2) 19 | 20 | # return coefficients 21 | return U 22 | 23 | 24 | # calculate the tensor, "W", that maps input points 25 | # to coefficients of polynomial segments. w[j, k, i] maps 26 | # the i-th spline point to the k-th coefficient of the 27 | # j-th polynomial segment. 28 | def calc_interp_w(npts, order=3, strategy='overlap'): 29 | # set up equations 30 | if strategy == 'overlap': 31 | A, B = ovl_pwp_mats(npts=npts, order=order) 32 | else: 33 | raise Exception(f'Unknown strategy: {strategy}') 34 | 35 | # solve the equations 36 | C = np.linalg.solve(A, B) 37 | 38 | # fill in the W tensor 39 | W = np.zeros((npts, order+1, npts), dtype=float) 40 | W[:-1, :, :] = C.reshape((npts-1, order+1, npts)) 41 | 42 | # a final PWC segment is included for make the math easier 43 | W[-1, 0, -1] = 1 44 | 45 | # return W tensor 46 | return W 47 | 48 | 49 | # return matrices for piecewise-polynomial fit 50 | def ovl_pwp_mats(npts, order): 51 | # create empty equation matrices 52 | A = np.zeros(((npts-1)*(order+1), (npts-1)*(order+1)), dtype=float) 53 | B = np.zeros(((npts-1)*(order+1), npts), dtype=float) 54 | 55 | # for the middle of the curve, how many points should 56 | # match below the starting point (tgt_lo) and above (tgt_hi) 57 | tgt_lo = order//2 58 | tgt_hi = order-tgt_lo 59 | 60 | # build up the equations 61 | idx = 0 62 | for i in range(npts-1): 63 | # figure out the starting and stopping matching points 64 | if (i-tgt_lo) < 0: 65 | lo = 0 66 | hi = lo + order 67 | elif (i+tgt_hi) > (npts-1): 68 | hi = npts-1 69 | lo = hi-order 70 | else: 71 | lo = i-tgt_lo 72 | hi = i+tgt_hi 73 | 74 | # add equations for each point 75 | offset = i*(order+1) 76 | for j in range(lo, hi+1): 77 | A[idx, offset:(offset+order+1)] = eval_poly(j-i, order=order) 78 | B[idx, j] = 1 79 | idx += 1 80 | 81 | return A, B 82 | 83 | 84 | # evaluate a polynomial; return a such that 85 | # a[k] = x^k 86 | # sum(ak*x^k, 0, order) == sum(bk*x^k, 0, order) 87 | def eval_poly(x, order): 88 | return x**np.arange(order+1) 89 | 90 | # evaluate a piecewise polynomial waveform 91 | # U[i, j] is the ith segment, jth coefficient (i.e., jth power) 92 | def eval_piecewise_poly(t, th, U): 93 | # convert t to a numpy array if needed 94 | t = np.array(t) 95 | 96 | # calculate coefficient indices and remainders 97 | ivec = np.floor(t/th).astype(int) 98 | rvec = (t-(ivec*th))/th 99 | 100 | # sum contributions from each polynomial order 101 | pows = rvec[..., np.newaxis]**np.arange(U.shape[1]) 102 | retval = (U[ivec, :]*pows).sum(axis=len(pows.shape)-1) 103 | 104 | # return result 105 | return retval 106 | -------------------------------------------------------------------------------- /msdsl/interp/nonlin.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import scipy.optimize 3 | 4 | 5 | def v2db(x): 6 | return 20*np.log10(x) 7 | 8 | 9 | def db2v(x): 10 | return 10**(x/20) 11 | 12 | 13 | def tanhsat(v, vsat): 14 | return vsat * np.tanh(v / np.abs(vsat)) 15 | 16 | 17 | def calc_tanh_vsat(compr, units=None, veval=1.0): 18 | # calculate attenuation 19 | if units is not None: 20 | if units in {'dB'}: 21 | compr = db2v(compr) 22 | else: 23 | raise Exception(f'Unknown units: {units}') 24 | 25 | # solve nonlinear equation for vsat 26 | func = lambda vsat: tanhsat(veval, vsat) - compr*veval 27 | root = scipy.optimize.fsolve(func, veval) # second argument is the initial guess 28 | vsat = np.abs(root[0]) 29 | 30 | # return vsat 31 | return vsat 32 | -------------------------------------------------------------------------------- /msdsl/plugin/__init__.py: -------------------------------------------------------------------------------- 1 | from .msdsl import CustomPlugin -------------------------------------------------------------------------------- /msdsl/plugin/msdsl.py: -------------------------------------------------------------------------------- 1 | import os 2 | from pathlib import Path 3 | from argparse import ArgumentParser 4 | 5 | from svreal import get_svreal_header 6 | 7 | # path to the top-level msdsl package directory 8 | PACK_DIR = Path(__file__).resolve().parent.parent 9 | 10 | # TODO: figure out how to remove dependency on anasymod (which itself depends on msdsl) 11 | from anasymod.sources import VerilogHeader, VerilogSource, FunctionalModel 12 | from anasymod.defines import Define 13 | from anasymod.files import mkdir_p, rm_rf, which 14 | from anasymod.util import call 15 | from anasymod.plugins import Plugin 16 | from anasymod.config import EmuConfig 17 | 18 | class CustomPlugin(Plugin): 19 | def __init__(self, prj_cfg: EmuConfig, cfg_file, prj_root): 20 | super().__init__(cfg_file=cfg_file, prj_root=prj_root, build_root=prj_cfg.build_root_functional_models, name='msdsl') 21 | 22 | self.include_statements += ['`include "msdsl.sv"'] 23 | 24 | # Initialize list of functional models to be generated 25 | self.generator_sources = [] 26 | 27 | # Initialize Parameters 28 | self.dt = prj_cfg.cfg.dt 29 | 30 | # Update msdsl config with msdsl section in config file 31 | self.cfg.update_config(subsection=self._name) 32 | 33 | ##### Custom functions for actions triggered from command line or by setting an option 34 | 35 | def models(self): 36 | """ 37 | Call gen.py to generate analog models. 38 | """ 39 | # make model directory, removing the old model directory if necessary 40 | rm_rf(self._build_root) 41 | 42 | # run generator script 43 | if 'PYTHON_MSDSL' in os.environ: 44 | python_name = os.environ['PYTHON_MSDSL'] 45 | else: 46 | python_name = which('python') 47 | for generator_source in self.generator_sources: 48 | # make model directory if necessary 49 | mkdir_p(os.path.join(self._build_root, generator_source.fileset, generator_source.name)) 50 | for file in generator_source.files: 51 | call([python_name, file, '-o', os.path.join(self._build_root, generator_source.fileset, generator_source.name), '--dt', str(self.dt)]) 52 | 53 | def float(self): 54 | self._add_define(Define(name='FLOAT_REAL', fileset='sim')) 55 | 56 | def range_assertions(self): 57 | self._add_define(Define(name='RANGE_ASSERTIONS', fileset='sim')) 58 | 59 | def add_saturation(self): 60 | self._add_define(Define(name='ADD_SATURATION')) 61 | 62 | ##### Utility Functions 63 | 64 | def _setup_defines(self): 65 | """ 66 | Add Define objects that are specific to MSDSL 67 | """ 68 | self._add_define(Define(name='DT_MSDSL', value=self.dt)) 69 | self._add_define(Define(name='SIMULATION_MODE_MSDSL', fileset='sim')) 70 | 71 | def _setup_sources(self): 72 | """ 73 | Add Source objects that are specific to MSDSL 74 | """ 75 | 76 | # Add MSDSL and SVREAL sources 77 | self._add_source(source=VerilogHeader(files=[PACK_DIR / 'msdsl.sv'], 78 | config_path=self._srccfg_path, 79 | name='msdsl')) 80 | self._add_source(source=VerilogHeader(files=[get_svreal_header()], 81 | config_path=self._srccfg_path, 82 | name='svreal')) 83 | 84 | def _parse_args(self): 85 | """ 86 | Read command line arguments. This supports convenient usage from command shell e.g.: 87 | python analysis.py -i filter --models --sim --view 88 | 89 | --range_assertions: Enables range checks, to detect overflows when working with fixed-point datatypes. 90 | To work with this feature efficiently, make sure to have --float set as well. 91 | 92 | --float: Change from fixed-point datatypes to float for running generated models. 93 | 94 | --add_saturation: Enable saturation feature for fixed-point based simulations. This will prevent overflows. 95 | 96 | """ 97 | parser = ArgumentParser() 98 | parser.add_argument('--range_assertions', action='store_true') 99 | parser.add_argument('--float', action='store_true') 100 | parser.add_argument('--add_saturation', action='store_true') 101 | 102 | self.args, _ = parser.parse_known_args() -------------------------------------------------------------------------------- /msdsl/rf.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from scipy.interpolate import interp1d 3 | from scipy.fftpack import ifft 4 | import logging 5 | from math import log2, ceil 6 | from scipy.integrate import cumtrapz 7 | 8 | from skrf import Network 9 | 10 | 11 | def s2sdd(s): 12 | """ Converts a 4-port single-ended S-parameter matrix 13 | to a 2-port differential mode representation. 14 | Reference: https://www.aesa-cortaillod.com/fileadmin/documents/knowledge/AN_150421_E_Single_ended_S_Parameters.pdf 15 | """ 16 | 17 | sdd = np.zeros((2, 2), dtype=np.complex128) 18 | sdd[0, 0] = 0.5 * (s[0, 0] - s[0, 2] - s[2, 0] + s[2, 2]) 19 | sdd[0, 1] = 0.5 * (s[0, 1] - s[0, 3] - s[2, 1] + s[2, 3]) 20 | sdd[1, 0] = 0.5 * (s[1, 0] - s[1, 2] - s[3, 0] + s[3, 2]) 21 | sdd[1, 1] = 0.5 * (s[1, 1] - s[1, 3] - s[3, 1] + s[3, 3]) 22 | 23 | return sdd 24 | 25 | 26 | def s2tf(s, zo, zs, zl): 27 | """ Converts a two-port S-parameter matrix to a transfer function, 28 | given characteristic impedance, input impedance, and output 29 | impedance. 30 | Reference: https://www.mathworks.com/help/rf/ug/s2tf.html 31 | """ 32 | 33 | gamma_l = (zl - zo) / (zl + zo) 34 | gamma_s = (zs - zo) / (zs + zo) 35 | gamma_in = s[0, 0] + (s[0, 1] * s[1, 0] * gamma_l / (1 - s[1, 1] * gamma_l)) 36 | 37 | tf = ((zs + np.conj(zs)) / np.conj(zs)) * (s[1, 0] * (1 + gamma_l) * (1 - gamma_s)) / ( 38 | 2 * (1 - s[1, 1] * gamma_l) * (1 - gamma_in * gamma_s)) 39 | 40 | return tf 41 | 42 | 43 | def is_mostly_real(v, ratio=1e-6): 44 | return np.all(np.abs(np.imag(v) / np.real(v)) < ratio) 45 | 46 | 47 | def get_impulse(f, tf, dt, T): 48 | """ Calculates the impulse response, given a single-sided transfer function. 49 | f should be non-negative and increasing. See https://www.overleaf.com/read/mxxtgdvkmkvt 50 | """ 51 | 52 | # calculate number of time points in impulse response 53 | n_req = round(T / dt) 54 | logging.debug('Number of time points requested: {}'.format(n_req)) 55 | 56 | # calculate number of IFFT points 57 | n = 1 << int(ceil(log2(n_req))) 58 | logging.debug('Number of IFFT points: {}'.format(n)) 59 | 60 | # calculate frequency spacing 61 | df = 1 / (n * dt) 62 | 63 | # copy f and tf vectors so they can be modified 64 | f = f.copy() 65 | tf = tf.copy() 66 | 67 | # make sure that the DC component is real if present 68 | if f[0] == 0: 69 | logging.debug('Removing imaginary part of tf[0]') 70 | 71 | assert is_mostly_real(tf[0]) 72 | tf[0] = tf[0].real 73 | 74 | # calculate magnitude and phase 75 | ma = np.abs(tf) 76 | ph = np.unwrap(np.angle(tf)) 77 | 78 | # add DC component if necessary 79 | if f[0] != 0: 80 | logging.debug('Adding point f[0]=0, tf[0]=abs(tf[1])') 81 | 82 | f = np.concatenate(([0], f)) 83 | ma = np.concatenate(([ma[0]], ma)) 84 | ph = np.concatenate(([0], ph)) 85 | 86 | # interpolate magnitude and phase 87 | logging.debug('Interpolating magnitude and phase.') 88 | f_interp = np.arange(n / 2) * df 89 | ma_interp = interp1d(f, ma, bounds_error=False, fill_value=(ma[0], 0))(f_interp) 90 | ph_interp = interp1d(f, ph, bounds_error=False, fill_value=(0, 0))(f_interp) 91 | 92 | # create frequency response vector needed for IFFT 93 | logging.debug('Creating the frequency response vector.') 94 | Gtilde = np.zeros(n, dtype=np.complex128) 95 | Gtilde[:(n // 2)] = ma_interp * np.exp(1j * ph_interp) 96 | Gtilde[((n // 2) + 1):] = np.conjugate(Gtilde[((n // 2) - 1):0:-1]) 97 | 98 | # compute impulse response 99 | y_imp = n * df * (ifft(Gtilde)[:n_req]) 100 | 101 | # check that the impulse response is real to within numerical precision 102 | if not is_mostly_real(y_imp): 103 | raise Exception('IFFT contains unacceptable imaginary component.') 104 | y_imp = np.real(y_imp) 105 | 106 | return np.arange(n_req) * dt, y_imp 107 | 108 | 109 | def imp2step(imp, dt): 110 | step = cumtrapz(imp, initial=0) * dt 111 | 112 | return step 113 | 114 | 115 | def s4p_to_step(s4p, dt, T, zs=50, zl=50): 116 | t, imp = s4p_to_impulse(s4p=s4p, dt=dt, T=T, zs=zs, zl=zl) 117 | step = imp2step(imp, dt) 118 | 119 | return t, step 120 | 121 | 122 | def s4p_to_tf(s4p, zs=50, zl=50): 123 | # read S-parameter file 124 | ntwk = Network(s4p) 125 | 126 | # extract characteristic impedance 127 | # assumed to be the same for all 16 measurements 128 | z0 = ntwk.z0[0, 0] 129 | 130 | # extract frequency list 131 | freq = ntwk.frequency.f 132 | 133 | # extract transfer function 134 | tf = np.array([s2tf(s2sdd(s), 2 * z0, 2 * zs, 2 * zl) for s in ntwk.s]) 135 | 136 | return freq, tf 137 | 138 | 139 | def s4p_to_impulse(s4p, dt, T, zs=50, zl=50): 140 | # get transfer function 141 | freq, tf = s4p_to_tf(s4p=s4p, zs=zs, zl=zl) 142 | 143 | # get impulse response 144 | t, y_imp = get_impulse(freq, tf, dt, T) 145 | 146 | return t, y_imp 147 | -------------------------------------------------------------------------------- /msdsl/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/msdsl/templates/__init__.py -------------------------------------------------------------------------------- /msdsl/templates/oscillator.py: -------------------------------------------------------------------------------- 1 | from msdsl import MixedSignalModel, to_sint, to_uint, clamp_op 2 | from msdsl.expr.format import SIntFormat 3 | from msdsl.expr.extras import if_ 4 | 5 | class OscillatorModel(MixedSignalModel): 6 | def __init__(self, period='period', dt_req='dt_req', emu_dt='emu_dt', clk_en='clk_en', 7 | clk=None, rst=None, init=0, dt_width=32, dt_scale=1e-15, **kwargs): 8 | # call the super constructor 9 | super().__init__(**kwargs) 10 | 11 | # add other signals 12 | period = self.add_analog_input(period) 13 | emu_dt = self.add_digital_input(emu_dt, width=dt_width) 14 | dt_req = self.add_digital_output(dt_req, width=dt_width, init=init) 15 | clk_en = self.add_digital_output(clk_en) 16 | if clk is not None: 17 | clk = self.add_digital_input(clk) 18 | if rst is not None: 19 | rst = self.add_digital_input(rst) 20 | 21 | # determine if the request was granted 22 | self.set_this_cycle(clk_en, self.dt_req == self.emu_dt) 23 | 24 | # discretize the period to a uint 25 | # TODO: cleanup 26 | period_real = self.set_this_cycle( 27 | 'period_real', period/dt_scale) 28 | period_sint = self.set_this_cycle( 29 | 'period_sint', to_sint(period_real, width=dt_width+1)) 30 | period_sint.format_ = SIntFormat( 31 | width=dt_width+1, min_val=0, max_val=((1<>num_bits) & 1) - 0.5) 47 | unf_noise_mag = unf_noise & ((1< 0: 62 | meas = 1 - meas_cdf[k] 63 | expct = 1 - expct_cdf[k] 64 | else: 65 | meas = meas_cdf[k] 66 | expct = expct_cdf[k] 67 | rel_err = (meas-expct)/expct 68 | print(f'{test_pts[k]}: meas {meas:e}, expct {expct:e}, rel_err {rel_err*100:0.3f}%') -------------------------------------------------------------------------------- /random/diffeq.py: -------------------------------------------------------------------------------- 1 | from msdsl import * 2 | r, c = 1e3, 1e-9 3 | m = MixedSignalModel('rc', dt=0.1e-6) 4 | x = m.add_analog_input('x') 5 | y = m.add_analog_output('y') 6 | m.add_eqn_sys([c*Deriv(y) == (x-y)/r]) 7 | m.compile_and_print(VerilogGenerator()) 8 | -------------------------------------------------------------------------------- /random/diffeqcond.py: -------------------------------------------------------------------------------- 1 | from msdsl import * 2 | r, rsw, c = 1e3, 100, 1e-9 3 | m = MixedSignalModel('rc', dt=0.1e-6) 4 | x = m.add_analog_input('x') 5 | s = m.add_digital_input('s') 6 | y = m.add_analog_output('y') 7 | g = eqn_case([1/r, 1/r+1/rsw], [s]) 8 | m.add_eqn_sys([c*Deriv(y) == (x-y)*g]) 9 | m.compile_and_print(VerilogGenerator()) 10 | -------------------------------------------------------------------------------- /random/digital_const.py: -------------------------------------------------------------------------------- 1 | 2 | # class DigitalConstant(ModelExpr): 3 | # def __init__(self, value: Number): 4 | # 5 | # def __init__(self, value: Integral, width: Integral = None, signed: bool = None): 6 | -------------------------------------------------------------------------------- /random/func.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from msdsl import * 3 | r, c = 1e3, 1e-9 4 | m = MixedSignalModel('rc') 5 | x = m.add_analog_input('x') 6 | dt = m.add_analog_input('dt') 7 | y = m.add_analog_output('y') 8 | func = lambda dt: np.exp(-dt/(r*c)) 9 | f = m.make_function(func, 10 | domain=[0, 10*r*c], numel=512, order=1) 11 | a = m.set_from_sync_func('a', f, dt) 12 | x_prev = m.cycle_delay(x, 1) 13 | y_prev = m.cycle_delay(y, 1) 14 | m.set_this_cycle(y, a*y_prev + (1-a)*x_prev) 15 | m.compile_and_print(VerilogGenerator()) -------------------------------------------------------------------------------- /random/func2.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from msdsl import * 3 | m = MixedSignalModel('model') 4 | x = m.add_analog_input('x') 5 | y1 = m.add_analog_output('y1') 6 | y2 = m.add_analog_output('y2') 7 | func1 = lambda t: np.sin(t) 8 | func2 = lambda t: np.cos(t) 9 | f = m.make_function([func1, func2], 10 | domain=[-np.pi, np.pi], numel=512, order=1) 11 | m.set_from_sync_func([y1, y2], f, x) 12 | m.compile_and_print(VerilogGenerator()) -------------------------------------------------------------------------------- /random/func3.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from msdsl import * 3 | m = MixedSignalModel('rc') 4 | dt = m.add_analog_input('dt') 5 | alpha = m.add_analog_output('alpha') 6 | func = lambda dt: np.exp(-dt) 7 | f = m.make_function(func, domain=[0, 10], numel=512, order=1) 8 | m.set_from_sync_func(alpha, f, dt) 9 | m.compile_and_print(VerilogGenerator()) -------------------------------------------------------------------------------- /random/gaussian.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import numpy.ma as ma 3 | import matplotlib.pyplot as plt 4 | from msdsl import Function 5 | from scipy.stats import truncnorm 6 | 7 | inv_cdf = lambda x: truncnorm.ppf(x, -8, 8) 8 | num_bits = 31 # 31 because the top bit is used to determine the sign 9 | 10 | num_test = 10000 11 | test_pts = 2**(np.random.uniform(0, num_bits, num_test)) 12 | test_pts = np.floor(test_pts).astype(np.int) 13 | test_pts = np.append(test_pts, 0) 14 | test_pts = np.sort(test_pts) 15 | 16 | # logarithmic mapping 17 | def map_f(r): 18 | # convert input to an array 19 | # ref: https://stackoverflow.com/questions/29318459/python-function-that-handles-scalar-or-arrays 20 | r = np.asarray(r) 21 | scalar_input = False 22 | if r.ndim == 0: 23 | r = r[np.newaxis] # make 1D 24 | scalar_input = True 25 | 26 | # compute x and y values 27 | x = np.floor(ma.log2(r)) + 1.0 28 | y = (r/(2.0**(x-1.0))) - 1.0 29 | 30 | # compute the sum of x and y, filling zero 31 | # where there are masked values (should only 32 | # occur when there are zero entries) 33 | retval = (x + y).filled(0) 34 | 35 | # return scalar or array 36 | if scalar_input: 37 | return np.squeeze(retval) 38 | else: 39 | return retval 40 | 41 | def unmap_f(r): 42 | # convert input to an array 43 | # ref: https://stackoverflow.com/questions/29318459/python-function-that-handles-scalar-or-arrays 44 | r = np.asarray(r) 45 | scalar_input = False 46 | if r.ndim == 0: 47 | r = r[np.newaxis] # make 1D 48 | scalar_input = True 49 | 50 | # invert the mapping 51 | x = np.floor(r) 52 | retval = (2.0**(x-1)) * (1 + r - x) 53 | 54 | # make sure zero maps to zero 55 | retval[r==0] = 0 56 | 57 | # return scalar or array 58 | if scalar_input: 59 | return np.squeeze(retval) 60 | else: 61 | return retval 62 | 63 | # calculate domain 64 | domain = [map_f(0), map_f((1< install_hardfloat.sh 6 | source install_hardfloat.sh 7 | 8 | # get S4P data 9 | curl -L https://git.io/JtNrY > peters_01_0605_B1_thru.s4p 10 | curl -L https://git.io/JqceS > channel_resp_mar11.csv 11 | 12 | # install various python dependencies 13 | pip install wheel 14 | pip install pytest pytest-cov 15 | 16 | # install msdsl 17 | pip install -e . 18 | 19 | # install magma ecosystem 20 | # a specific version of pysmt is used to avoid cluttering the output with warnings 21 | pip install pysmt==0.9.0 22 | pip install fault==3.0.36 magma-lang==2.1.17 coreir==2.0.120 mantle==2.0.10 hwtypes==1.4.3 ast_tools==0.0.30 kratos==0.0.31.1 23 | 24 | # run tests 25 | pytest --cov-report=xml --cov=msdsl tests/ -v -r s 26 | 27 | # upload coverage information 28 | curl -s https://codecov.io/bash | bash 29 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import setup, find_packages 3 | 4 | name = 'msdsl' 5 | version = '0.3.8' 6 | 7 | DESCRIPTION = '''\ 8 | Library for generating synthesizable mixed-signal models for FPGA emulation\ 9 | ''' 10 | 11 | with open('README.md', 'r') as fh: 12 | LONG_DESCRIPTION = fh.read() 13 | 14 | install_requires = [ 15 | 'svreal>=0.2.7', 16 | 'scipy', 17 | 'numpy', 18 | 'matplotlib', 19 | 'scikit-rf', 20 | 'tqdm' 21 | ] 22 | if os.name != 'nt': 23 | install_requires.append('cvxpy') 24 | 25 | setup( 26 | name=name, 27 | version=version, 28 | description=DESCRIPTION, 29 | long_description=LONG_DESCRIPTION, 30 | long_description_content_type='text/markdown', 31 | keywords = ['analog', 'mixed-signal', 'mixed signal', 'behavioral', 32 | 'real number model', 'real number models', 'rnm', 'rnms', 33 | 'model', 'models', 'generator', 'verilog', 'system-verilog', 34 | 'system verilog', 'synthesizable', 'emulation', 'fpga'], 35 | packages=find_packages(), 36 | install_requires=install_requires, 37 | license='MIT', 38 | url=f'https://github.com/sgherbst/{name}', 39 | author='Steven Herbst', 40 | author_email='sgherbst@gmail.com', 41 | python_requires='>=3.7', 42 | download_url = f'https://github.com/sgherbst/{name}/archive/v{version}.tar.gz', 43 | classifiers=[ 44 | 'Development Status :: 3 - Alpha', 45 | 'Intended Audience :: Developers', 46 | 'Topic :: Scientific/Engineering :: Electronic Design Automation (EDA)', 47 | 'License :: OSI Approved :: MIT License', 48 | f'Programming Language :: Python :: 3.7' 49 | ], 50 | include_package_data=True, 51 | zip_safe=False 52 | ) 53 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/__init__.py -------------------------------------------------------------------------------- /tests/adc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/adc/__init__.py -------------------------------------------------------------------------------- /tests/adc/test_adc.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | import numpy as np 3 | from math import floor 4 | from pathlib import Path 5 | 6 | # AHA imports 7 | import magma as m 8 | 9 | # msdsl imports 10 | from ..common import * 11 | from msdsl import MixedSignalModel, VerilogGenerator, to_sint, clamp_op 12 | 13 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 14 | 15 | def pytest_generate_tests(metafunc): 16 | pytest_sim_params(metafunc) 17 | pytest_real_type_params(metafunc) 18 | 19 | def gen_model(n, vn, vp, dt, real_type): 20 | # declare model I/O 21 | m = MixedSignalModel('model', dt=dt, real_type=real_type) 22 | m.add_analog_input('a_in') 23 | m.add_digital_output('d_out', width=n, signed=True) 24 | 25 | # compute expression for ADC output as an unclamped, real number 26 | expr = ((m.a_in-vn)/(vp-vn) * ((2**n)-1)) - (2**(n-1)) 27 | 28 | # clamp to ADC range 29 | clamped = clamp_op(expr, -(2**(n-1)), (2**(n-1))-1) 30 | 31 | # assign expression to output 32 | m.set_this_cycle(m.d_out, to_sint(clamped, width=n)) 33 | 34 | # compile to a file 35 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 36 | model_file = BUILD_DIR / 'model.sv' 37 | m.compile_to_file(VerilogGenerator(), filename=model_file) 38 | 39 | # return file location 40 | return model_file 41 | 42 | def test_adc(simulator, real_type, n_adc=8, v_ref_n=-1.0, v_ref_p=+1.0, 43 | dt=0.1e-6): 44 | model_file = gen_model(n=n_adc, vn=v_ref_n, vp=v_ref_p, dt=dt, 45 | real_type=real_type) 46 | 47 | # declare circuit 48 | class dut(m.Circuit): 49 | name = 'test_adc' 50 | io = m.IO( 51 | a_in=fault.RealIn, 52 | d_out=m.Out(m.SInt[n_adc]) 53 | ) 54 | 55 | def model(a_in): 56 | code = ((a_in - v_ref_n) / (v_ref_p - v_ref_n)) * ((2**n_adc) - 1) 57 | code -= 2**(n_adc - 1) 58 | code = min(max(code, -(2**(n_adc-1))), (2**(n_adc-1))-1) 59 | code = floor(code) 60 | return code 61 | 62 | # create mechanism to run trials 63 | t = MsdslTester(dut) 64 | def run_trial(a_in, should_print=False): 65 | t.poke(dut.a_in, a_in) 66 | t.eval() 67 | if should_print: 68 | t.print('a_in: %0f, d_out: %0d\n', dut.a_in, dut.d_out) 69 | t.expect(dut.d_out, model(a_in)) 70 | 71 | # specify trials to be run 72 | delta = 0.1*(v_ref_p - v_ref_n) 73 | for x in np.linspace(v_ref_n - delta, v_ref_p + delta, 1000): 74 | run_trial(x) 75 | 76 | # run the simulation 77 | t.compile_and_run( 78 | directory=BUILD_DIR, 79 | simulator=simulator, 80 | ext_srcs=[model_file, get_file('adc/test_adc.sv')], 81 | parameters={'n_adc': n_adc}, 82 | real_type=real_type 83 | ) 84 | -------------------------------------------------------------------------------- /tests/adc/test_adc.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_adc #( 4 | parameter integer n_adc=8 5 | ) ( 6 | input real a_in, 7 | output signed [(n_adc-1):0] d_out 8 | ); 9 | `MAKE_REAL(a_in_int, 10); 10 | assign `FORCE_REAL(a_in, a_in_int); 11 | 12 | model #( 13 | `PASS_REAL(a_in, a_in_int) 14 | ) model_i ( 15 | .a_in(a_in_int), 16 | .d_out(d_out) 17 | ); 18 | endmodule -------------------------------------------------------------------------------- /tests/arb_noise/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/arb_noise/__init__.py -------------------------------------------------------------------------------- /tests/arb_noise/test_arb_noise.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | import numpy as np 4 | from scipy.stats import truncnorm 5 | 6 | # AHA imports 7 | import magma as m 8 | 9 | # msdsl imports 10 | from ..common import * 11 | from msdsl import MixedSignalModel, VerilogGenerator 12 | 13 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 14 | 15 | def pytest_generate_tests(metafunc): 16 | pytest_sim_params(metafunc) 17 | pytest_real_type_params(metafunc) 18 | 19 | def gen_model(mean=0.0, std=1.0, num_sigma=6.0, order=1, numel=512, 20 | real_type=RealType.FixedPoint): 21 | # create mixed-signal model 22 | model = MixedSignalModel('model', build_dir=BUILD_DIR, real_type=real_type) 23 | model.add_digital_input('clk') 24 | model.add_digital_input('rst') 25 | model.add_analog_output('real_out') 26 | 27 | # compute the inverse CDF of the distribution (truncated to 0, 1 domain) 28 | inv_cdf = lambda x: truncnorm.ppf(x, -num_sigma, +num_sigma, loc=mean, scale=std) 29 | 30 | # create the function object 31 | inv_cdf_func = model.make_function(inv_cdf, domain=[0.0, 1.0], order=order, numel=numel) 32 | 33 | model.set_this_cycle(model.real_out, model.arbitrary_noise(inv_cdf_func, clk=model.clk, rst=model.rst)) 34 | 35 | # write the model 36 | return model.compile_to_file(VerilogGenerator()) 37 | 38 | def test_arb_noise(simulator, real_type, n_trials=10000): 39 | # generate model 40 | model_file = gen_model(mean=1.23, std=0.456, real_type=real_type) 41 | 42 | # declare circuit 43 | class dut(m.Circuit): 44 | name = 'test_arb_noise' 45 | io = m.IO( 46 | clk=m.ClockIn, 47 | rst=m.BitIn, 48 | real_out=fault.RealOut 49 | ) 50 | 51 | # create the tester 52 | t = MsdslTester(dut, dut.clk) 53 | 54 | # initialize 55 | t.poke(dut.clk, 0) 56 | t.poke(dut.rst, 1) 57 | t.step(2) 58 | t.poke(dut.rst, 0) 59 | t.step(2) 60 | 61 | # print the first few outputs 62 | data = [] 63 | for _ in range(n_trials): 64 | data.append(t.get_value(dut.real_out)) 65 | t.step(2) 66 | 67 | # run the simulation 68 | t.compile_and_run( 69 | directory=BUILD_DIR, 70 | simulator=simulator, 71 | ext_srcs=[model_file, get_file('arb_noise/test_arb_noise.sv')], 72 | real_type=real_type 73 | ) 74 | 75 | # analyze the data 76 | data = np.array([elem.value for elem in data], dtype=float) 77 | mean = np.mean(data) 78 | std = np.std(data) 79 | min_val = np.min(data) 80 | max_val = np.max(data) 81 | print(f"mean: {mean}, standard dev: {std}, min: {min_val}, max: {max_val}") 82 | 83 | # uncomment to plot results 84 | # import matplotlib.pyplot as plt 85 | # plt.hist(data, bins=50) 86 | # plt.show() 87 | 88 | # check the results 89 | assert 1.1 <= mean <= 1.3, 'Mean is unexpected.' 90 | assert 0.4 <= std <= 0.5, 'Standard deviation is unexpected.' 91 | assert -1.6 <= min_val, 'Minimum value is unexpected.' 92 | assert max_val <= 4.1, 'Maximum value is unexpected.' 93 | -------------------------------------------------------------------------------- /tests/arb_noise/test_arb_noise.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_arb_noise #( 4 | parameter real real_range=10 5 | ) ( 6 | input clk, 7 | input rst, 8 | output real real_out 9 | ); 10 | `MAKE_REAL(real_out_int, real_range); 11 | assign real_out = `TO_REAL(real_out_int); 12 | 13 | model #( 14 | `PASS_REAL(real_out, real_out_int) 15 | ) model_i ( 16 | .clk(clk), 17 | .rst(rst), 18 | .real_out(real_out_int) 19 | ); 20 | endmodule -------------------------------------------------------------------------------- /tests/binding/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/binding/__init__.py -------------------------------------------------------------------------------- /tests/binding/test_binding.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | 4 | # AHA imports 5 | import magma as m 6 | 7 | # msdsl imports 8 | from ..common import * 9 | from msdsl import MixedSignalModel, VerilogGenerator 10 | 11 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 12 | 13 | def pytest_generate_tests(metafunc): 14 | pytest_sim_params(metafunc) 15 | pytest_real_type_params(metafunc) 16 | 17 | def gen_model(real_type): 18 | # declare module 19 | m = MixedSignalModel('model', real_type=real_type) 20 | m.add_analog_input('a') 21 | m.add_analog_input('b') 22 | 23 | # bind expression to internal signal 24 | m.bind_name('c', m.a + m.b) 25 | 26 | # compile to a file 27 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 28 | model_file = BUILD_DIR / 'model.sv' 29 | m.compile_to_file(VerilogGenerator(), filename=model_file) 30 | 31 | # return file location 32 | return model_file 33 | 34 | def test_binding(simulator, real_type): 35 | model_file = gen_model(real_type=real_type) 36 | 37 | # declare circuit 38 | class dut(m.Circuit): 39 | name = 'test_binding' 40 | io = m.IO( 41 | a=fault.RealIn, 42 | b=fault.RealIn, 43 | c=fault.RealOut 44 | ) 45 | 46 | t = MsdslTester(dut) 47 | 48 | def run_trial(a, b, should_print=True): 49 | t.poke(dut.a, a) 50 | t.poke(dut.b, b) 51 | t.eval() 52 | if should_print: 53 | t.print('a: %0f, b: %0f, c: %0f\n', dut.a, dut.b, dut.c) 54 | t.expect(dut.c, a+b, abs_tol=1e-3) 55 | 56 | # record tests 57 | run_trial(1.23, 2.34) 58 | run_trial(-3.45, 4.56) 59 | run_trial(5.67, -7.89) 60 | 61 | # run the simulation 62 | t.compile_and_run( 63 | directory=BUILD_DIR, 64 | simulator=simulator, 65 | ext_srcs=[model_file, get_file('binding/test_binding.sv')], 66 | real_type=real_type 67 | ) 68 | -------------------------------------------------------------------------------- /tests/binding/test_binding.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_binding ( 4 | input real a, 5 | input real b, 6 | output real c 7 | ); 8 | `MAKE_REAL(a_int, 10); 9 | assign `FORCE_REAL(a, a_int); 10 | 11 | `MAKE_REAL(b_int, 100); 12 | assign `FORCE_REAL(b, b_int); 13 | 14 | model #( 15 | `PASS_REAL(a, a_int), 16 | `PASS_REAL(b, b_int) 17 | ) model_i ( 18 | .a(a_int), 19 | .b(b_int) 20 | ); 21 | 22 | // wire internal node to output of fault module 23 | real c_int; 24 | assign c = c_int; 25 | always @(model_i.c) c_int = `TO_REAL(model_i.c); 26 | endmodule -------------------------------------------------------------------------------- /tests/chan_interp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/chan_interp/__init__.py -------------------------------------------------------------------------------- /tests/chan_interp/test_chan_interp.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | `define MAKE_OUTPUT(k) output real out_``k`` 4 | `define PASS_OUTPUT(k) `PASS_REAL(out_``k``, out_``k``_int) 5 | `define CONNECT_OUTPUT(k) .out_``k``(out_``k``_int) 6 | 7 | `define WIRE_OUTPUT(k) \ 8 | `MAKE_REAL(out_``k``_int, out_range); \ 9 | assign out_``k`` = `TO_REAL(out_``k``_int) 10 | 11 | `define REPLICATE_MACRO_SEMICOLON(name) \ 12 | `name(0); \ 13 | `name(1); \ 14 | `name(2); \ 15 | `name(3) 16 | 17 | `define REPLICATE_MACRO_COMMA(name) \ 18 | `name(0), \ 19 | `name(1), \ 20 | `name(2), \ 21 | `name(3) 22 | 23 | module test_chan_interp #( 24 | parameter real in_range=10, 25 | parameter real out_range=10 26 | ) ( 27 | input real dt, 28 | input real in_, 29 | `REPLICATE_MACRO_COMMA(MAKE_OUTPUT), 30 | input clk, 31 | input rst 32 | ); 33 | // wire input 34 | `MAKE_REAL(in_int, in_range); 35 | assign `FORCE_REAL(in_, in_int); 36 | 37 | // wire outputs 38 | `REPLICATE_MACRO_SEMICOLON(WIRE_OUTPUT); 39 | 40 | // wire dt 41 | `MAKE_REAL(dt_int, 1e-9); 42 | assign `FORCE_REAL(dt, dt_int); 43 | 44 | // instantiate model 45 | model #( 46 | `PASS_REAL(dt, dt_int), 47 | `PASS_REAL(in_, in_int), 48 | `REPLICATE_MACRO_COMMA(PASS_OUTPUT) 49 | ) model_i ( 50 | .dt(dt_int), 51 | .in_(in_int), 52 | `REPLICATE_MACRO_COMMA(CONNECT_OUTPUT), 53 | .clk(clk), 54 | .rst(rst) 55 | ); 56 | endmodule -------------------------------------------------------------------------------- /tests/circuit_amp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/circuit_amp/__init__.py -------------------------------------------------------------------------------- /tests/circuit_amp/test_circuit_amp.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | 4 | # AHA imports 5 | import magma as m 6 | 7 | # msdsl imports 8 | from ..common import * 9 | from msdsl import MixedSignalModel, VerilogGenerator, AnalogSignal 10 | 11 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 12 | 13 | def pytest_generate_tests(metafunc): 14 | pytest_sim_params(metafunc) 15 | pytest_real_type_params(metafunc) 16 | 17 | def gen_model(real_type, gain=123, dt=0.1e-6): 18 | # declare model 19 | m = MixedSignalModel('model', dt=dt, real_type=real_type) 20 | 21 | # declare I/O 22 | m.add_analog_input('v_in') 23 | m.add_analog_output('v_out') 24 | 25 | # declare buffer circuit using negative feedback 26 | c = m.make_circuit() 27 | gnd = c.make_ground() 28 | c.voltage('net_v_in', gnd, m.v_in) 29 | c.vcvs('net_v_in', 'net_v_out', 'net_v_out', gnd, gain) 30 | c.add_eqns( 31 | AnalogSignal('net_v_out') == m.v_out 32 | ) 33 | 34 | # compile to a file 35 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 36 | model_file = BUILD_DIR / 'model.sv' 37 | m.compile_to_file(VerilogGenerator(), filename=model_file) 38 | 39 | # return file location 40 | return model_file 41 | 42 | def test_amp(simulator, real_type, gain=123): 43 | model_file = gen_model(gain=gain, real_type=real_type) 44 | 45 | # declare circuit 46 | class dut(m.Circuit): 47 | name = 'test_circuit_amp' 48 | io = m.IO( 49 | v_in=fault.RealIn, 50 | v_out=fault.RealOut 51 | ) 52 | 53 | t = MsdslTester(dut) 54 | 55 | def model(v_in, gain=gain): 56 | return v_in * gain / (gain + 1) 57 | 58 | def run_trial(v_in, should_print=True): 59 | t.poke(dut.v_in, v_in) 60 | t.eval() 61 | if should_print: 62 | t.print('v_in: %0f, v_out: %0f\n', dut.v_in, dut.v_out) 63 | t.expect(dut.v_out, model(v_in), abs_tol=1e-3) 64 | 65 | # record tests 66 | run_trial(0.1) 67 | run_trial(0.2) 68 | run_trial(-0.1) 69 | run_trial(-0.2) 70 | 71 | # run the simulation 72 | t.compile_and_run( 73 | directory=BUILD_DIR, 74 | simulator=simulator, 75 | ext_srcs=[model_file, get_file('circuit_amp/test_circuit_amp.sv')], 76 | real_type=real_type 77 | ) 78 | -------------------------------------------------------------------------------- /tests/circuit_amp/test_circuit_amp.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_circuit_amp ( 4 | input real v_in, 5 | output real v_out 6 | ); 7 | `MAKE_REAL(v_in_int, 5); 8 | assign `FORCE_REAL(v_in, v_in_int); 9 | 10 | `MAKE_REAL(v_out_int, 5); 11 | assign v_out = `TO_REAL(v_out_int); 12 | 13 | model #( 14 | `PASS_REAL(v_in, v_in_int), 15 | `PASS_REAL(v_out, v_out_int) 16 | ) model_i ( 17 | .v_in(v_in_int), 18 | .v_out(v_out_int) 19 | ); 20 | endmodule 21 | -------------------------------------------------------------------------------- /tests/circuit_ind_sw/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/circuit_ind_sw/__init__.py -------------------------------------------------------------------------------- /tests/circuit_ind_sw/test_circuit_ind_sw.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | 4 | # AHA imports 5 | import magma as m 6 | 7 | # msdsl imports 8 | from ..common import * 9 | from msdsl import MixedSignalModel, VerilogGenerator, AnalogSignal 10 | 11 | NAME = '_'.join(Path(__file__).stem.split('_')[1:]) 12 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 13 | 14 | def pytest_generate_tests(metafunc): 15 | pytest_sim_params(metafunc) 16 | pytest_real_type_params(metafunc) 17 | 18 | # TODO: fix bug in which this test fails with r_off greater than or equal to 2.5e3 19 | # The timestep doesn't really affect this particular bug, nor does the inductance, 20 | # or the "ON" resistance of the switch. Instead, the problem is related to the 21 | # product of "r_off" and "current_range". If both switches are off, and the 22 | # current through the inductor is actually at the specified limit, the output 23 | # voltage could be enormous. But this is unlikely to happen, so we need some 24 | # way to (1) clamp internally to more reasonable values, and (2) detect when 25 | # this problem is likely to occur. 26 | def gen_model(r_off=2.5e3, current_range=100, real_type=RealType.FixedPoint): 27 | # declare model 28 | m = MixedSignalModel('model', dt=1e-9, real_type=real_type) 29 | m.add_analog_input('v_in') 30 | m.add_analog_output('v_out') 31 | m.add_digital_input('sw1') 32 | m.add_digital_input('sw2') 33 | m.add_digital_input('clk') 34 | m.add_digital_input('rst') 35 | 36 | # create test circuit 37 | c = m.make_circuit(clk=m.clk, rst=m.rst) 38 | gnd = c.make_ground() 39 | c.voltage('net_v_in', gnd, m.v_in) 40 | c.switch('net_v_in', 'net_v_x', m.sw1, r_off=r_off) 41 | c.switch('net_v_x', gnd, m.sw2, r_off=r_off) 42 | c.inductor('net_v_in', 'net_v_x', 1.0, current_range=current_range) 43 | c.add_eqns(AnalogSignal('net_v_x') == m.v_out) 44 | 45 | # compile to a file 46 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 47 | model_file = BUILD_DIR / 'model.sv' 48 | m.compile_to_file(VerilogGenerator(), filename=model_file) 49 | 50 | # return file location 51 | return model_file 52 | 53 | def test_circuit_ind_sw(simulator, real_type): 54 | model_file = gen_model(real_type=real_type) 55 | 56 | # declare circuit 57 | class dut(m.Circuit): 58 | name = f'test_{NAME}' 59 | io = m.IO( 60 | v_in=fault.RealIn, 61 | v_out=fault.RealOut, 62 | sw1=m.BitIn, 63 | sw2=m.BitIn, 64 | clk=m.ClockIn, 65 | rst=m.BitIn 66 | ) 67 | 68 | t = MsdslTester(dut, dut.clk) 69 | 70 | # initialize 71 | t.poke(dut.v_in, 0.0) 72 | t.poke(dut.sw1, 0) 73 | t.poke(dut.sw2, 0) 74 | t.poke(dut.clk, 0) 75 | t.poke(dut.rst, 1) 76 | 77 | def model(v_in, sw1, sw2): 78 | if sw1 == 0 and sw2 == 0: 79 | return 0.5*v_in 80 | elif sw1 == 0 and sw2 == 1: 81 | return 0.0 82 | elif sw1 == 1 and sw2 == 0: 83 | return v_in 84 | elif sw1 == 1 and sw2 == 1: 85 | return 0.5*v_in 86 | else: 87 | raise Exception(f'Invalid switch values: sw1={sw1}, sw2={sw2}.') 88 | 89 | def run_trial(v_in, sw1, sw2, should_print=True): 90 | t.poke(dut.v_in, v_in) 91 | t.poke(dut.sw1, sw1) 92 | t.poke(dut.sw2, sw2) 93 | t.step(2) 94 | if should_print: 95 | t.print('v_in: %0f, sw1: %0d, sw2: %0d, v_out: %0f\n', 96 | dut.v_in, dut.sw1, dut.sw2, dut.v_out) 97 | t.expect(dut.v_out, model(v_in, sw1, sw2), abs_tol=1e-2) 98 | 99 | # record tests 100 | v_in = 1.23 101 | run_trial(v_in, 0, 0) 102 | run_trial(v_in, 0, 1) 103 | run_trial(v_in, 1, 0) 104 | run_trial(v_in, 1, 1) 105 | 106 | # run the simulation 107 | t.compile_and_run( 108 | directory=BUILD_DIR, 109 | simulator=simulator, 110 | ext_srcs=[model_file, get_file(f'{NAME}/test_{NAME}.sv')], 111 | real_type=real_type 112 | ) 113 | -------------------------------------------------------------------------------- /tests/circuit_ind_sw/test_circuit_ind_sw.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_circuit_ind_sw ( 4 | input real v_in, 5 | output real v_out, 6 | input sw1, 7 | input sw2, 8 | input clk, 9 | input rst 10 | ); 11 | `MAKE_REAL(v_in_int, 5); 12 | assign `FORCE_REAL(v_in, v_in_int); 13 | 14 | `MAKE_REAL(v_out_int, 5); 15 | assign v_out = `TO_REAL(v_out_int); 16 | 17 | model #( 18 | `PASS_REAL(v_in, v_in_int), 19 | `PASS_REAL(v_out, v_out_int) 20 | ) model_i ( 21 | .v_in(v_in_int), 22 | .v_out(v_out_int), 23 | .sw1(sw1), 24 | .sw2(sw2), 25 | .clk(clk), 26 | .rst(rst) 27 | ); 28 | endmodule -------------------------------------------------------------------------------- /tests/circuit_rc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/circuit_rc/__init__.py -------------------------------------------------------------------------------- /tests/circuit_rc/test_circuit_rc.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from math import exp 3 | from pathlib import Path 4 | 5 | # AHA imports 6 | import magma as m 7 | 8 | # msdsl imports 9 | from ..common import * 10 | from msdsl import MixedSignalModel, VerilogGenerator, RangeOf, AnalogSignal 11 | 12 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 13 | 14 | def pytest_generate_tests(metafunc): 15 | pytest_sim_params(metafunc) 16 | pytest_real_type_params(metafunc) 17 | 18 | def gen_model(res=1e3, cap=1e-9, dt=0.1e-6, real_type=RealType.FixedPoint): 19 | m = MixedSignalModel('model', dt=dt, real_type=real_type) 20 | m.add_analog_input('v_in') 21 | m.add_analog_output('v_out') 22 | m.add_digital_input('clk') 23 | m.add_digital_input('rst') 24 | 25 | c = m.make_circuit(clk=m.clk, rst=m.rst) 26 | gnd = c.make_ground() 27 | 28 | c.capacitor('net_v_out', gnd, cap, voltage_range=RangeOf(m.v_out)) 29 | c.resistor('net_v_in', 'net_v_out', res) 30 | c.voltage('net_v_in', gnd, m.v_in) 31 | 32 | c.add_eqns( 33 | AnalogSignal('net_v_out') == m.v_out 34 | ) 35 | 36 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 37 | model_file = BUILD_DIR / 'model.sv' 38 | m.compile_to_file(VerilogGenerator(), filename=model_file) 39 | 40 | return model_file 41 | 42 | def test_circuit_rc(simulator, real_type, res=1e3, cap=1e-9, dt=0.1e-6): 43 | model_file = gen_model(res=res, cap=cap, dt=dt, real_type=real_type) 44 | 45 | # declare circuit 46 | class dut(m.Circuit): 47 | name = 'test_circuit_rc' 48 | io = m.IO( 49 | v_in=fault.RealIn, 50 | v_out=fault.RealOut, 51 | clk=m.ClockIn, 52 | rst=m.BitIn 53 | ) 54 | 55 | # create the tester 56 | tester = MsdslTester(dut, dut.clk) 57 | 58 | # initialize 59 | v_in = 1.0 60 | tester.poke(dut.clk, 0) 61 | tester.poke(dut.rst, 1) 62 | tester.poke(dut.v_in, v_in) 63 | tester.eval() 64 | 65 | # reset 66 | tester.step(2) 67 | 68 | # model for circuit behavior 69 | def model(t): 70 | return v_in*(1-exp(-t/(res*cap))) 71 | 72 | # print the first few outputs 73 | tester.poke(dut.rst, 0) 74 | for k in range(20): 75 | tester.expect(dut.v_out, model(k*dt), abs_tol=0.025) 76 | tester.print("v_out: %0f\n", dut.v_out) 77 | tester.step(2) 78 | 79 | # run the simulation 80 | tester.compile_and_run( 81 | directory=BUILD_DIR, 82 | simulator=simulator, 83 | ext_srcs=[model_file, get_file('circuit_rc/test_circuit_rc.sv')], 84 | real_type=real_type 85 | ) 86 | -------------------------------------------------------------------------------- /tests/circuit_rc/test_circuit_rc.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_circuit_rc( 4 | input real v_in, 5 | output real v_out, 6 | input clk, 7 | input rst 8 | ); 9 | `MAKE_REAL(v_in_int, 10); 10 | assign `FORCE_REAL(v_in, v_in_int); 11 | 12 | `MAKE_REAL(v_out_int, 10); 13 | assign v_out = `TO_REAL(v_out_int); 14 | 15 | model #( 16 | `PASS_REAL(v_in, v_in_int), 17 | `PASS_REAL(v_out, v_out_int) 18 | ) model_i ( 19 | .v_in(v_in_int), 20 | .v_out(v_out_int), 21 | .clk(clk), 22 | .rst(rst) 23 | ); 24 | endmodule -------------------------------------------------------------------------------- /tests/circuit_sw/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/circuit_sw/__init__.py -------------------------------------------------------------------------------- /tests/circuit_sw/test_circuit_sw.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | 4 | # AHA imports 5 | import magma as m 6 | 7 | # msdsl imports 8 | from ..common import * 9 | from msdsl import MixedSignalModel, VerilogGenerator, AnalogSignal 10 | 11 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 12 | 13 | def pytest_generate_tests(metafunc): 14 | pytest_sim_params(metafunc) 15 | pytest_real_type_params(metafunc) 16 | 17 | def gen_model(rp1, rn1, rp2, rn2, real_type, dt=0.1e-6): 18 | # declare model 19 | m = MixedSignalModel('model', dt=dt, real_type=real_type) 20 | 21 | # declare I/O 22 | m.add_analog_input('v_in') 23 | m.add_analog_output('v_out') 24 | m.add_digital_input('sw1') 25 | m.add_digital_input('sw2') 26 | 27 | # declare switch circuit 28 | c = m.make_circuit() 29 | gnd = c.make_ground() 30 | c.voltage('net_v_in', gnd, m.v_in) 31 | c.switch('net_v_in', 'net_v_x', m.sw1, r_on=rp1, r_off=rn1) 32 | c.switch('net_v_x', gnd, m.sw2, r_on=rp2, r_off=rn2) 33 | c.add_eqns( 34 | AnalogSignal('net_v_x') == m.v_out 35 | ) 36 | 37 | # compile to a file 38 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 39 | model_file = BUILD_DIR / 'model.sv' 40 | m.compile_to_file(VerilogGenerator(), filename=model_file) 41 | 42 | # return file location 43 | return model_file 44 | 45 | def test_binding(simulator, real_type, rp1=1.0, rn1=2.0, rp2=3.0, rn2=4.0): 46 | model_file = gen_model(rp1=rp1, rn1=rn1, rp2=rp2, rn2=rn2, 47 | real_type=real_type) 48 | 49 | # declare circuit 50 | class dut(m.Circuit): 51 | name = 'test_circuit_sw' 52 | io = m.IO( 53 | v_in=fault.RealIn, 54 | v_out=fault.RealOut, 55 | sw1=m.BitIn, 56 | sw2=m.BitIn 57 | ) 58 | 59 | t = MsdslTester(dut) 60 | 61 | def model(v_in, sw1, sw2): 62 | r_up = rp1 if sw1==1 else rn1 63 | r_dn = rp2 if sw2==1 else rn2 64 | return v_in * r_dn / (r_up + r_dn) 65 | 66 | def run_trial(v_in, sw1, sw2, should_print=True): 67 | t.poke(dut.v_in, v_in) 68 | t.poke(dut.sw1, sw1) 69 | t.poke(dut.sw2, sw2) 70 | t.eval() 71 | if should_print: 72 | t.print('v_in: %0f, sw1: %0d, sw2: %0d, v_out: %0f\n', 73 | dut.v_in, dut.sw1, dut.sw2, dut.v_out) 74 | t.expect(dut.v_out, model(v_in, sw1, sw2), abs_tol=1e-3) 75 | 76 | # record tests 77 | v_in = 1.23 78 | run_trial(v_in, 0, 0) 79 | run_trial(v_in, 0, 1) 80 | run_trial(v_in, 1, 0) 81 | run_trial(v_in, 1, 1) 82 | 83 | # run the simulation 84 | t.compile_and_run( 85 | directory=BUILD_DIR, 86 | simulator=simulator, 87 | ext_srcs=[model_file, get_file('circuit_sw/test_circuit_sw.sv')], 88 | real_type=real_type 89 | ) 90 | -------------------------------------------------------------------------------- /tests/circuit_sw/test_circuit_sw.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_circuit_sw ( 4 | input real v_in, 5 | output real v_out, 6 | input sw1, 7 | input sw2 8 | ); 9 | `MAKE_REAL(v_in_int, 5); 10 | assign `FORCE_REAL(v_in, v_in_int); 11 | 12 | `MAKE_REAL(v_out_int, 5); 13 | assign v_out = `TO_REAL(v_out_int); 14 | 15 | model #( 16 | `PASS_REAL(v_in, v_in_int), 17 | `PASS_REAL(v_out, v_out_int) 18 | ) model_i ( 19 | .v_in(v_in_int), 20 | .v_out(v_out_int), 21 | .sw1(sw1), 22 | .sw2(sw2) 23 | ); 24 | endmodule -------------------------------------------------------------------------------- /tests/common.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | from shutil import which 3 | 4 | import fault 5 | from msdsl import get_msdsl_header 6 | from svreal import * 7 | 8 | TEST_DIR = Path(__file__).resolve().parent 9 | 10 | def get_file(path): 11 | return Path(TEST_DIR, path) 12 | 13 | def get_dir(path): 14 | # alias for get_file 15 | return get_file(path) 16 | 17 | def get_files(*args): 18 | return [get_file(path) for path in args] 19 | 20 | def get_dirs(*args): 21 | # alias for get_files 22 | return get_files(*args) 23 | 24 | def pytest_sim_params(metafunc, simulators=None): 25 | if simulators is None: 26 | simulators = ['vcs', 'vivado', 'ncsim', 'iverilog'] 27 | 28 | # parameterize with the simulators available 29 | if 'simulator' in metafunc.fixturenames: 30 | targets = [] 31 | for simulator in simulators: 32 | if which(simulator): 33 | targets.append(simulator) 34 | 35 | metafunc.parametrize('simulator', targets) 36 | 37 | def pytest_real_type_params(metafunc, real_types=None): 38 | if real_types is None: 39 | real_types = [RealType.FixedPoint, RealType.FloatReal, RealType.HardFloat] 40 | 41 | if 'real_type' in metafunc.fixturenames: 42 | metafunc.parametrize('real_type', real_types) 43 | 44 | def pytest_func_mode_params(metafunc, func_modes=None): 45 | if func_modes is None: 46 | func_modes = ['sync', 'async'] 47 | 48 | if 'func_mode' in metafunc.fixturenames: 49 | metafunc.parametrize('func_mode', func_modes) 50 | 51 | class MsdslTester(fault.Tester): 52 | def __init__(self, circuit, clock=None, expect_strict_default=True, debug_mode=True): 53 | super().__init__(circuit=circuit, clock=clock, 54 | expect_strict_default=expect_strict_default) 55 | self.debug_mode = debug_mode 56 | 57 | def compile_and_run(self, target='system-verilog', ext_srcs=None, 58 | inc_dirs=None, ext_model_file=True, tmp_dir=None, 59 | disp_type=None, real_type=RealType.FixedPoint, 60 | defines=None, **kwargs): 61 | # set defaults 62 | if ext_srcs is None: 63 | ext_srcs = [] 64 | if inc_dirs is None: 65 | inc_dirs = [] 66 | if tmp_dir is None: 67 | tmp_dir = not self.debug_mode 68 | if disp_type is None: 69 | disp_type = 'on_error' if (not self.debug_mode) else 'realtime' 70 | if defines is None: 71 | defines = {} 72 | 73 | # add to ext_srcs 74 | if real_type == RealType.HardFloat: 75 | ext_srcs = get_hard_float_sources() + ext_srcs 76 | 77 | # add to inc_dirs 78 | inc_dirs = [get_svreal_header().parent, get_msdsl_header().parent] + inc_dirs 79 | if real_type == RealType.HardFloat: 80 | inc_dirs = get_hard_float_inc_dirs() + inc_dirs 81 | 82 | # add defines as needed for the real number type 83 | defines = defines.copy() 84 | if real_type == RealType.FixedPoint: 85 | pass 86 | elif real_type == RealType.FloatReal: 87 | defines['FLOAT_REAL'] = None 88 | elif real_type == RealType.HardFloat: 89 | defines['HARD_FLOAT'] = None 90 | 91 | # call the command 92 | super().compile_and_run( 93 | target='system-verilog', 94 | ext_srcs=ext_srcs, 95 | inc_dirs=inc_dirs, 96 | defines=defines, 97 | ext_model_file=ext_model_file, 98 | tmp_dir=tmp_dir, 99 | disp_type=disp_type, 100 | **kwargs 101 | ) 102 | -------------------------------------------------------------------------------- /tests/comparator/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/comparator/__init__.py -------------------------------------------------------------------------------- /tests/comparator/test_comparator.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | 4 | # AHA imports 5 | import magma as m 6 | 7 | # msdsl imports 8 | from ..common import * 9 | from msdsl import MixedSignalModel, VerilogGenerator, get_msdsl_header 10 | 11 | NAME = Path(__file__).stem.split('_')[1] 12 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 13 | 14 | def pytest_generate_tests(metafunc): 15 | pytest_sim_params(metafunc) 16 | pytest_real_type_params(metafunc) 17 | 18 | def gen_model(real_type): 19 | # declare module 20 | m = MixedSignalModel('model', real_type=real_type) 21 | m.add_analog_input('a') 22 | m.add_analog_input('b') 23 | m.add_digital_output('c') 24 | 25 | # bind expression to internal signal 26 | m.set_this_cycle(m.c, m.a > m.b) 27 | 28 | # compile to a file 29 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 30 | model_file = BUILD_DIR / 'model.sv' 31 | m.compile_to_file(VerilogGenerator(), filename=model_file) 32 | 33 | # return file location 34 | return model_file 35 | 36 | def test_comparator(simulator, real_type): 37 | model_file = gen_model(real_type=real_type) 38 | 39 | # declare circuit 40 | class dut(m.Circuit): 41 | name = f'test_{NAME}' 42 | io = m.IO( 43 | a=fault.RealIn, 44 | b=fault.RealIn, 45 | c=m.BitOut 46 | ) 47 | 48 | t = MsdslTester(dut) 49 | 50 | def run_trial(a, b, should_print=True): 51 | t.poke(dut.a, a) 52 | t.poke(dut.b, b) 53 | t.eval() 54 | if should_print: 55 | t.print('a: %0f, b: %0f, c: %0d\n', dut.a, dut.b, dut.c) 56 | t.expect(dut.c, a > b) 57 | 58 | # record tests 59 | run_trial(1.23, 2.34) 60 | run_trial(-1.23, 2.34) 61 | run_trial(1.23, -2.34) 62 | run_trial(-1.23, -2.34) 63 | 64 | # run the simulation 65 | t.compile_and_run( 66 | directory=BUILD_DIR, 67 | simulator=simulator, 68 | ext_srcs=[model_file, get_file(f'{NAME}/test_{NAME}.sv')], 69 | real_type=real_type 70 | ) 71 | -------------------------------------------------------------------------------- /tests/comparator/test_comparator.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_comparator ( 4 | input real a, 5 | input real b, 6 | output c 7 | ); 8 | `MAKE_REAL(a_int, 10); 9 | assign `FORCE_REAL(a, a_int); 10 | 11 | `MAKE_REAL(b_int, 100); 12 | assign `FORCE_REAL(b, b_int); 13 | 14 | model #( 15 | `PASS_REAL(a, a_int), 16 | `PASS_REAL(b, b_int) 17 | ) model_i ( 18 | .a(a_int), 19 | .b(b_int), 20 | .c(c) 21 | ); 22 | endmodule -------------------------------------------------------------------------------- /tests/ctle_interp/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/ctle_interp/__init__.py -------------------------------------------------------------------------------- /tests/ctle_interp/test_ctle_interp.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | `define MAKE_INPUT(k) input real in_``k`` 4 | `define MAKE_OUTPUT(k) output real out_``k`` 5 | 6 | `define PASS_INPUT(k) `PASS_REAL(in_``k``, in_``k``_int) 7 | `define PASS_OUTPUT(k) `PASS_REAL(out_``k``, out_``k``_int) 8 | 9 | `define CONNECT_INPUT(k) .in_``k``(in_``k``_int) 10 | `define CONNECT_OUTPUT(k) .out_``k``(out_``k``_int) 11 | 12 | `define WIRE_INPUT(k) \ 13 | `MAKE_REAL(in_``k``_int, in_range); \ 14 | assign `FORCE_REAL(in_``k``, in_``k``_int) 15 | 16 | `define WIRE_OUTPUT(k) \ 17 | `MAKE_REAL(out_``k``_int, out_range); \ 18 | assign out_``k`` = `TO_REAL(out_``k``_int) 19 | 20 | `define REPLICATE_MACRO_SEMICOLON(name) \ 21 | `name(0); \ 22 | `name(1); \ 23 | `name(2); \ 24 | `name(3) 25 | 26 | `define REPLICATE_MACRO_COMMA(name) \ 27 | `name(0), \ 28 | `name(1), \ 29 | `name(2), \ 30 | `name(3) 31 | 32 | module test_ctle_interp #( 33 | parameter real in_range=10, 34 | parameter real out_range=10 35 | ) ( 36 | input real dt, 37 | `REPLICATE_MACRO_COMMA(MAKE_INPUT), 38 | `REPLICATE_MACRO_COMMA(MAKE_OUTPUT), 39 | input clk, 40 | input rst 41 | ); 42 | // wire inputs 43 | `REPLICATE_MACRO_SEMICOLON(WIRE_INPUT); 44 | 45 | // wire outputs 46 | `REPLICATE_MACRO_SEMICOLON(WIRE_OUTPUT); 47 | 48 | // wire dt 49 | `MAKE_REAL(dt_int, 1.2); 50 | assign `FORCE_REAL(dt, dt_int); 51 | 52 | // instantiate model 53 | model #( 54 | `PASS_REAL(dt, dt_int), 55 | `REPLICATE_MACRO_COMMA(PASS_INPUT), 56 | `REPLICATE_MACRO_COMMA(PASS_OUTPUT) 57 | ) model_i ( 58 | .dt(dt_int), 59 | `REPLICATE_MACRO_COMMA(CONNECT_INPUT), 60 | `REPLICATE_MACRO_COMMA(CONNECT_OUTPUT), 61 | .clk(clk), 62 | .rst(rst) 63 | ); 64 | endmodule -------------------------------------------------------------------------------- /tests/ctle_interp2/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/ctle_interp2/__init__.py -------------------------------------------------------------------------------- /tests/ctle_interp2/test_ctle_interp2.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | `define MAKE_INPUT(k) input real in_``k`` 4 | `define MAKE_OUTPUT(k) output real out_``k`` 5 | 6 | `define PASS_INPUT(k) `PASS_REAL(in_``k``, in_``k``_int) 7 | `define PASS_OUTPUT(k) `PASS_REAL(out_``k``, out_``k``_int) 8 | 9 | `define CONNECT_INPUT(k) .in_``k``(in_``k``_int) 10 | `define CONNECT_OUTPUT(k) .out_``k``(out_``k``_int) 11 | 12 | `define WIRE_INPUT(k) \ 13 | `MAKE_REAL(in_``k``_int, in_range); \ 14 | assign `FORCE_REAL(in_``k``, in_``k``_int) 15 | 16 | `define WIRE_OUTPUT(k) \ 17 | `MAKE_REAL(out_``k``_int, out_range); \ 18 | assign out_``k`` = `TO_REAL(out_``k``_int) 19 | 20 | `define REPLICATE_MACRO_SEMICOLON(name) \ 21 | `name(0); \ 22 | `name(1); \ 23 | `name(2); \ 24 | `name(3) 25 | 26 | `define REPLICATE_MACRO_COMMA(name) \ 27 | `name(0), \ 28 | `name(1), \ 29 | `name(2), \ 30 | `name(3) 31 | 32 | module test_ctle_interp2 #( 33 | parameter real in_range=1.5, 34 | parameter real out_range=1.5 35 | ) ( 36 | input real dt, 37 | `REPLICATE_MACRO_COMMA(MAKE_INPUT), 38 | `REPLICATE_MACRO_COMMA(MAKE_OUTPUT), 39 | input clk, 40 | input rst 41 | ); 42 | // wire inputs 43 | `REPLICATE_MACRO_SEMICOLON(WIRE_INPUT); 44 | 45 | // wire outputs 46 | `REPLICATE_MACRO_SEMICOLON(WIRE_OUTPUT); 47 | 48 | // wire dt 49 | `MAKE_REAL(dt_int, 1.1); 50 | assign `FORCE_REAL(dt, dt_int); 51 | 52 | // instantiate model 53 | model #( 54 | `PASS_REAL(dt, dt_int), 55 | `REPLICATE_MACRO_COMMA(PASS_INPUT), 56 | `REPLICATE_MACRO_COMMA(PASS_OUTPUT) 57 | ) model_i ( 58 | .dt(dt_int), 59 | `REPLICATE_MACRO_COMMA(CONNECT_INPUT), 60 | `REPLICATE_MACRO_COMMA(CONNECT_OUTPUT), 61 | .clk(clk), 62 | .rst(rst) 63 | ); 64 | endmodule -------------------------------------------------------------------------------- /tests/dac/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/dac/__init__.py -------------------------------------------------------------------------------- /tests/dac/test_dac.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | 4 | # AHA imports 5 | import magma as m 6 | 7 | # msdsl imports 8 | from ..common import * 9 | from msdsl import MixedSignalModel, VerilogGenerator 10 | 11 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 12 | 13 | def pytest_generate_tests(metafunc): 14 | pytest_sim_params(metafunc) 15 | pytest_real_type_params(metafunc) 16 | 17 | def gen_model(n, vn, vp, dt, real_type): 18 | # declare model I/O 19 | m = MixedSignalModel('model', dt=dt, real_type=real_type) 20 | m.add_digital_input('d_in', width=n, signed=True) 21 | m.add_analog_output('a_out') 22 | 23 | # compute expression for DAC output 24 | expr = ((m.d_in + (2**(n-1)))/((2**n)-1))*(vp-vn) + vn 25 | 26 | # assign expression to output 27 | m.set_this_cycle(m.a_out, expr) 28 | 29 | # compile to a file 30 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 31 | model_file = BUILD_DIR / 'model.sv' 32 | m.compile_to_file(VerilogGenerator(), filename=model_file) 33 | 34 | # return file location 35 | return model_file 36 | 37 | def test_adc(simulator, real_type, n_dac=8, v_ref_n=-1.0, 38 | v_ref_p=+1.0, dt=0.1e-6): 39 | model_file = gen_model(n=n_dac, vn=v_ref_n, vp=v_ref_p, 40 | dt=dt, real_type=real_type) 41 | 42 | # declare circuit 43 | class dut(m.Circuit): 44 | name = 'test_dac' 45 | io = m.IO( 46 | d_in=m.In(m.SInt[n_dac]), 47 | a_out=fault.RealOut 48 | ) 49 | 50 | def model(d_in): 51 | # scale code to real number from 0 to 1 52 | out = (d_in + (2**(n_dac-1))) / ((2**n_dac)-1) 53 | 54 | # apply scaling and offset 55 | out *= (v_ref_p - v_ref_n) 56 | out += v_ref_n 57 | 58 | # return output 59 | return out 60 | 61 | # create mechanism to run trials 62 | t = MsdslTester(dut) 63 | def run_trial(d_in, should_print=False): 64 | t.poke(dut.d_in, d_in) 65 | t.eval() 66 | if should_print: 67 | t.print('d_in: %0d, a_out: %0f\n', dut.d_in, dut.a_out) 68 | t.expect(dut.a_out, model(d_in), abs_tol=1e-3) 69 | 70 | # determine tolerance 71 | for k in range(-(2**(n_dac-1)), (2**(n_dac-1)) - 1): 72 | run_trial(k) 73 | 74 | # run the simulation 75 | t.compile_and_run( 76 | directory=BUILD_DIR, 77 | simulator=simulator, 78 | ext_srcs=[model_file, get_file('dac/test_dac.sv')], 79 | parameters={'n_dac': n_dac}, 80 | real_type=real_type 81 | ) 82 | -------------------------------------------------------------------------------- /tests/dac/test_dac.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_dac #( 4 | parameter integer n_dac=8 5 | ) ( 6 | input signed [(n_dac-1):0] d_in, 7 | output real a_out 8 | ); 9 | `MAKE_REAL(a_out_int, 10); 10 | assign a_out = `TO_REAL(a_out_int); 11 | 12 | model #( 13 | `PASS_REAL(a_out, a_out_int) 14 | ) model_i ( 15 | .d_in(d_in), 16 | .a_out(a_out_int) 17 | ); 18 | endmodule -------------------------------------------------------------------------------- /tests/det/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/det/__init__.py -------------------------------------------------------------------------------- /tests/det/test_det.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | 4 | # AHA imports 5 | import magma as m 6 | 7 | # msdsl imports 8 | from ..common import * 9 | from msdsl import MixedSignalModel, VerilogGenerator, eqn_case, Deriv 10 | 11 | NAME = Path(__file__).stem.split('_')[1] 12 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 13 | 14 | def pytest_generate_tests(metafunc): 15 | pytest_sim_params(metafunc) 16 | pytest_real_type_params(metafunc) 17 | 18 | def gen_model(tau_f=1e-9, tau_s=100e-9, dt=10e-9, real_type=RealType.FixedPoint): 19 | m = MixedSignalModel('model', dt=dt, real_type=real_type) 20 | m.add_analog_input('v_in') 21 | m.add_analog_output('v_out') 22 | m.add_digital_input('clk') 23 | m.add_digital_input('rst') 24 | 25 | m.bind_name('in_gt_out', m.v_in > m.v_out) 26 | 27 | # detector dynamics 28 | eqns = [ 29 | Deriv(m.v_out) == eqn_case([0, 1 / tau_f], [m.in_gt_out]) * (m.v_in - m.v_out) - (m.v_out / tau_s) 30 | ] 31 | m.add_eqn_sys(eqns,clk=m.clk, rst=m.rst) 32 | 33 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 34 | model_file = BUILD_DIR / 'model.sv' 35 | m.compile_to_file(VerilogGenerator(), filename=model_file) 36 | 37 | return model_file 38 | 39 | def test_det(simulator, real_type, tau_f=1e-9, tau_s=100e-9, dt=10e-9): 40 | model_file = gen_model(tau_f=tau_f, tau_s=tau_s, dt=dt, real_type=real_type) 41 | 42 | # declare circuit 43 | class dut(m.Circuit): 44 | name=f'test_{NAME}' 45 | io=m.IO( 46 | v_in=fault.RealIn, 47 | v_out=fault.RealOut, 48 | clk=m.ClockIn, 49 | rst=m.BitIn 50 | ) 51 | 52 | 53 | # create the tester 54 | tester = MsdslTester(dut, dut.clk) 55 | 56 | def debug(): 57 | tester.print("v_in: %0f, v_out: %0f\n", dut.v_in, dut.v_out) 58 | 59 | # initialize 60 | v_in = 0.0 61 | tester.poke(dut.clk, 0) 62 | tester.poke(dut.rst, 1) 63 | tester.poke(dut.v_in, v_in) 64 | tester.step(2) 65 | 66 | # clear reset 67 | tester.poke(dut.rst, 0) 68 | tester.step(2) 69 | 70 | # check initial values 71 | debug() 72 | tester.expect(dut.v_out, 0.0, abs_tol=1e-3) 73 | 74 | # poke input 75 | tester.poke(dut.v_in, 1.5) 76 | tester.step(2) 77 | debug() 78 | tester.expect(dut.v_out, 1.5, abs_tol=0.1) 79 | 80 | # clear input and make sure output is still high 81 | tester.poke(dut.v_in, 0.0) 82 | tester.step(2) 83 | debug() 84 | tester.expect(dut.v_out, 1.4, abs_tol=0.1) 85 | 86 | # wait longer for output to decay back to zero 87 | tester.step(2*30) 88 | debug() 89 | tester.expect(dut.v_out, 0.0, abs_tol=0.1) 90 | 91 | # poke input again 92 | tester.poke(dut.v_in, 1.5) 93 | tester.step(2) 94 | debug() 95 | tester.expect(dut.v_out, 1.5, abs_tol=0.1) 96 | 97 | # this time set input to a mid-range value 98 | tester.poke(dut.v_in, 0.5) 99 | tester.step(2) 100 | debug() 101 | tester.expect(dut.v_out, 1.4, abs_tol=0.1) 102 | 103 | # wait longer for output to decay to new input 104 | tester.step(2*30) 105 | debug() 106 | tester.expect(dut.v_out, 0.5, abs_tol=0.1) 107 | 108 | # increase input and make sure output tracks immediately 109 | tester.poke(dut.v_in, 2.5) 110 | tester.step(2) 111 | debug() 112 | tester.expect(dut.v_out, 2.5, abs_tol=0.1) 113 | 114 | # run the simulation 115 | tester.compile_and_run( 116 | directory=BUILD_DIR, 117 | simulator=simulator, 118 | ext_srcs=[model_file, get_file(f'{NAME}/test_{NAME}.sv')], 119 | real_type=real_type 120 | ) 121 | -------------------------------------------------------------------------------- /tests/det/test_det.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_det( 4 | input real v_in, 5 | output real v_out, 6 | input clk, 7 | input rst 8 | ); 9 | `MAKE_REAL(v_in_int, 10); 10 | assign `FORCE_REAL(v_in, v_in_int); 11 | 12 | `MAKE_REAL(v_out_int, 10); 13 | assign v_out = `TO_REAL(v_out_int); 14 | 15 | model #( 16 | `PASS_REAL(v_in, v_in_int), 17 | `PASS_REAL(v_out, v_out_int) 18 | ) model_i ( 19 | .v_in(v_in_int), 20 | .v_out(v_out_int), 21 | .clk(clk), 22 | .rst(rst) 23 | ); 24 | endmodule -------------------------------------------------------------------------------- /tests/eqn_no_dyn/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/eqn_no_dyn/__init__.py -------------------------------------------------------------------------------- /tests/eqn_no_dyn/test_eqn_no_dyn.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | 4 | # AHA imports 5 | import magma as m 6 | 7 | # msdsl imports 8 | from ..common import * 9 | from msdsl import MixedSignalModel, VerilogGenerator 10 | 11 | NAME = '_'.join(Path(__file__).stem.split('_')[1:]) 12 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 13 | 14 | def pytest_generate_tests(metafunc): 15 | pytest_sim_params(metafunc) 16 | pytest_real_type_params(metafunc) 17 | 18 | def gen_model(const=1.23, real_type=RealType.FixedPoint): 19 | # declare module 20 | m = MixedSignalModel('model', real_type=real_type) 21 | m.add_analog_input('a') 22 | m.add_analog_output('b') 23 | 24 | m.add_eqn_sys([ 25 | m.b == const*m.a 26 | ]) 27 | 28 | # compile to a file 29 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 30 | model_file = BUILD_DIR / 'model.sv' 31 | m.compile_to_file(VerilogGenerator(), filename=model_file) 32 | 33 | # return file location 34 | return model_file 35 | 36 | def test_eqn_no_dyn(simulator, real_type, const=1.23): 37 | model_file = gen_model(const=const, real_type=real_type) 38 | 39 | # declare circuit 40 | class dut(m.Circuit): 41 | name=f'test_{NAME}' 42 | io=m.IO( 43 | a=fault.RealIn, 44 | b=fault.RealOut 45 | ) 46 | 47 | t = MsdslTester(dut) 48 | 49 | def run_trial(a, should_print=True): 50 | t.poke(dut.a, a) 51 | t.eval() 52 | if should_print: 53 | t.print('a: %0f, b: %0f\n', dut.a, dut.b) 54 | t.expect(dut.b, const*a, abs_tol=1e-3) 55 | 56 | # record tests 57 | run_trial(1.23) 58 | run_trial(-1.23) 59 | run_trial(2.34) 60 | run_trial(-2.34) 61 | 62 | # run the simulation 63 | t.compile_and_run( 64 | directory=BUILD_DIR, 65 | simulator=simulator, 66 | ext_srcs=[model_file, get_file(f'{NAME}/test_{NAME}.sv')], 67 | real_type=real_type 68 | ) 69 | -------------------------------------------------------------------------------- /tests/eqn_no_dyn/test_eqn_no_dyn.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_eqn_no_dyn ( 4 | input real a, 5 | output real b 6 | ); 7 | `MAKE_REAL(a_int, 10); 8 | assign `FORCE_REAL(a, a_int); 9 | 10 | `MAKE_REAL(b_int, 10); 11 | assign b = `TO_REAL(b_int); 12 | 13 | model #( 14 | `PASS_REAL(a, a_int), 15 | `PASS_REAL(b, b_int) 16 | ) model_i ( 17 | .a(a_int), 18 | .b(b_int) 19 | ); 20 | endmodule -------------------------------------------------------------------------------- /tests/func_sim/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/func_sim/__init__.py -------------------------------------------------------------------------------- /tests/func_sim/test_func_sim.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | import numpy as np 4 | import importlib 5 | 6 | # AHA imports 7 | import magma as m 8 | 9 | # msdsl imports 10 | from ..common import * 11 | from msdsl import MixedSignalModel, VerilogGenerator 12 | 13 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 14 | DOMAIN = np.pi 15 | RANGE = 1.0 16 | 17 | def pytest_generate_tests(metafunc): 18 | pytest_sim_params(metafunc) 19 | pytest_real_type_params(metafunc) 20 | pytest_func_mode_params(metafunc) 21 | tests = [(0, 0.0105, 512), 22 | (1, 0.000318, 128)] 23 | if importlib.util.find_spec('cvxpy'): 24 | tests.append((2, 0.000232, 32)) 25 | metafunc.parametrize('order,err_lim,numel', tests) 26 | metafunc.parametrize('func', [np.sin, np.cos]) 27 | 28 | def gen_model(myfunc, order=0, numel=512, real_type=RealType.FixedPoint, func_mode='sync'): 29 | # create mixed-signal model 30 | model = MixedSignalModel('model', build_dir=BUILD_DIR, 31 | real_type=real_type) 32 | model.add_analog_input('in_') 33 | model.add_analog_output('out') 34 | model.add_digital_input('clk') 35 | model.add_digital_input('rst') 36 | 37 | # create function 38 | write_tables = (func_mode in {'sync'}) 39 | real_func = model.make_function( 40 | myfunc, domain=[-DOMAIN, +DOMAIN], order=order, numel=numel, write_tables=write_tables) 41 | 42 | # apply function 43 | model.set_from_func(model.out, real_func, model.in_, clk=model.clk, 44 | rst=model.rst, func_mode=func_mode) 45 | 46 | # write the model 47 | return model.compile_to_file(VerilogGenerator()) 48 | 49 | def test_func_sim(func, simulator, order, err_lim, numel, real_type, func_mode): 50 | # set the random seed for repeatable results 51 | np.random.seed(0) 52 | 53 | # create clipped version of function 54 | myfunc = lambda x: func(np.clip(x, -DOMAIN, +DOMAIN)) 55 | 56 | # generate model 57 | model_file = gen_model( 58 | myfunc=myfunc, order=order, numel=numel, real_type=real_type, func_mode=func_mode) 59 | 60 | # declare circuit 61 | class dut(m.Circuit): 62 | name = 'test_func_sim' 63 | io = m.IO( 64 | in_=fault.RealIn, 65 | out=fault.RealOut, 66 | clk=m.In(m.Clock), 67 | rst=m.BitIn 68 | ) 69 | 70 | # create the tester 71 | tester = MsdslTester(dut, dut.clk) 72 | 73 | # initialize 74 | tester.poke(dut.in_, 0) 75 | tester.poke(dut.clk, 0) 76 | tester.poke(dut.rst, 1) 77 | tester.eval() 78 | 79 | # apply reset 80 | tester.step(2) 81 | 82 | # clear reset 83 | tester.poke(dut.rst, 0) 84 | tester.step(2) 85 | 86 | # save the outputs 87 | inpts = np.random.uniform(-1.2*DOMAIN, +1.2*DOMAIN, 100) 88 | apprx = [] 89 | for in_ in inpts: 90 | tester.poke(dut.in_, in_) 91 | if func_mode in {'sync'}: 92 | tester.step(2) 93 | else: 94 | tester.eval() 95 | apprx.append(tester.get_value(dut.out)) 96 | 97 | # run the simulation 98 | parameters = { 99 | 'in_range': 2*DOMAIN, 100 | 'out_range': 2*RANGE 101 | } 102 | tester.compile_and_run( 103 | directory=BUILD_DIR, 104 | simulator=simulator, 105 | ext_srcs=[model_file, get_file('func_sim/test_func_sim.sv')], 106 | parameters=parameters, 107 | real_type=real_type 108 | ) 109 | 110 | # evaluate the outputs 111 | apprx = np.array([elem.value for elem in apprx], dtype=float) 112 | 113 | # compute the exact response to inputs 114 | exact = myfunc(inpts) 115 | 116 | # uncomment to plot results 117 | # import matplotlib.pyplot as plt 118 | # plt.plot(inpts, apprx, '*') 119 | # plt.show() 120 | 121 | # check the result 122 | err = np.sqrt(np.mean((exact-apprx)**2)) 123 | print(f'RMS error: {err}') 124 | assert err <= err_lim 125 | -------------------------------------------------------------------------------- /tests/func_sim/test_func_sim.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_func_sim #( 4 | parameter real in_range=10, 5 | parameter real out_range=10 6 | ) ( 7 | input real in_, 8 | output real out, 9 | input clk, 10 | input rst 11 | ); 12 | // wire input 13 | `MAKE_REAL(in_int, in_range); 14 | assign `FORCE_REAL(in_, in_int); 15 | 16 | // wire output 17 | `MAKE_REAL(out_int, out_range); 18 | assign out = `TO_REAL(out_int); 19 | 20 | // instantiate model 21 | model #( 22 | `PASS_REAL(in_, in_int), 23 | `PASS_REAL(out, out_int) 24 | ) model_i ( 25 | .in_(in_int), 26 | .out(out_int), 27 | .clk(clk), 28 | .rst(rst) 29 | ); 30 | endmodule -------------------------------------------------------------------------------- /tests/gauss_inv_cdf/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/gauss_inv_cdf/__init__.py -------------------------------------------------------------------------------- /tests/gauss_inv_cdf/test_gauss_inv_cdf.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_gauss_inv_cdf #( 4 | parameter real out_range=10 5 | ) ( 6 | input [30:0] in_, 7 | output real out, 8 | input clk, 9 | input rst 10 | ); 11 | // wire output 12 | `MAKE_REAL(out_int, out_range); 13 | assign out = `TO_REAL(out_int); 14 | 15 | // instantiate model 16 | model #( 17 | `PASS_REAL(out, out_int) 18 | ) model_i ( 19 | .in_(in_), 20 | .out(out_int), 21 | .clk(clk), 22 | .rst(rst) 23 | ); 24 | endmodule -------------------------------------------------------------------------------- /tests/gaussian_noise/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/gaussian_noise/__init__.py -------------------------------------------------------------------------------- /tests/gaussian_noise/test_gaussian_noise.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | import random 4 | import numpy as np 5 | from scipy.stats import norm 6 | 7 | # AHA imports 8 | import magma as m 9 | 10 | # msdsl imports 11 | from ..common import * 12 | from msdsl import MixedSignalModel, VerilogGenerator 13 | 14 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 15 | 16 | def pytest_generate_tests(metafunc): 17 | pytest_sim_params(metafunc) 18 | pytest_real_type_params(metafunc) 19 | metafunc.parametrize('gen_type', ['lcg', 'mt19937', 'lfsr']) 20 | 21 | def gen_model(real_type, gen_type): 22 | # create mixed-signal model 23 | model = MixedSignalModel('model', build_dir=BUILD_DIR, real_type=real_type) 24 | model.add_digital_input('clk') 25 | model.add_digital_input('rst') 26 | model.add_analog_input('mean_in') 27 | model.add_analog_input('std_in') 28 | model.add_analog_output('real_out') 29 | 30 | # apply noise 31 | model.set_gaussian_noise(model.real_out, std=model.std_in, mean=model.mean_in, 32 | clk=model.clk, rst=model.rst, gen_type=gen_type) 33 | 34 | # write the model 35 | return model.compile_to_file(VerilogGenerator()) 36 | 37 | def test_gaussian_noise(simulator, real_type, gen_type, 38 | n_trials=10000, mean_val=1.23, std_val=0.456): 39 | # set the random seed for repeatable results. some code uses 40 | # numpy for random number generation, and some code uses the 41 | # random package, so both have to be set 42 | np.random.seed(1) 43 | random.seed(1) 44 | 45 | # generate model 46 | model_file = gen_model(real_type=real_type, gen_type=gen_type) 47 | 48 | # declare circuit 49 | class dut(m.Circuit): 50 | name = 'test_gaussian_noise' 51 | io = m.IO( 52 | clk=m.ClockIn, 53 | rst=m.BitIn, 54 | mean_in=fault.RealIn, 55 | std_in=fault.RealIn, 56 | real_out=fault.RealOut 57 | ) 58 | 59 | # create the tester 60 | t = MsdslTester(dut, dut.clk) 61 | 62 | # initialize 63 | t.zero_inputs() 64 | t.poke(dut.mean_in, mean_val) 65 | t.poke(dut.std_in, std_val) 66 | t.poke(dut.rst, 1) 67 | t.step(2) 68 | 69 | # clear reset and wait for RNG to start 70 | # this take a long time when using the MT19937 option 71 | t.poke(dut.rst, 0) 72 | if gen_type=='mt19937': 73 | wait_time = 25000 74 | else: 75 | wait_time = 100 76 | for _ in range(wait_time): 77 | t.step(2) 78 | 79 | # gather data 80 | data = [] 81 | for _ in range(n_trials): 82 | data.append(t.get_value(dut.real_out)) 83 | t.step(2) 84 | 85 | # run the simulation 86 | t.compile_and_run( 87 | directory=BUILD_DIR, 88 | simulator=simulator, 89 | ext_srcs=[model_file, get_file('gaussian_noise/test_gaussian_noise.sv')], 90 | real_type=real_type 91 | ) 92 | 93 | # analyze the data 94 | data = np.array([elem.value for elem in data], dtype=float) 95 | mean = np.mean(data) 96 | std = np.std(data) 97 | print(f"mean: {mean}, standard dev: {std}") 98 | 99 | # construct empirical CDF 100 | data_sorted = np.sort(data) 101 | p = 1. * np.arange(len(data)) / (len(data) - 1) 102 | 103 | # read out the CDF at multiples of the standard deviation 104 | test_pts = std_val*np.arange(-2, 3) + mean_val 105 | meas_cdf = np.interp(test_pts, data_sorted, p) 106 | print(f'meas_cdf: {meas_cdf}') 107 | 108 | # determine what values the CDF should have at those points 109 | expct_cdf = norm.cdf(test_pts, mean_val, std_val) 110 | print(f'expct_cdf: {expct_cdf}') 111 | 112 | # uncomment to plot results 113 | # import matplotlib.pyplot as plt 114 | # plt.hist(data, bins=50) 115 | # plt.show() 116 | 117 | # check the results 118 | assert np.isclose(mean, mean_val, rtol=0.05), 'Mean is unexpected.' 119 | assert np.isclose(std, std_val, rtol=0.05), 'Standard deviation is unexpected.' 120 | for k in range(len(test_pts)): 121 | assert np.isclose(meas_cdf[k], expct_cdf[k], rtol=0.15), \ 122 | f'CDF mismatch at index {k}: {meas_cdf[k]} vs {expct_cdf[k]}' 123 | -------------------------------------------------------------------------------- /tests/gaussian_noise/test_gaussian_noise.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_gaussian_noise #( 4 | parameter real real_range=10 5 | ) ( 6 | input clk, 7 | input rst, 8 | input real mean_in, 9 | input real std_in, 10 | output real real_out 11 | ); 12 | `MAKE_REAL(mean_int, real_range); 13 | assign `FORCE_REAL(mean_in, mean_int); 14 | 15 | `MAKE_REAL(std_int, real_range); 16 | assign `FORCE_REAL(std_in, std_int); 17 | 18 | `MAKE_REAL(real_out_int, real_range); 19 | assign real_out = `TO_REAL(real_out_int); 20 | 21 | model #( 22 | `PASS_REAL(mean_in, mean_int), 23 | `PASS_REAL(std_in, std_int), 24 | `PASS_REAL(real_out, real_out_int) 25 | ) model_i ( 26 | .clk(clk), 27 | .rst(rst), 28 | .mean_in(mean_int), 29 | .std_in(std_int), 30 | .real_out(real_out_int) 31 | ); 32 | endmodule -------------------------------------------------------------------------------- /tests/lfsr_sim/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/lfsr_sim/__init__.py -------------------------------------------------------------------------------- /tests/lfsr_sim/test_lfsr_sim.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | 4 | # AHA imports 5 | import magma as m 6 | from random import seed, randint 7 | 8 | # msdsl imports 9 | from ..common import * 10 | from msdsl import MixedSignalModel, VerilogGenerator 11 | from msdsl.lfsr import LFSR 12 | 13 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 14 | 15 | def pytest_generate_tests(metafunc): 16 | pytest_sim_params(metafunc) 17 | pytest_real_type_params(metafunc) 18 | seed(0) 19 | metafunc.parametrize('width,init', [(k, randint(0, (1<= prev_state 68 | print(f'Expecting clk_en={dt_req_grant}') 69 | 70 | # run the cycle 71 | tester.expect(dut.dt_req, prev_state, abs_tol=abs_tol) 72 | tester.poke(dut.ext_dt, ext_dt) 73 | tester.eval() 74 | tester.expect(dut.clk_en, dt_req_grant) 75 | tester.step(2) 76 | 77 | # return new state 78 | return period if dt_req_grant else prev_state-ext_dt 79 | 80 | # run cycles 81 | prev_state = period 82 | prev_state = run_cycle(32.1e-12, prev_state) 83 | prev_state = run_cycle(345e-12, prev_state) 84 | prev_state = run_cycle(345e-12, prev_state) 85 | prev_state = run_cycle(20e-12, prev_state) 86 | prev_state = run_cycle(20e-12, prev_state) 87 | prev_state = run_cycle(20e-12, prev_state) 88 | prev_state = run_cycle(20e-12, prev_state) 89 | prev_state = run_cycle(123e-12, prev_state) 90 | prev_state = run_cycle(12.3e-12, prev_state) 91 | prev_state = run_cycle(23.4e-12, prev_state) 92 | prev_state = run_cycle(34.5e-12, prev_state) 93 | prev_state = run_cycle(45.6e-12, prev_state) 94 | prev_state = run_cycle(56.7e-12, prev_state) 95 | prev_state = run_cycle(67.8e-12, prev_state) 96 | 97 | # run the simulation 98 | tester.compile_and_run( 99 | directory=BUILD_DIR, 100 | simulator=simulator, 101 | ext_srcs=[model_file, get_file('osc_model/test_osc_model.sv')], 102 | real_type=real_type, 103 | defines={'DT_WIDTH': dt_width, 'DT_SCALE': dt_scale}, 104 | #dump_waveforms=True 105 | ) 106 | -------------------------------------------------------------------------------- /tests/osc_model/test_osc_model.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_osc_model #( 4 | parameter real period_range = 1e-9 5 | ) ( 6 | input real period, 7 | input real ext_dt, 8 | output real dt_req, 9 | output clk_en, 10 | input clk, 11 | input rst 12 | ); 13 | // wire period 14 | `MAKE_REAL(period_int, period_range); 15 | assign `FORCE_REAL(period, period_int); 16 | 17 | // wire dt_req 18 | logic [((`DT_WIDTH)-1):0] dt_req_int; 19 | assign dt_req = (dt_req_int)*(`DT_SCALE); 20 | 21 | // wire emu_dt 22 | logic [((`DT_WIDTH)-1):0] ext_dt_int; 23 | assign ext_dt_int = (ext_dt)/(`DT_SCALE); 24 | logic [((`DT_WIDTH)-1):0] emu_dt; 25 | assign emu_dt = (ext_dt_int < dt_req_int) ? ext_dt_int : dt_req_int; 26 | 27 | // instantiate MSDSL model, passing through format information 28 | model #( 29 | `PASS_REAL(period, period_int) 30 | ) model_i ( 31 | .period(period_int), 32 | .dt_req(dt_req_int), 33 | .emu_dt(emu_dt), 34 | .clk_en(clk_en), 35 | .clk(clk), 36 | .rst(rst) 37 | ); 38 | endmodule -------------------------------------------------------------------------------- /tests/parameters/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/parameters/__init__.py -------------------------------------------------------------------------------- /tests/parameters/test_parameters.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | 4 | # AHA imports 5 | import magma as m 6 | 7 | # msdsl imports 8 | from ..common import * 9 | from msdsl import MixedSignalModel, VerilogGenerator 10 | 11 | THIS_DIR = Path(__file__).resolve().parent 12 | BUILD_DIR = THIS_DIR / 'build' 13 | 14 | def pytest_generate_tests(metafunc): 15 | pytest_sim_params(metafunc) 16 | pytest_real_type_params(metafunc) 17 | 18 | def gen_model(real_type): 19 | # declare module 20 | m = MixedSignalModel('model', real_type=real_type) 21 | m.add_digital_input('clk') 22 | m.add_digital_input('rst') 23 | m.add_analog_output('g') 24 | 25 | # bind expression to internal signal 26 | m.add_digital_param('param_a') 27 | m.add_digital_param('param_b') 28 | m.add_digital_param('param_c', width=2, signed=True) 29 | m.add_digital_param('param_d', width=2, signed=True) 30 | m.add_real_param('param_e') 31 | m.add_real_param('param_f') 32 | 33 | # create state signals 34 | m.add_digital_state('sig1', init=m.param_a) 35 | m.add_digital_state('sig2', init=m.param_c, width=2, signed=True) 36 | m.add_analog_state('sig3', init=m.param_e, range_=25) 37 | 38 | # create main logic 39 | m.set_next_cycle(m.sig1, m.param_b, clk=m.clk, rst=m.rst) 40 | m.set_next_cycle(m.sig2, m.param_d, clk=m.clk, rst=m.rst) 41 | m.set_next_cycle(m.sig3, m.param_f, clk=m.clk, rst=m.rst) 42 | 43 | # sum signals to output 44 | m.set_this_cycle(m.g, m.sig1 + m.sig2 + m.sig3) 45 | 46 | # compile to a file 47 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 48 | model_file = BUILD_DIR / 'model.sv' 49 | m.compile_to_file(VerilogGenerator(), filename=model_file) 50 | 51 | # return file location 52 | return model_file 53 | 54 | def test_parameters(simulator, real_type): 55 | model_file = gen_model(real_type=real_type) 56 | 57 | param_a = 0 58 | param_b = 1 59 | param_c = -2 60 | param_d = 1 61 | param_e = 1.23 62 | param_f = -4.56 63 | 64 | # declare circuit 65 | class dut(m.Circuit): 66 | name = 'test_parameters' 67 | io = m.IO( 68 | clk=m.ClockIn, 69 | rst=m.BitIn, 70 | g=fault.RealOut 71 | ) 72 | 73 | t = MsdslTester(dut, dut.clk) 74 | 75 | t.zero_inputs() 76 | t.poke(dut.rst, 1) 77 | t.step(2) 78 | t.expect(dut.g, param_a + param_c + param_e, abs_tol=1e-3) 79 | 80 | t.poke(dut.rst, 0) 81 | t.step(2) 82 | t.expect(dut.g, param_b + param_d + param_f, abs_tol=1e-3) 83 | 84 | # run the simulation 85 | t.compile_and_run( 86 | directory=BUILD_DIR, 87 | simulator=simulator, 88 | ext_srcs=[model_file, THIS_DIR / 'test_parameters.sv'], 89 | parameters={'param_a': param_a, 'param_b': param_b, 'param_c': param_c, 90 | 'param_d': param_d, 'param_e': param_e, 'param_f': param_f}, 91 | real_type=real_type 92 | ) 93 | 94 | -------------------------------------------------------------------------------- /tests/parameters/test_parameters.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_parameters #( 4 | parameter param_a=0, 5 | parameter param_b=0, 6 | parameter signed [1:0] param_c=0, 7 | parameter signed [1:0] param_d=0, 8 | parameter real param_e=0, 9 | parameter real param_f=0 10 | ) ( 11 | input clk, 12 | input rst, 13 | output real g 14 | ); 15 | `MAKE_REAL(g_int, 25); 16 | assign g = `TO_REAL(g_int); 17 | 18 | model #( 19 | .param_a(param_a), 20 | .param_b(param_b), 21 | .param_c(param_c), 22 | .param_d(param_d), 23 | .param_e(param_e), 24 | .param_f(param_f), 25 | `PASS_REAL(g, g_int) 26 | ) model_i ( 27 | .clk(clk), 28 | .rst(rst), 29 | .g(g_int) 30 | ); 31 | endmodule -------------------------------------------------------------------------------- /tests/placeholder/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/placeholder/__init__.py -------------------------------------------------------------------------------- /tests/placeholder/test_placeholder.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_placeholder #( 4 | parameter real in_range=10, 5 | parameter real out_range=10, 6 | parameter integer addr_bits=9, 7 | parameter integer data_bits=18 8 | ) ( 9 | input real in_, 10 | output real out, 11 | input clk, 12 | input rst, 13 | // user configuration controls 14 | input signed [(data_bits-1):0] wdata0, 15 | input signed [(data_bits-1):0] wdata1, 16 | input [(addr_bits-1):0] waddr, 17 | input we 18 | ); 19 | // wire input 20 | `MAKE_REAL(in_int, in_range); 21 | assign `FORCE_REAL(in_, in_int); 22 | 23 | // wire output 24 | `MAKE_REAL(out_int, out_range); 25 | assign out = `TO_REAL(out_int); 26 | 27 | // instantiate model 28 | model #( 29 | `PASS_REAL(in_, in_int), 30 | `PASS_REAL(out, out_int) 31 | ) model_i ( 32 | .in_(in_int), 33 | .out(out_int), 34 | .clk(clk), 35 | .rst(rst), 36 | .wdata0(wdata0), 37 | .wdata1(wdata1), 38 | .waddr(waddr), 39 | .we(we) 40 | ); 41 | endmodule -------------------------------------------------------------------------------- /tests/rc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/rc/__init__.py -------------------------------------------------------------------------------- /tests/rc/test_rc.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from math import exp 3 | from pathlib import Path 4 | 5 | # AHA imports 6 | import magma as m 7 | 8 | # msdsl imports 9 | from ..common import * 10 | from msdsl import MixedSignalModel, VerilogGenerator, Deriv 11 | 12 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 13 | 14 | def pytest_generate_tests(metafunc): 15 | pytest_sim_params(metafunc) 16 | pytest_real_type_params(metafunc) 17 | 18 | def gen_model(tau, dt, real_type): 19 | model = MixedSignalModel('model', dt=dt, real_type=real_type) 20 | model.add_analog_input('v_in') 21 | model.add_analog_output('v_out') 22 | model.add_digital_input('clk') 23 | model.add_digital_input('rst') 24 | 25 | model.add_eqn_sys([Deriv(model.v_out) == (model.v_in - model.v_out)/tau], 26 | clk=model.clk, rst=model.rst) 27 | 28 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 29 | model_file = BUILD_DIR / 'model.sv' 30 | model.compile_to_file(VerilogGenerator(), filename=model_file) 31 | 32 | return model_file 33 | 34 | def test_rc(simulator, real_type, tau=1e-6, dt=0.1e-6): 35 | model_file = gen_model(tau=tau, dt=dt, real_type=real_type) 36 | 37 | # declare circuit 38 | class dut(m.Circuit): 39 | name='test_rc' 40 | io=m.IO( 41 | v_in=fault.RealIn, 42 | v_out=fault.RealOut, 43 | clk=m.ClockIn, 44 | rst=m.BitIn 45 | ) 46 | 47 | # create the tester 48 | tester = MsdslTester(dut, dut.clk) 49 | 50 | # initialize 51 | v_in = 1.0 52 | tester.poke(dut.clk, 0) 53 | tester.poke(dut.rst, 1) 54 | tester.poke(dut.v_in, v_in) 55 | tester.eval() 56 | 57 | # reset 58 | tester.step(2) 59 | 60 | # print the first few outputs 61 | tester.poke(dut.rst, 0) 62 | for k in range(20): 63 | tester.expect(dut.v_out, v_in*(1-exp(-k*dt/tau)), abs_tol=0.025) 64 | tester.print("v_out: %0f\n", dut.v_out) 65 | tester.step(2) 66 | 67 | # run the simulation 68 | tester.compile_and_run( 69 | directory=BUILD_DIR, 70 | simulator=simulator, 71 | ext_srcs=[model_file, get_file('rc/test_rc.sv')], 72 | real_type=real_type 73 | ) 74 | -------------------------------------------------------------------------------- /tests/rc/test_rc.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_rc( 4 | input real v_in, 5 | output real v_out, 6 | input clk, 7 | input rst 8 | ); 9 | `MAKE_REAL(v_in_int, 10); 10 | assign `FORCE_REAL(v_in, v_in_int); 11 | 12 | `MAKE_REAL(v_out_int, 10); 13 | assign v_out = `TO_REAL(v_out_int); 14 | 15 | model #( 16 | `PASS_REAL(v_in, v_in_int), 17 | `PASS_REAL(v_out, v_out_int) 18 | ) model_i ( 19 | .v_in(v_in_int), 20 | .v_out(v_out_int), 21 | .clk(clk), 22 | .rst(rst) 23 | ); 24 | endmodule -------------------------------------------------------------------------------- /tests/rlc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/rlc/__init__.py -------------------------------------------------------------------------------- /tests/rlc/test_rlc.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from math import exp, cos, sin, sqrt 3 | from pathlib import Path 4 | 5 | # AHA imports 6 | import magma as m 7 | 8 | # msdsl imports 9 | from ..common import * 10 | from msdsl import MixedSignalModel, VerilogGenerator, AnalogSignal, Deriv 11 | 12 | NAME = Path(__file__).stem.split('_')[1] 13 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 14 | 15 | def pytest_generate_tests(metafunc): 16 | pytest_sim_params(metafunc) 17 | pytest_real_type_params(metafunc) 18 | 19 | def gen_model(cap=0.16e-6, ind=0.16e-6, res=0.1, dt=0.01e-6, 20 | real_type=RealType.FixedPoint): 21 | # declare model 22 | m = MixedSignalModel('model', dt=dt, real_type=real_type) 23 | m.add_analog_input('v_in') 24 | m.add_analog_output('v_out') 25 | m.add_digital_input('clk') 26 | m.add_digital_input('rst') 27 | 28 | # declare system of equations 29 | m.add_analog_state('i_ind', 10) # TODO: can this be tightened down a bit? 30 | v_l = AnalogSignal('v_l') 31 | v_r = AnalogSignal('v_r') 32 | eqns = [ 33 | Deriv(m.i_ind) == v_l / ind, 34 | Deriv(m.v_out) == m.i_ind / cap, 35 | v_r == m.i_ind * res, 36 | m.v_in == m.v_out + v_l + v_r 37 | ] 38 | m.add_eqn_sys(eqns, clk=m.clk, rst=m.rst) 39 | 40 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 41 | model_file = BUILD_DIR / 'model.sv' 42 | m.compile_to_file(VerilogGenerator(), filename=model_file) 43 | 44 | return model_file 45 | 46 | def test_rlc(simulator, real_type, cap=0.16e-6, ind=0.16e-6, res=0.1, dt=0.01e-6): 47 | model_file = gen_model(cap=cap, ind=ind, res=res, dt=dt, 48 | real_type=real_type) 49 | 50 | # declare circuit 51 | class dut(m.Circuit): 52 | name=f'test_{NAME}' 53 | io=m.IO( 54 | v_in=fault.RealIn, 55 | v_out=fault.RealOut, 56 | clk=m.ClockIn, 57 | rst=m.BitIn 58 | ) 59 | 60 | # create the tester 61 | tester = MsdslTester(dut, dut.clk) 62 | 63 | # initialize 64 | v_in = 1.0 65 | tester.poke(dut.clk, 0) 66 | tester.poke(dut.rst, 1) 67 | tester.poke(dut.v_in, v_in) 68 | tester.eval() 69 | 70 | # reset 71 | tester.step(2) 72 | 73 | # model for circuit behavior 74 | # see slide 15 here: http://tuttle.merc.iastate.edu/ee201/topics/capacitors_inductors/RLC_transients.pdf 75 | vf = v_in 76 | vi = 0.0 77 | o = -res/(2*ind) 78 | wd = sqrt(1/(ind*cap)-((res/(2*ind))**2)) 79 | def model(t): 80 | return vf - (vf-vi)*(exp(o*t)*(cos(wd*t)-(o/wd)*sin(wd*t))) 81 | 82 | # print the first few outputs 83 | tester.poke(dut.rst, 0) 84 | for k in range(20): 85 | tester.expect(dut.v_out, model(k*dt), abs_tol=0.025) 86 | tester.print("v_out: %0f\n", dut.v_out) 87 | tester.step(2) 88 | 89 | # run the simulation 90 | tester.compile_and_run( 91 | directory=BUILD_DIR, 92 | simulator=simulator, 93 | ext_srcs=[model_file, get_file(f'{NAME}/test_{NAME}.sv')], 94 | real_type=real_type 95 | ) 96 | -------------------------------------------------------------------------------- /tests/rlc/test_rlc.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_rlc( 4 | input real v_in, 5 | output real v_out, 6 | input clk, 7 | input rst 8 | ); 9 | `MAKE_REAL(v_in_int, 10); 10 | assign `FORCE_REAL(v_in, v_in_int); 11 | 12 | `MAKE_REAL(v_out_int, 10); 13 | assign v_out = `TO_REAL(v_out_int); 14 | 15 | model #( 16 | `PASS_REAL(v_in, v_in_int), 17 | `PASS_REAL(v_out, v_out_int) 18 | ) model_i ( 19 | .v_in(v_in_int), 20 | .v_out(v_out_int), 21 | .clk(clk), 22 | .rst(rst) 23 | ); 24 | endmodule -------------------------------------------------------------------------------- /tests/sub/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/sub/__init__.py -------------------------------------------------------------------------------- /tests/sub/test_sub.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | 4 | # AHA imports 5 | import magma as m 6 | 7 | # msdsl imports 8 | from ..common import * 9 | from msdsl import MixedSignalModel, VerilogGenerator 10 | 11 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 12 | 13 | def pytest_generate_tests(metafunc): 14 | pytest_sim_params(metafunc) 15 | 16 | def gen_model(): 17 | # declare model I/O 18 | m = MixedSignalModel('model') 19 | m.add_digital_input('a', width=63, signed=True) 20 | m.add_digital_input('b', width=63, signed=True) 21 | m.add_digital_output('c', width=64, signed=True) 22 | 23 | # assign expression to output 24 | m.bind_name('d', m.a - m.b) 25 | m.set_this_cycle(m.c, m.d) 26 | 27 | # compile to a file 28 | BUILD_DIR.mkdir(parents=True, exist_ok=True) 29 | model_file = BUILD_DIR / 'model.sv' 30 | m.compile_to_file(VerilogGenerator(), filename=model_file) 31 | 32 | # return file location 33 | return model_file 34 | 35 | def test_sub(simulator): 36 | model_file = gen_model() 37 | 38 | # declare circuit 39 | class dut(m.Circuit): 40 | name = 'test_sub' 41 | io = m.IO( 42 | a=m.In(m.SInt[63]), 43 | b=m.In(m.SInt[63]), 44 | c=m.Out(m.SInt[64]) 45 | ) 46 | 47 | def model(a, b): 48 | return a - b 49 | 50 | # create mechanism to run trials 51 | t = MsdslTester(dut) 52 | def run_trial(a, b, should_print=False): 53 | t.poke(dut.a, a) 54 | t.poke(dut.b, b) 55 | t.eval() 56 | if should_print: 57 | t.print('a: %0d, b: %0d, c: %0d\n', dut.a, dut.b, dut.c) 58 | t.expect(dut.c, model(a, b)) 59 | 60 | # determine tolerance 61 | run_trial(1293371963190904369, 4127252734515468513) 62 | run_trial(4401526010147921985, -2843975463655034865) 63 | run_trial(3334474569623275707, -2203503052900441272) 64 | run_trial(-2576643849353512883, -2271681343036037956) 65 | run_trial(1562136255888534365, 1335463821191115164) 66 | 67 | # run the simulation 68 | t.compile_and_run( 69 | directory=BUILD_DIR, 70 | simulator=simulator, 71 | ext_srcs=[model_file, get_file('sub/test_sub.sv')] 72 | ) 73 | -------------------------------------------------------------------------------- /tests/sub/test_sub.sv: -------------------------------------------------------------------------------- 1 | `include "svreal.sv" 2 | 3 | module test_sub ( 4 | input signed [62:0] a, 5 | input signed [62:0] b, 6 | output signed [63:0] c 7 | ); 8 | model model_i ( 9 | .a(a), 10 | .b(b), 11 | .c(c) 12 | ); 13 | endmodule -------------------------------------------------------------------------------- /tests/table_sim/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sgherbst/msdsl/4e1642d6af79f857182b22e04f63984b2089802e/tests/table_sim/__init__.py -------------------------------------------------------------------------------- /tests/table_sim/test_table_sim.py: -------------------------------------------------------------------------------- 1 | # general imports 2 | from pathlib import Path 3 | import pytest 4 | import numpy as np 5 | 6 | # AHA imports 7 | import magma as m 8 | 9 | # msdsl imports 10 | from ..common import * 11 | from msdsl import MixedSignalModel, VerilogGenerator 12 | 13 | BUILD_DIR = Path(__file__).resolve().parent / 'build' 14 | 15 | def pytest_generate_tests(metafunc): 16 | pytest_sim_params(metafunc) 17 | 18 | def gen_model(real_vals, sint_vals, uint_vals, addr_bits, 19 | sint_bits, uint_bits, real_type): 20 | # create mixed-signal model 21 | model = MixedSignalModel('model', build_dir=BUILD_DIR, 22 | real_type=real_type) 23 | model.add_digital_input('addr', width=addr_bits) 24 | model.add_digital_input('clk') 25 | model.add_analog_output('real_out') 26 | model.add_digital_output('sint_out', width=sint_bits) 27 | model.add_digital_output('uint_out', width=uint_bits) 28 | 29 | # create tables 30 | real_table = model.make_real_table(real_vals) 31 | sint_table = model.make_sint_table(sint_vals) 32 | uint_table = model.make_uint_table(uint_vals) 33 | 34 | # assign values 35 | model.set_from_sync_rom(model.real_out, real_table, model.addr, clk=model.clk) 36 | model.set_from_sync_rom(model.sint_out, sint_table, model.addr, clk=model.clk) 37 | model.set_from_sync_rom(model.uint_out, uint_table, model.addr, clk=model.clk) 38 | 39 | # write the model 40 | return model.compile_to_file(VerilogGenerator()) 41 | 42 | @pytest.mark.parametrize('real_type', [RealType.FixedPoint, RealType.HardFloat]) 43 | def test_table_sim(simulator, real_type, addr_bits=8, real_range=10, sint_bits=8, 44 | uint_bits=8): 45 | # generate random data to go into the table 46 | n_samp = 1<