Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in
Toggle navigation
L
Libre Filter Design and Analysis Tool
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
Wiki
Wiki
image/svg+xml
Discourse
Discourse
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Projects
Libre Filter Design and Analysis Tool
Commits
19f17f15
Commit
19f17f15
authored
Oct 05, 2013
by
Javier D. Garcia-Lasheras
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
GHDL support added.
Output signal is calculated by parsing VCD file
parent
07f49352
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
1115 additions
and
280 deletions
+1115
-280
Verilog_VCD.py
Verilog_VCD.py
+481
-0
filter.py
filter.py
+12
-10
librefdatool.py
librefdatool.py
+18
-7
scope.py
scope.py
+6
-6
simcore.py
simcore.py
+182
-236
simulator.py
simulator.py
+25
-8
snippets.py
snippets.py
+391
-13
No files found.
Verilog_VCD.py
0 → 100644
View file @
19f17f15
# This is a manual translation, from perl to python, of :
# http://cpansearch.perl.org/src/GSULLIVAN/Verilog-VCD-0.03/lib/Verilog/VCD.pm
import
re
global
timescale
global
endtime
def
croak
(
*
args
):
"""Function similar to Perl's Carp::croak, to simplify porting this code"""
a
=
""
.
join
(
args
);
raise
Exception
(
a
)
def
list_sigs
(
file
)
:
"""Parse input VCD file into data structure,
then return just a list of the signal names."""
vcd
=
parse_vcd
(
file
,
only_sigs
=
1
)
sigs
=
[]
for
k
,
v
in
vcd
:
nets
=
v
[
'nets'
]
sigs
.
extend
(
n
[
'hier'
]
+
n
[
'name'
]
for
n
in
nets
)
return
sigs
def
parse_vcd
(
file
,
only_sigs
=
0
,
use_stdout
=
0
,
siglist
=
[],
opt_timescale
=
''
):
"""Parse input VCD file into data structure.
Also, print t-v pairs to STDOUT, if requested."""
usigs
=
{}
for
i
in
siglist
:
usigs
[
i
]
=
1
if
len
(
usigs
):
all_sigs
=
0
else
:
all_sigs
=
1
data
=
{}
mult
=
0
num_sigs
=
0
hier
=
[]
time
=
0
re_time
=
re
.
compile
(
r"^#(\d+)"
)
re_1b_val
=
re
.
compile
(
r"^([01zx])(.+)"
)
re_Nb_val
=
re
.
compile
(
r"^[br](\S+)\s+(.+)"
)
fh
=
open
(
file
,
'r'
)
while
True
:
line
=
fh
.
readline
()
if
line
==
''
:
# EOF
break
# chomp
# s/ ^ \s+ //x
line
=
line
.
strip
()
if
"$enddefinitions"
in
line
:
num_sigs
=
len
(
data
)
if
(
num_sigs
==
0
)
:
if
(
all_sigs
)
:
croak
(
"Error: No signals were found in the VCD file file."
,
'Check the VCD file for proper var syntax.'
)
else
:
croak
(
"Error: No matching signals were found in the VCD file file."
,
' Use list_sigs to view all signals in the VCD file.'
)
if
((
num_sigs
>
1
)
and
use_stdout
)
:
croak
(
"Error: There are too many signals (num_sigs) for output "
,
'to STDOUT. Use list_sigs to select a single signal.'
)
if
only_sigs
:
break
elif
"$timescale"
in
line
:
statement
=
line
if
not
"$end"
in
line
:
while
fh
:
line
=
fh
.
readline
()
statement
+=
line
if
"$end"
in
line
:
break
mult
=
calc_mult
(
statement
,
opt_timescale
)
elif
"$scope"
in
line
:
# assumes all on one line
# $scope module dff end
hier
.
append
(
line
.
split
()[
2
]
)
# just keep scope name
elif
"$upscope"
in
line
:
hier
.
pop
()
elif
"$var"
in
line
:
# assumes all on one line:
# $var reg 1 *@ data $end
# $var wire 4 ) addr [3:0] $end
ls
=
line
.
split
()
type
=
ls
[
1
]
size
=
ls
[
2
]
code
=
ls
[
3
]
name
=
""
.
join
(
ls
[
4
:
-
1
])
path
=
'.'
.
join
(
hier
)
full_name
=
path
+
name
if
(
full_name
in
usigs
)
or
all_sigs
:
if
code
not
in
data
:
data
[
code
]
=
{}
if
'nets'
not
in
data
[
code
]:
data
[
code
][
'nets'
]
=
[]
var_struct
=
{
'type'
:
type
,
'name'
:
name
,
'size'
:
size
,
'hier'
:
path
,
}
if
var_struct
not
in
data
[
code
][
'nets'
]:
data
[
code
][
'nets'
]
.
append
(
var_struct
)
elif
line
.
startswith
(
'#'
):
re_time_match
=
re_time
.
match
(
line
)
time
=
mult
*
int
(
re_time_match
.
group
(
1
))
endtime
=
time
elif
line
.
startswith
((
'0'
,
'1'
,
'x'
,
'z'
,
'b'
,
'r'
)):
re_1b_val_match
=
re_1b_val
.
match
(
line
)
re_Nb_val_match
=
re_Nb_val
.
match
(
line
)
if
re_Nb_val_match
:
value
=
re_Nb_val_match
.
group
(
1
)
code
=
re_Nb_val_match
.
group
(
2
)
elif
re_1b_val_match
:
value
=
re_1b_val_match
.
group
(
1
)
code
=
re_1b_val_match
.
group
(
2
)
if
(
code
in
data
)
:
if
(
use_stdout
)
:
print
(
time
,
value
)
else
:
if
'tv'
not
in
data
[
code
]:
data
[
code
][
'tv'
]
=
[]
data
[
code
][
'tv'
]
.
append
(
(
time
,
value
)
)
fh
.
close
()
return
data
def
calc_mult
(
statement
,
opt_timescale
=
''
):
"""
Calculate a new multiplier for time values.
Input statement is complete timescale, for example:
timescale 10ns end
Input new_units is one of s|ms|us|ns|ps|fs.
Return numeric multiplier.
Also sets the package timescale variable.
"""
fields
=
statement
.
split
()
fields
.
pop
()
# delete end from array
fields
.
pop
(
0
)
# delete timescale from array
tscale
=
''
.
join
(
fields
)
new_units
=
''
if
(
opt_timescale
!=
''
):
new_units
=
opt_timescale
.
lower
()
new_units
=
re
.
sub
(
r"\s"
,
''
,
new_units
)
timescale
=
"1"
+
new_units
else
:
timescale
=
tscale
return
1
mult
=
0
units
=
0
ts_match
=
re
.
compile
(
r"(\d+)([a-z]+)"
)
if
ts_match
.
match
(
tscale
):
mult
=
ts_match
.
group
(
1
)
units
=
ts_match
.
group
(
2
)
.
lower
()
else
:
croak
(
"Error: Unsupported timescale found in VCD file: tscale. "
,
'Refer to the Verilog LRM.'
)
mults
=
{
'fs'
:
1e-15
,
'ps'
:
1e-12
,
'ns'
:
1e-09
,
'us'
:
1e-06
,
'ms'
:
1e-03
,
's'
:
1e-00
,
}
mults_keys
=
keys
(
mults
)
mults_keys
.
sort
(
key
=
lambda
x
:
mults
[
x
])
usage
=
'|'
.
join
(
mults_keys
)
scale
=
0
if
units
in
mults
:
scale
=
mults
[
units
]
else
:
croak
(
"Error: Unsupported timescale units found in VCD file: "
+
units
+
". "
,
"Supported values are: "
+
usage
)
new_scale
=
0
if
new_units
in
mults
:
new_scale
=
mults
[
new_units
]
else
:
croak
(
"Error: Illegal user-supplied timescale: "
+
new_units
+
". "
,
"Legal values are: "
+
usage
)
return
((
mult
*
scale
)
/
new_scale
)
def
get_timescale
()
:
return
timescale
def
get_endtime
()
:
return
endtime
# =head1 NAME
#
# Verilog_VCD - Parse a Verilog VCD text file
#
# =head1 VERSION
#
# This document refers to Verilog::VCD version 0.03.
#
# =head1 SYNOPSIS
#
# from Verilog_VCD import parse_vcd
# vcd = parse_vcd('/path/to/some.vcd')
#
# =head1 DESCRIPTION
#
# Verilog is a Hardware Description Language (HDL) used to model digital logic.
# While simulating logic circuits, the values of signals can be written out to
# a Value Change Dump (VCD) file. This module can be used to parse a VCD file
# so that further analysis can be performed on the simulation data. The entire
# VCD file can be stored in a python data structure and manipulated using
# standard hash and array operations.
#
# =head2 Input File Syntax
#
# The syntax of the VCD text file is described in the documentation of
# the IEEE standard for Verilog. Only the four-state VCD format is supported.
# The extended VCD format (with strength information) is not supported.
# Since the input file is assumed to be legal VCD syntax, only minimal
# validation is performed.
#
# =head1 SUBROUTINES
#
#
# =head2 parse_vcd(file, $opt_ref)
#
# Parse a VCD file and return a reference to a data structure which
# includes hierarchical signal definitions and time-value data for all
# the specified signals. A file name is required. By default, all
# signals in the VCD file are included, and times are in units
# specified by the C<$timescale> VCD keyword.
#
# vcd = parse_vcd('/path/to/some.vcd')
#
# It returns a reference to a nested data structure. The top of the
# structure is a Hash-of-Hashes. The keys to the top hash are the VCD
# identifier codes for each signal. The following is an example
# representation of a very simple VCD file. It shows one signal named
# C<chip.cpu.alu.clk>, whose VCD code is C<+>. The time-value pairs
# are stored as an Array-of-Tuples, referenced by the C<tv> key. The
# time is always the first number in the pair, and the times are stored in
# increasing order in the array.
#
# {
# '+' : {
# 'tv' : [
# (
# 0,
# '1'
# ),
# (
# 12,
# '0'
# ),
# ],
# 'nets' : [
# {
# 'hier' : 'chip.cpu.alu.',
# 'name' : 'clk',
# 'type' : 'reg',
# 'size' : '1'
# }
# ]
# }
# }
#
# Since each code could have multiple hierarchical signal names, the names are
# stored as an Array-of-Hashes, referenced by the C<nets> key. The example above
# only shows one signal name for the code.
#
#
# =head3 OPTIONS
#
# Options to C<parse_vcd> should be passed as a hash reference.
#
# =over 4
#
# =item timescale
#
# It is possible to scale all times in the VCD file to a desired timescale.
# To specify a certain timescale, such as nanoseconds:
#
# vcd = parse_vcd(file, opt_timescale='ns'})
#
# Valid timescales are:
#
# s ms us ns ps fs
#
# =item siglist
#
# If only a subset of the signals included in the VCD file are needed,
# they can be specified by a signal list passed as an array reference.
# The signals should be full hierarchical paths separated by the dot
# character. For example:
#
# signals = [
# 'top.chip.clk',
# 'top.chip.cpu.alu.status',
# 'top.chip.cpu.alu.sum[15:0]',
# ]
# vcd = parse_vcd(file, siglist=signals)
#
# Limiting the number of signals can substantially reduce memory usage of the
# returned data structure because only the time-value data for the selected
# signals is loaded into the data structure.
#
# =item use_stdout
#
# It is possible to print time-value pairs directly to STDOUT for a
# single signal using the C<use_stdout> option. If the VCD file has
# more than one signal, the C<siglist> option must also be used, and there
# must only be one signal specified. For example:
#
# vcd = parse_vcd(file,
# use_stdout=1,
# siglist=['top.clk']
# )
#
# The time-value pairs are output as space-separated tokens, one per line.
# For example:
#
# 0 x
# 15 0
# 277 1
# 500 0
#
# Times are listed in the first column.
# Times units can be controlled by the C<timescale> option.
#
# =item only_sigs
#
# Parse a VCD file and return a reference to a data structure which
# includes only the hierarchical signal definitions. Parsing stops once
# all signals have been found. Therefore, no time-value data are
# included in the returned data structure. This is useful for
# analyzing signals and hierarchies.
#
# vcd = parse_vcd(file, only_sigs=1)
#
# =back
#
#
# =head2 list_sigs(file)
#
# Parse a VCD file and return a list of all signals in the VCD file.
# Parsing stops once all signals have been found. This is
# helpful for deciding how to limit what signals are parsed.
#
# Here is an example:
#
# signals = list_sigs('input.vcd')
#
# The signals are full hierarchical paths separated by the dot character
#
# top.chip.cpu.alu.status
# top.chip.cpu.alu.sum[15:0]
#
# =head2 get_timescale( )
#
# This returns a string corresponding to the timescale as specified
# by the C<$timescale> VCD keyword. It returns the timescale for
# the last VCD file parsed. If called before a file is parsed, it
# returns an undefined value. If the C<parse_vcd> C<timescale> option
# was used to specify a timescale, the specified value will be returned
# instead of what is in the VCD file.
#
# vcd = parse_vcd(file); # Parse a file first
# ts = get_timescale(); # Then query the timescale
#
# =head2 get_endtime( )
#
# This returns the last time found in the VCD file, scaled
# appropriately. It returns the last time for the last VCD file parsed.
# If called before a file is parsed, it returns an undefined value.
#
# vcd = parse_vcd(file); # Parse a file first
# et = get_endtime(); # Then query the endtime
#
# =head1 EXPORT
#
# Nothing is exported by default. Functions may be exported individually, or
# all functions may be exported at once, using the special tag C<:all>.
#
# =head1 DIAGNOSTICS
#
# Error conditions cause the program to raise an Exception.
#
# =head1 LIMITATIONS
#
# Only the following VCD keywords are parsed:
#
# $end $scope
# $enddefinitions $upscope
# $timescale $var
#
# The extended VCD format (with strength information) is not supported.
#
# The default mode of C<parse_vcd> is to load the entire VCD file into the
# data structure. This could be a problem for huge VCD files. The best solution
# to any memory problem is to plan ahead and keep VCD files as small as possible.
# When simulating, dump fewer signals and scopes, and use shorter dumping
# time ranges. Another technique is to parse only a small list of signals
# using the C<siglist> option; this method only loads the desired signals into
# the data structure. Finally, the C<use_stdout> option will parse the input VCD
# file line-by-line, instead of loading it into the data structure, and directly
# prints time-value data to STDOUT. The drawback is that this only applies to
# one signal.
#
# =head1 BUGS
#
# There are no known bugs in this module.
#
# =head1 SEE ALSO
#
# Refer to the following Verilog documentation:
#
# IEEE Standard for Verilog (c) Hardware Description Language
# IEEE Std 1364-2005
# Section 18.2, "Format of four-state VCD file"
#
# =head1 AUTHOR
#
# Originally written in Perl by Gene Sullivan (gsullivan@cpan.org)
# Translated into Python by Sameer Gauria (sgauria+python@gmail.com)
#
# =head1 COPYRIGHT AND LICENSE
#
# Copyright (c) 2012 Gene Sullivan, Sameer Gauria. All rights reserved.
#
# This module is free software; you can redistribute it and/or modify
# it under the same terms as Perl itself. See L<perlartistic|perlartistic>.
#
# =cut
filter.py
View file @
19f17f15
...
...
@@ -407,13 +407,13 @@ class Filter():
warnings
.
simplefilter
(
'error'
)
try
:
self
.
b
,
self
.
a
=
self
.
calculate_filter_coefficients
()
except
ValueError
,
exVE
:
except
ValueError
as
exVE
:
warningMessage
=
'
%
s'
%
exVE
except
ZeroDivisionError
,
exZDE
:
except
ZeroDivisionError
as
exZDE
:
warningMessage
=
'
%
s'
%
exZDE
except
RuntimeWarning
,
exRW
:
except
RuntimeWarning
as
exRW
:
warningMessage
=
'
%
s'
%
exRW
except
signal
.
BadCoefficients
,
exBC
:
except
signal
.
BadCoefficients
as
exBC
:
warningMessage
=
'
%
s'
%
exBC
if
warningMessage
!=
''
:
...
...
@@ -487,12 +487,12 @@ class Filter():
labelFilterStructure
=
QLabel
(
'Structure:'
)
self
.
comboFilterStructure
=
QComboBox
()
self
.
comboFilterStructure
.
addItem
(
"Direct
Form I
Transposed"
)
self
.
comboFilterStructure
.
addItem
(
"Direct
Form II
Transposed"
)
self
.
comboFilterStructure
.
addItem
(
"Direct
Form
I"
)
self
.
comboFilterStructure
.
addItem
(
"Direct
_Form_I_
Transposed"
)
self
.
comboFilterStructure
.
addItem
(
"Direct
_Form_II_
Transposed"
)
self
.
comboFilterStructure
.
addItem
(
"Direct
_Form_
I"
)
self
.
comboFilterStructure
.
addItem
(
"Parallel"
)
self
.
comboFilterStructure
.
addItem
(
"Cascade"
)
self
.
comboFilterStructure
.
addItem
(
"Direct
Form
II"
)
self
.
comboFilterStructure
.
addItem
(
"Direct
_Form_
II"
)
self
.
comboFilterStructure
.
setEditable
(
True
)
self
.
comboFilterStructure
.
lineEdit
()
.
setReadOnly
(
True
)
self
.
comboFilterStructure
.
lineEdit
()
.
setAlignment
(
Qt
.
AlignRight
)
...
...
@@ -514,7 +514,7 @@ class Filter():
self
.
comboFilterOverflow
.
setItemData
(
ii
,
Qt
.
AlignRight
,
Qt
.
TextAlignmentRole
)
#self.connect(self.comboFilterStructure, SIGNAL('currentIndexChanged(int)'), self.on_parameter_change)
# TODO: the connected method is still provisional, it forces to DF I Transposed
self
.
connect
(
self
.
comboFilter
Structure
,
SIGNAL
(
'currentIndexChanged(int)'
),
self
.
on_overflow_change
)
self
.
connect
(
self
.
comboFilter
Overflow
,
SIGNAL
(
'currentIndexChanged(int)'
),
self
.
on_overflow_change
)
labelResponseType
=
QLabel
(
'Filtering Method:'
)
...
...
@@ -1010,10 +1010,11 @@ class Filter():
return
vboxMain
def
on_structure_change
(
self
):
# TODO: provisional, check the structure change and force to DF1 Trans
activeStructure
=
str
(
self
.
comboFilterStructure
.
currentText
())
if
activeStructure
==
'Direct
Form I
Transposed'
:
if
activeStructure
==
'Direct
_Form_I_
Transposed'
:
self
.
on_parameter_change
()
else
:
QMessageBox
.
information
(
self
,
'Filter Structure'
,
...
...
@@ -1021,6 +1022,7 @@ class Filter():
%
activeStructure
,
QMessageBox
.
Ok
)
self
.
comboFilterStructure
.
setCurrentIndex
(
0
)
def
on_overflow_change
(
self
):
# TODO: provisional, check the structure change and force to DF1 Trans
...
...
librefdatool.py
View file @
19f17f15
...
...
@@ -45,11 +45,6 @@ import sys, os, random
from
PyQt4.QtCore
import
*
from
PyQt4.QtGui
import
*
#import matplotlib
#from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
#from matplotlib.backends.backend_qt4agg import NavigationToolbar2QTAgg as NavigationToolbar
#from matplotlib.figure import Figure
class
LibreFDATool
(
QMainWindow
,
...
...
@@ -62,6 +57,9 @@ class LibreFDATool(
stimulusUpdatedSignal
=
pyqtSignal
()
filterUpdatedSignal
=
pyqtSignal
()
simulatorUpdatedSignal
=
pyqtSignal
()
simulatorBeginSignal
=
pyqtSignal
()
simulatorEndSignal
=
pyqtSignal
()
def
__init__
(
self
):
QMainWindow
.
__init__
(
self
)
...
...
@@ -75,6 +73,8 @@ class LibreFDATool(
self
.
stimulusUpdatedSignal
.
connect
(
self
.
on_stimulus_updated
)
self
.
filterUpdatedSignal
.
connect
(
self
.
on_filter_updated
)
self
.
simulatorUpdatedSignal
.
connect
(
self
.
on_simulator_updated
)
self
.
simulatorBeginSignal
.
connect
(
self
.
on_simulator_begin
)
self
.
simulatorEndSignal
.
connect
(
self
.
on_simulator_end
)
self
.
runSimulator
()
...
...
@@ -245,8 +245,9 @@ class LibreFDATool(
scalingX
=
1
scalingC
=
1
self
.
execute_simulation
(
self
.
b
,
self
.
a
,
self
.
x
,
str
(
self
.
textFilterName
.
text
()),
'vhdl'
,
self
.
execute_simulation
(
self
.
b
,
self
.
a
,
self
.
x
,
str
(
self
.
textFilterName
.
text
()),
str
(
self
.
comboSimulatorLanguage
.
currentText
()),
str
(
self
.
comboSimulatorEngine
.
currentText
()),
str
(
self
.
comboFilterStructure
.
currentText
()),
busX
,
busY
,
busC
,
scalingX
,
scalingC
)
...
...
@@ -275,6 +276,16 @@ class LibreFDATool(
#QMessageBox.information(self, 'Simulator Updated',
# 'This is only a message', QMessageBox.Ok)
self
.
status_text
.
setText
(
'Simulator configuration has been updated'
)
def
on_simulator_begin
(
self
):
#QMessageBox.information(self, 'Simulator Begin',
# 'This is only a message', QMessageBox.Ok)
self
.
status_text
.
setText
(
'Simulation started...'
)
def
on_simulator_end
(
self
):
#QMessageBox.information(self, 'Simulator End',
# 'This is only a message', QMessageBox.Ok)
self
.
status_text
.
setText
(
'Simulation ended!!'
)
def
main
():
app
=
QApplication
(
sys
.
argv
)
...
...
scope.py
View file @
19f17f15
...
...
@@ -111,11 +111,11 @@ def analyze_pole_zero(figure, b, a, p, q, grid):
p2
=
np
.
roots
(
a2
)
z2
=
np
.
roots
(
b2
)
except
ValueError
,
exVE
:
except
ValueError
as
exVE
:
warningMessage
=
'
%
s'
%
exVE
except
ZeroDivisionError
,
exZDE
:
except
ZeroDivisionError
as
exZDE
:
warningMessage
=
'
%
s'
%
exZDE
except
RuntimeWarning
,
exRW
:
except
RuntimeWarning
as
exRW
:
warningMessage
=
'
%
s'
%
exRW
if
warningMessage
!=
''
:
...
...
@@ -192,11 +192,11 @@ def analyze_frequency_response(figure, b, a, p, q, grid):
wd
,
hd
=
signal
.
freqz
(
b2
,
a2
)
hd_dB
=
20
*
log10
(
abs
(
hd
))
except
ValueError
,
exVE
:
except
ValueError
as
exVE
:
warningMessage
=
'
%
s'
%
exVE
except
ZeroDivisionError
,
exZDE
:
except
ZeroDivisionError
as
exZDE
:
warningMessage
=
'
%
s'
%
exZDE
except
RuntimeWarning
,
exRW
:
except
RuntimeWarning
as
exRW
:
warningMessage
=
'
%
s'
%
exRW
...
...
simcore.py
View file @
19f17f15
...
...
@@ -24,6 +24,8 @@ from scipy import signal
from
bitstring
import
BitArray
from
Verilog_VCD
import
*
from
snippets
import
*
...
...
@@ -33,12 +35,21 @@ class Simcore:
'''
def
simfilter
(
self
,
b
,
a
,
x
,
structure
,
model
,
structure
,
language
,
engine
,
name
,
busX
,
busY
,
busC
,
scalingX
,
scalingC
):
self
.
name
=
name
self
.
structure
=
structure
self
.
model
=
model
self
.
language
=
language
self
.
engine
=
engine
#TODO: debugging prints
print
(
'structure'
,
structure
)
print
(
'language'
,
language
)
print
(
'engine'
,
engine
)
print
(
'name'
,
name
)
self
.
busX
=
busX
self
.
busY
=
busY
...
...
@@ -73,7 +84,7 @@ class Simcore:
self
.
_generateDUT
()
self
.
_generateTB
(
x
)
return
self
.
_simulation
()
return
self
.
_simulation
(
x
)
#***********************************************#
...
...
@@ -83,7 +94,6 @@ class Simcore:
def
_generateDUT
(
self
):
# All the products has an extra integer (the 2^1 factor)
# A fixed point number is comprised by integer (P) and fractional bits (Q)
# When you calculate the C product of A and B, then => PC = PA + PB; QC = QA + QB;
...
...
@@ -112,11 +122,10 @@ class Simcore:
hdl
=
snippets
()
if
self
.
model
==
'vhdl
'
:
if
self
.
language
==
'VHDL
'
:
structure
=
self
.
structure
name
=
self
.
structure
realization
=
'direct_form_1_transposed'
name
=
self
.
name
# Code generation for a Transposed Direct Form FIR
hdlFile
=
open
(
'{!s}.vhd'
.
format
(
name
),
'w'
)
...
...
@@ -127,62 +136,27 @@ class Simcore:
hdlFile
.
write
(
hdl
.
entity
(
name
,
busX
,
busY
))
hdlFile
.
write
(
'
\n
'
)
hdlFile
.
write
(
hdl
.
architectureHeader
(
name
,
realization
))
hdlFile
.
write
(
hdl
.
architectureHeader
(
name
,
structure
))
hdlFile
.
write
(
'
\n
'
)
# *** DECLARE INTERNAL SIGNALS ***
if
realization
==
'direct_form_1_transposed'
:
hdlFile
.
write
(
"-- Z^-1 delay blocks
\n
"
)
# Z blocks in the b coefficients side
for
ii
in
range
(
self
.
M
-
1
):
hdlFile
.
write
(
'signal zb{!s}, zb{!s}_next: signed({!s} downto 0);
\n
'
.
format
(
ii
,
ii
,
busX
+
busC
-
1
-
1
))
hdlFile
.
write
(
"
\n
"
)
# Z blocks in the a coefficients side
for
ii
in
range
(
self
.
N
-
1
):
if
ii
!=
0
:
hdlFile
.
write
(
'signal za{!s}, za{!s}_next: signed({!s} downto 0);
\n
'
.
format
(
ii
,
ii
,
busX
+
busC
-
1
-
1
))
else
:
hdlFile
.
write
(
'signal za{!s}, za{!s}_next: signed({!s} downto 0);
\n
'
.
format
(
ii
,
ii
,
busX
-
1
))
hdlFile
.
write
(
"
\n
"
)
hdlFile
.
write
(
hdl
.
signalZ
(
self
.
M
,
self
.
N
,
busX
,
busY
,
busC
,
structure
))
hdlFile
.
write
(
'
\n
'
)
hdlFile
.
write
(
"-- Filter constants
\n
"
)
for
ii
in
range
(
self
.
M
):
hdlFile
.
write
(
'signal b{!s}: signed({!s} downto 0);
\n
'
.
format
(
ii
,
busC
-
1
))
hdlFile
.
write
(
"
\n
"
)
for
ii
in
range
(
self
.
N
):
if
ii
!=
0
:
hdlFile
.
write
(
'signal a{!s}: signed({!s} downto 0);
\n
'
.
format
(
ii
,
busC
-
1
))
hdlFile
.
write
(
"
\n
"
)
hdlFile
.
write
(
"-- Filter Adders
\n
"
)
for
ii
in
range
(
self
.
M
-
1
):
hdlFile
.
write
(
'signal sb{!s}: signed({!s} downto 0);
\n
'
.
format
(
ii
,
busX
+
busC
-
1
-
1
))
hdlFile
.
write
(
"
\n
"
)
for
ii
in
range
(
self
.
N
-
1
):
if
ii
!=
0
:
hdlFile
.
write
(
'signal sa{!s}: signed({!s} downto 0);
\n
'
.
format
(
ii
,
busX
+
busC
-
1
-
1
))
else
:
hdlFile
.
write
(
'signal sa{!s}: signed({!s} downto 0);
\n
'
.
format
(
ii
,
busX
-
1
))
hdlFile
.
write
(
"
\n
"
)
hdlFile
.
write
(
hdl
.
signalC
(
self
.
M
,
self
.
N
,
busC
,
structure
))
hdlFile
.
write
(
'
\n
'
)
hdlFile
.
write
(
"-- Filter Products
\n
"
)
for
ii
in
range
(
self
.
M
):
hdlFile
.
write
(
'signal pb{!s}: signed({!s} downto 0);
\n
'
.
format
(
ii
,
busX
+
busC
-
1
-
1
))
hdlFile
.
write
(
"
\n
"
)
for
ii
in
range
(
self
.
N
):
if
ii
!=
0
:
hdlFile
.
write
(
'signal pa{!s}: signed({!s} downto 0);
\n
'
.
format
(
ii
,
busX
+
busC
-
1
-
1
))
hdlFile
.
write
(
"
\n
"
)
hdlFile
.
write
(
hdl
.
signalAdders
(
self
.
M
,
self
.
N
,
busX
,
busY
,
busC
,
structure
))
hdlFile
.
write
(
'
\n
'
)
hdlFile
.
write
(
hdl
.
signalProducts
(
self
.
M
,
self
.
N
,
busX
,
busY
,
busC
,
structure
))
hdlFile
.
write
(
'
\n
'
)
# Intermediate signal for df1: assume that the width is the same that in x!
# NOTE: we have set v as equal to X, but this may lead to overflow troubles!!!!!!
hdlFile
.
write
(
'signal v: signed({!s} downto 0);
\n\n
'
.
format
(
busX
-
1
))
hdlFile
.
write
(
hdl
.
signalFeedback
(
self
.
M
,
self
.
N
,
busX
,
busY
,
busC
,
structure
))
hdlFile
.
write
(
'
\n
'
)
hdlFile
.
write
(
"-- Begin Architecture
\n
"
)
hdlFile
.
write
(
"begin
\n\n
"
)
...
...
@@ -193,81 +167,34 @@ class Simcore:
hdlFile
.
write
(
'-- Arithmetics
\n
'
)
hdlFile
.
write
(
"
\n
"
)
# Coefficients
for
ii
in
range
(
self
.
M
):
hdlFile
.
write
(
'b{!s} <=
\"
{!s}
\"
; -- {!s}
\n
'
.
format
(
ii
,
self
.
icoefB
[
ii
],
self
.
coefB
[
ii
]))
hdlFile
.
write
(
hdl
.
assignCoeff
(
M
,
N
,
self
.
icoefB
,
self
.
coefB
,
self
.
icoefA
,
self
.
coefA
,
structure
))
hdlFile
.
write
(
"
\n
"
)
for
ii
in
range
(
self
.
N
):
if
ii
!=
0
:
hdlFile
.
write
(
'a{!s} <=
\"
{!s}
\"
; -- {!s}
\n
'
.
format
(
ii
,
self
.
icoefA
[
ii
],
self
.
coefA
[
ii
]))
hdlFile
.
write
(
"
\n
"
)
# Products
for
ii
in
range
(
self
.
M
):
hdlFile
.
write
(
'pb{!s} <= v * b{!s};
\n
'
.
format
(
ii
,
ii
))
hdlFile
.
write
(
"
\n
"
)
for
ii
in
range
(
self
.
N
):
if
ii
!=
0
:
hdlFile
.
write
(
'pa{!s} <= v * a{!s};
\n
'
.
format
(
ii
,
ii
))
hdlFile
.
write
(
"
\n
"
)
hdlFile
.
write
(
hdl
.
arithProduct
(
M
,
N
,
busX
,
busY
,
busC
,
structure
))
hdlFile
.
write
(
"
\n
"
)
# Sums
for
ii
in
range
(
self
.
M
-
1
):
hdlFile
.
write
(
'sb{!s} <= pb{!s} + zb{!s};
\n
'
.
format
(
ii
,
ii
,
ii
))
hdlFile
.
write
(
"
\n\n
"
)
for
ii
in
range
(
self
.
N
-
1
):
if
ii
==
0
:
hdlFile
.
write
(
'sa{!s} <= sig_in + za{!s};
\n
'
.
format
(
ii
,
ii
))
else
:
hdlFile
.
write
(
'sa{!s} <= pa{!s} + za{!s};
\n
'
.
format
(
ii
,
ii
,
ii
))
hdlFile
.
write
(
"
\n\n
"
)
hdlFile
.
write
(
hdl
.
arithAdders
(
M
,
N
,
structure
))
hdlFile
.
write
(
"
\n
"
)
hdlFile
.
write
(
'-- Signal link, trunk, overflow, fixed point...
\n
'
)
for
ii
in
range
(
self
.
M
):
if
ii
==
0
:
# Why always is a Zero as MSB in si? --> this is because the input is always a positive number?????
if
self
.
M
==
1
:
hdlFile
.
write
(
hdl
.
convertFixedPoint
(
'pb{!s}'
.
format
(
ii
),
self
.
busC
[
0
]
+
self
.
busX
[
0
]
-
1
,
self
.
busC
[
1
]
+
self
.
busX
[
1
],
'sig_out'
,
self
.
busY
[
0
],
self
.
busY
[
1
]))
else
:
hdlFile
.
write
(
hdl
.
convertFixedPoint
(
'sb{!s}'
.
format
(
ii
),
self
.
busC
[
0
]
+
self
.
busX
[
0
]
-
1
,
self
.
busC
[
1
]
+
self
.
busX
[
1
],
'sig_out'
,
self
.
busY
[
0
],
self
.
busY
[
1
]))
elif
ii
==
(
self
.
M
-
1
):
hdlFile
.
write
(
'zb{!s}_next <= pb{!s};
\n
'
.
format
(
ii
-
1
,
ii
))
else
:
hdlFile
.
write
(
'zb{!s}_next <= sb{!s};
\n
'
.
format
(
ii
-
1
,
ii
))
hdlFile
.
write
(
hdl
.
connectBlocks
(
M
,
N
,
self
.
busX
,
self
.
busY
,
self
.
busC
,
structure
))
hdlFile
.
write
(
'
\n
'
)
for
ii
in
range
(
self
.
N
):
if
ii
==
0
:
# Why always is a Zero as MSB in si? --> this is because the input is always a positive number?????
if
self
.
N
==
1
:
hdlFile
.
write
(
'v <= sig_in;
\n
'
)
else
:
hdlFile
.
write
(
'v <= sa0;
\n
'
)
elif
ii
==
1
:
if
ii
!=
(
self
.
N
-
1
):
hdlFile
.
write
(
hdl
.
convertFixedPoint
(
'sa{!s}'
.
format
(
ii
),
self
.
busC
[
0
]
+
self
.
busX
[
0
]
-
1
,
self
.
busC
[
1
]
+
self
.
busX
[
1
],
'za{!s}_next'
.
format
(
ii
-
1
),
self
.
busX
[
0
],
self
.
busX
[
1
]))
else
:
hdlFile
.
write
(
hdl
.
convertFixedPoint
(
'pa{!s}'
.
format
(
ii
),
self
.
busC
[
0
]
+
self
.
busX
[
0
]
-
1
,
self
.
busC
[
1
]
+
self
.
busX
[
1
],
'za{!s}_next'
.
format
(
ii
-
1
),
self
.
busX
[
0
],
self
.
busX
[
1
]))
elif
ii
==
(
self
.
N
-
1
):
hdlFile
.
write
(
'za{!s}_next <= pa{!s};
\n
'
.
format
(
ii
-
1
,
ii
))
else
:
hdlFile
.
write
(
'za{!s}_next <= sa{!s};
\n
'
.
format
(
ii
-
1
,
ii
))
hdlFile
.
write
(
"
\n\n
"
)
hdlFile
.
write
(
'end {!s};
\n
'
.
format
(
realization
))
hdlFile
.
write
(
'end {!s};
\n
'
.
format
(
structure
))
hdlFile
.
close
()
elif
self
.
model
==
'v
erilog'
:
elif
self
.
language
==
'V
erilog'
:
structure
=
self
.
structure
realization
=
'direct_form_transposed'
# Verilog Code generation for a Transposed Direct Form FIR
hdlFile
=
open
(
'{!s}.v'
.
format
(
structure
),
'w'
)
...
...
@@ -323,7 +250,7 @@ class Simcore:
hdlFile
.
write
(
"
\n
"
)
hdlFile
.
write
(
"always @(posedge clk) begin: {!s}
\n
"
.
format
(
realization
))
hdlFile
.
write
(
"always @(posedge clk) begin: {!s}
\n
"
.
format
(
structure
))
hdlFile
.
write
(
" if (rst == 1'b1) begin
\n
"
)
for
ii
in
range
(
self
.
M
-
1
):
hdlFile
.
write
(
' z[{!s}] <= 0;
\n
'
.
format
(
ii
))
...
...
@@ -365,7 +292,7 @@ class Simcore:
else
:
print
(
"Error: simulation
mod
e not recognized
\n
"
);
print
(
"Error: simulation
languag
e not recognized
\n
"
);
return
0
...
...
@@ -373,76 +300,36 @@ class Simcore:
def
_generateTB
(
self
,
x
):
#if self.language == 'vhdl':
# TBD: the process of input signal and output value parse
# must be aligned and configurable
# This Verilog file is common for verilog and vhdl DUTs in IVerilog
# If we want to add support for more simulators, then would be necessary VHDL
# Code generation for the testbench - myhdl link
hdl
=
snippets
()
busX
=
self
.
busX
[
0
]
+
self
.
busX
[
1
]
busY
=
self
.
busY
[
0
]
+
self
.
busY
[
1
]
busC
=
self
.
busC
[
0
]
+
self
.
busC
[
1
]
structure
=
self
.
structure
tbFile
=
open
(
'tb_{!s}.v'
.
format
(
structure
),
'w'
)
tbFile
.
write
(
"module tb_{!s};
\n
"
.
format
(
structure
))
tbFile
.
write
(
"
\n
"
)
tbFile
.
write
(
"reg clk;
\n
"
)
tbFile
.
write
(
"reg rst;
\n
"
)
tbFile
.
write
(
"reg [{!s}:0] sig_in;
\n
"
.
format
(
busX
-
1
))
tbFile
.
write
(
"wire [{!s}:0] sig_out;
\n
"
.
format
(
busY
-
1
))
tbFile
.
write
(
"
\n
"
)
tbFile
.
write
(
"{!s} dut(
\n
"
.
format
(
structure
))
tbFile
.
write
(
" clk,
\n
"
)
tbFile
.
write
(
" rst,
\n
"
)
tbFile
.
write
(
" sig_in,
\n
"
)
tbFile
.
write
(
" sig_out
\n
"
)
tbFile
.
write
(
");
\n
"
)
tbFile
.
write
(
"
\n
"
)
clockPosWidth
=
25
clockNegWidth
=
25
tbFile
.
write
(
"always begin
\n
"
)
tbFile
.
write
(
" clk <= 1;
\n
"
)
tbFile
.
write
(
" #{!s};
\n
"
.
format
(
clockNegWidth
))
tbFile
.
write
(
" clk <= 0;
\n
"
)
tbFile
.
write
(
" $display(
\"
%
d,
%
b,
%
b
\"
, $time, sig_in, sig_out);
\n
"
)
tbFile
.
write
(
" #{!s};
\n
"
.
format
(
clockPosWidth
))
tbFile
.
write
(
"end
\n
"
)
tbFile
.
write
(
"
\n
"
)
tbFile
.
write
(
"
\n
"
)
tbFile
.
write
(
"
\n
"
)
tbFile
.
write
(
"
\n
"
)
tbFile
.
write
(
"initial begin
\n
"
)
tbFile
.
write
(
" $dumpfile(
\"
{!s}.vcd
\"
);
\n
"
.
format
(
structure
))
tbFile
.
write
(
" $dumpvars;
\n
"
)
tbFile
.
write
(
"end
\n
"
)
tbFile
.
write
(
"
\n
"
)
tbFile
.
write
(
"initial begin
\n
"
)
tbFile
.
write
(
' sig_in <= {!s}
\'
b{!s}; // signal is initially zero
\n
'
.
format
(
busX
,
0
))
tbFile
.
write
(
" rst <= 1'b1; #100;
\n
"
)
tbFile
.
write
(
" rst <= 1'b0; #150;
\n
"
)
xb
=
self
.
_quantizer
(
x
,
self
.
busX
,
staircase
=
'midtread'
)
for
ii
in
range
(
len
(
x
)):
tbFile
.
write
(
' #50; sig_in <= {!s}
\'
b{!s}; // {!s}
\n
'
.
format
(
busX
,
xb
[
ii
],
x
[
ii
]))
tbFile
.
write
(
" $finish;
\n
"
)
tbFile
.
write
(
"end
\n
"
)
tbFile
.
write
(
"
\n
"
)
busX
=
self
.
busX
[
0
]
+
self
.
busX
[
1
]
busY
=
self
.
busY
[
0
]
+
self
.
busY
[
1
]
name
=
self
.
name
xb
=
self
.
_quantizer
(
x
,
self
.
busX
,
staircase
=
'midtread'
)
if
self
.
engine
==
'Icarus Verilog'
:
tbFile
.
write
(
"
\n
"
)
tbFile
.
write
(
"endmodule
\n\n
"
)
tbFile
=
open
(
'tb_{!s}.v'
.
format
(
name
),
'w'
)
tbFile
.
write
(
hdl
.
verilogTestBench
(
name
,
xb
,
x
,
busX
,
busY
))
tbFile
.
close
()
elif
self
.
engine
==
'GHDL'
:
tbFile
=
open
(
'tb_{!s}.vhd'
.
format
(
name
),
'w'
)
tbFile
.
write
(
hdl
.
vhdlTestBench
(
name
,
xb
,
x
,
busX
,
busY
))
tbFile
.
close
()
def
_quantizer
(
self
,
signal
,
bus
,
xm
=
1
,
staircase
=
'midtread'
):
...
...
@@ -457,18 +344,18 @@ class Simcore:
B
=
bus
[
1
]
+
bus
[
0
]
-
1
# Generate an
if
staircase
==
'midtread'
:
delta
=
1
/
2
delta
=
0.5
elif
staircase
==
'midriser'
:
delta
=
0
else
:
print
(
"Staircase model for quantizer unknown: using default"
)
delta
=
1
/
2
delta
=
0.5
# Convert the normalized signal to a integer value
signalInt
=
np
.
floor
(
signalNorm
*
(
2
**
(
bus
[
1
]))
+
delta
)
signalScaled
=
signalNorm
*
(
2
**
(
bus
[
1
]))
+
delta
signalInt
=
np
.
floor
(
signalScaled
)
# Trunk if the integers cannot be represented using the bus width
# Then assign the correspondent binary code
...
...
@@ -479,77 +366,136 @@ class Simcore:
elif
signalInt
[
ii
]
<
(
-
1
*
2
**
B
):
signalInt
[
ii
]
=
-
1
*
2
**
B
xBinary
.
append
(
np
.
binary_repr
(
int
(
signalInt
[
ii
]),
width
=
B
+
1
))
return
xBinary
def
_simulation
(
self
,
s
imulator
=
'iverilog'
):
def
_simulation
(
self
,
s
timulus
):
structure
=
self
.
structure
name
=
self
.
name
totalSamples
=
len
(
stimulus
)
timeStep
=
50
if
self
.
model
==
'v
erilog'
:
if
self
.
language
==
'V
erilog'
:
dutExtension
=
'v'
elif
self
.
model
==
'vhdl
'
:
elif
self
.
language
==
'VHDL
'
:
dutExtension
=
'vhd'
else
:
print
(
'Unknown model: assuming verilog'
)
dutExtension
=
'v'
print
(
'TESTPOINT_1'
)
print
(
'Cosimulation...'
)
print
(
'TEST iverilog -o {!s} {!s}.{!s} tb_{!s}.v'
.
format
(
structure
,
structure
,
dutExtension
,
structure
))
# Check if verilog/vhdl filter description is available TBD: make it a variable!!!!!!!!!!!!!!!!
if
not
os
.
path
.
isfile
(
'{!s}.
vhd'
.
format
(
structure
)):
if
not
os
.
path
.
isfile
(
'{!s}.
{!s}'
.
format
(
name
,
dutExtension
)):
print
(
'File not found: Run generation first'
)
return
None
# Clean design
if
os
.
path
.
isfile
(
'{!s}'
.
format
(
structur
e
)):
if
os
.
path
.
isfile
(
'{!s}'
.
format
(
nam
e
)):
print
(
'Existing compiled design: erase!! '
)
cmd
=
'rm {!s}'
.
format
(
structure
)
cmd
=
'rm {!s}'
.
format
(
name
)
os
.
system
(
cmd
)
if
self
.
engine
==
'Icarus Verilog'
:
# Running Icarus verilog
# ****** TBD: Set the dut file extension according with the HDL language
cmd
=
'iverilog -g2012 -o {!s} {!s}.{!s} tb_{!s}.v'
.
format
(
name
,
name
,
dutExtension
,
name
)
os
.
system
(
cmd
)
# Running Icarus verilog
# ****** TBD: Set the dut file extension according with the HDL language
cmd
=
'iverilog -g2012 -o {!s} {!s}.{!s} tb_{!s}.v'
.
format
(
structure
,
structure
,
dutExtension
,
structure
)
os
.
system
(
cmd
)
cmd
=
'vvp {!s}'
.
format
(
structure
)
cmd
=
'vvp {!s}'
.
format
(
name
)
proc
=
subprocess
.
Popen
(
cmd
,
stdout
=
subprocess
.
PIPE
,
shell
=
True
)
(
out
,
err
)
=
proc
.
communicate
()
elif
self
.
engine
==
'GHDL'
:
#Running GHDL
stopTime
=
(
5
+
totalSamples
)
*
timeStep
# Analyze the UUT
cmd
=
'ghdl -a {!s}.vhd'
.
format
(
name
)
proc
=
subprocess
.
Popen
(
cmd
,
stdout
=
subprocess
.
PIPE
,
shell
=
True
)
(
out
,
err
)
=
proc
.
communicate
()
# Analyze the TestBench
cmd
=
'ghdl -a tb_{!s}.vhd'
.
format
(
name
)
proc
=
subprocess
.
Popen
(
cmd
,
stdout
=
subprocess
.
PIPE
,
shell
=
True
)
(
out
,
err
)
=
proc
.
communicate
()
# Build an executable file
cmd
=
'ghdl -e tb_{!s}'
.
format
(
name
)
proc
=
subprocess
.
Popen
(
cmd
,
stdout
=
subprocess
.
PIPE
,
shell
=
True
)
(
out
,
err
)
=
proc
.
communicate
()
print
(
'GHDL executed'
)
# Run the executable
cmd
=
'ghdl -r tb_{!s} --vcd={!s}.vcd --stop-time={!s}ns'
.
format
(
name
,
name
,
stopTime
)
proc
=
subprocess
.
Popen
(
cmd
,
stdout
=
subprocess
.
PIPE
,
shell
=
True
)
(
out
,
err
)
=
proc
.
communicate
()
print
(
'GHDL runned'
)
proc
=
subprocess
.
Popen
(
cmd
,
stdout
=
subprocess
.
PIPE
,
shell
=
True
)
(
out
,
err
)
=
proc
.
communicate
()
reportFile
=
open
(
'{!s}_report.txt'
.
format
(
structure
),
'wb'
)
reportFile
.
write
(
out
)
reportFile
.
close
()
#print("program output: {!s}".format(out))
print
(
'TESTPOINT_4'
)
parsedOutput
=
[]
with
open
(
'{!s}_report.txt'
.
format
(
structure
),
'r'
)
as
csvfile
:
csvString
=
csv
.
reader
(
csvfile
,
delimiter
=
','
,
quotechar
=
'|'
,
skipinitialspace
=
True
)
ii
=
0
for
row
in
csvString
:
# Only the lines with three fields qualifies
if
len
(
row
)
==
3
:
# Only the values beyond a time are considered valids samples
# Note that init and reset values must be neglected
if
int
(
row
[
0
])
>
250
:
a
=
BitArray
(
bin
=
row
[
2
])
#print(a.int)
parsedOutput
.
append
(
a
.
int
/
float
(
2
**
(
self
.
busY
[
1
])
))
ii
=
ii
+
1
# The fixed point format for Y is calculated in an automatic fashion!!!
connect_sample_out
=
np
.
zeros
(
len
(
parsedOutput
))
for
ii
in
range
(
len
(
parsedOutput
)):
connect_sample_out
[
ii
]
=
parsedOutput
[
ii
]
# *****************************************************************************
#vcdDebug = parse_vcd('{!s}.vcd'.format(name),
# only_sigs=1
# )
#print(vcdDebug)
if
self
.
engine
==
'Icarus Verilog'
:
topName
=
'tb_{!s}'
.
format
(
name
)
timeScale
=
1
elif
self
.
engine
==
'GHDL'
:
topName
=
'uut'
timeScale
=
1000000
print
(
'Parsing VCD'
)
vcd
=
parse_vcd
(
'{!s}.vcd'
.
format
(
name
),
#use_stdout=1,
siglist
=
[
'{!s}sig_out[{!s}:0]'
.
format
(
topName
,
self
.
busY
[
0
]
+
self
.
busY
[
1
]
-
1
)]
)
for
key
in
sorted
(
vcd
):
vcdKey
=
key
print
(
'Parsed VCD'
)
print
(
vcd
[
vcdKey
][
'tv'
])
vcdParsedOutput
=
[]
#vcdBinaryOutput = []
outCurrentTime
=
0
vcdCurrentValue
=
vcd
[
vcdKey
][
'tv'
][
0
][
1
]
binOffset
=
self
.
busY
[
0
]
+
self
.
busY
[
1
]
-
len
(
vcdCurrentValue
)
for
jj
in
range
(
binOffset
):
vcdCurrentValue
=
'0'
+
vcdCurrentValue
for
ii
in
range
(
len
(
vcd
[
vcdKey
][
'tv'
])):
vcdNextTime
=
vcd
[
vcdKey
][
'tv'
][
ii
][
0
]
vcdNextTime
=
vcdNextTime
/
timeScale
vcdNextValue
=
vcd
[
vcdKey
][
'tv'
][
ii
][
1
]
while
vcdNextTime
>
outCurrentTime
:
a
=
BitArray
(
bin
=
vcdCurrentValue
)
#vcdBinaryOutput.append(vcdCurrentValue)
vcdParsedOutput
.
append
(
a
.
int
/
float
(
2
**
(
self
.
busY
[
1
])))
vcdCurrentValue
=
vcdNextValue
binOffset
=
self
.
busY
[
0
]
+
self
.
busY
[
1
]
-
len
(
vcdCurrentValue
)
for
jj
in
range
(
binOffset
):
vcdCurrentValue
=
'0'
+
vcdCurrentValue
outCurrentTime
+=
timeStep
print
(
'Signal reconstructed'
)
# remove the output samples from filter initialization
for
ii
in
range
(
5
):
vcdParsedOutput
.
pop
(
0
)
#vcdBinaryOutput.pop(0)
connect_sample_out
=
np
.
zeros
(
len
(
vcdParsedOutput
))
for
ii
in
range
(
len
(
vcdParsedOutput
)):
connect_sample_out
[
ii
]
=
vcdParsedOutput
[
ii
]
return
connect_sample_out
...
...
simulator.py
View file @
19f17f15
...
...
@@ -43,6 +43,8 @@ class Simulator(Simcore):
# Declare signals that will be sended to other classes
simulatorUpdatedSignal
=
pyqtSignal
()
simulatorBeginSignal
=
pyqtSignal
()
simulatorEndSignal
=
pyqtSignal
()
...
...
@@ -73,7 +75,7 @@ class Simulator(Simcore):
elif
selectedPlot
==
'Power Spectrum'
:
scopePower
(
self
.
figSimulator
,
simulatorWaves
,
grid
)
else
:
scope
Dual
(
self
.
figSimulator
,
simulatorWaves
,
grid
)
scope
Time
(
self
.
figSimulator
,
simulatorWaves
,
grid
)
# Change pushbutton state
self
.
pbUpdateFilter
.
setDisabled
(
True
)
...
...
@@ -84,6 +86,7 @@ class Simulator(Simcore):
def
execute_simulation
(
self
,
b
,
a
,
x
,
name
,
language
,
engine
,
structure
,
busX
,
busY
,
busC
,
scalingX
,
scalingC
):
""" Redraws the figure
...
...
@@ -94,16 +97,19 @@ class Simulator(Simcore):
warningMessage
=
''
warnings
.
simplefilter
(
'error'
)
try
:
self
.
simulatorBeginSignal
.
emit
()
self
.
yfloat
=
signal
.
lfilter
(
b
,
a
,
x
)
self
.
yhdl
=
self
.
simfilter
(
b
,
a
,
x
,
'testfilt'
,
'vhdl'
,
structure
,
language
,
engine
,
name
,
busX
,
busY
,
busC
,
scalingX
,
scalingC
)
except
ValueError
,
exVE
:
self
.
simulatorEndSignal
.
emit
()
except
ValueError
as
exVE
:
warningMessage
=
'
%
s'
%
exVE
except
ZeroDivisionError
,
exZDE
:
except
ZeroDivisionError
as
exZDE
:
warningMessage
=
'
%
s'
%
exZDE
except
RuntimeWarning
,
exRW
:
except
RuntimeWarning
as
exRW
:
warningMessage
=
'
%
s'
%
exRW
if
warningMessage
!=
''
:
...
...
@@ -156,14 +162,14 @@ class Simulator(Simcore):
labelSimulatorLanguage
=
QLabel
(
'HDL Language:'
)
self
.
comboSimulatorLanguage
=
QComboBox
()
self
.
comboSimulatorLanguage
.
addItem
(
"Verilog"
)
self
.
comboSimulatorLanguage
.
addItem
(
"VHDL"
)
self
.
comboSimulatorLanguage
.
addItem
(
"Verilog"
)
self
.
comboSimulatorLanguage
.
setEditable
(
True
)
self
.
comboSimulatorLanguage
.
lineEdit
()
.
setReadOnly
(
True
)
self
.
comboSimulatorLanguage
.
lineEdit
()
.
setAlignment
(
Qt
.
AlignRight
)
for
ii
in
range
(
self
.
comboSimulatorLanguage
.
count
()):
self
.
comboSimulatorLanguage
.
setItemData
(
ii
,
Qt
.
AlignRight
,
Qt
.
TextAlignmentRole
)
self
.
connect
(
self
.
comboSimulatorLanguage
,
SIGNAL
(
'currentIndexChanged(int)'
),
self
.
on_
simulator
_modified
)
self
.
connect
(
self
.
comboSimulatorLanguage
,
SIGNAL
(
'currentIndexChanged(int)'
),
self
.
on_
language
_modified
)
labelSimulatorScope
=
QLabel
(
'Output Scope:'
)
self
.
comboSimulatorScope
=
QComboBox
()
...
...
@@ -240,7 +246,7 @@ class Simulator(Simcore):
hboxWorkFolder
.
addWidget
(
labelWorkFolder
)
hboxWorkFolder
.
addWidget
(
self
.
textWorkFolder
)
hboxWorkFolder
.
addWidget
(
self
.
pbWorkFolder
)
hboxWorkFolder
.
setStretch
(
0
,
4
)
hboxWorkFolder
.
setStretch
(
0
,
5
)
hboxWorkFolder
.
setStretch
(
1
,
4
)
hboxWorkFolder
.
setStretch
(
2
,
1
)
...
...
@@ -280,4 +286,15 @@ class Simulator(Simcore):
self
.
simulatorUpdatedSignal
.
emit
()
def
on_language_modified
(
self
):
# TODO: provisional, check the structure change and force to DF1 Trans
activeLanguage
=
str
(
self
.
comboSimulatorLanguage
.
currentText
())
if
activeLanguage
==
'VHDL'
:
self
.
on_simulator_modified
()
else
:
QMessageBox
.
information
(
self
,
'Simulation Language'
,
'Sorry, but
%
s is not supported yet!'
%
activeLanguage
,
QMessageBox
.
Ok
)
self
.
comboSimulatorLanguage
.
setCurrentIndex
(
0
)
snippets.py
View file @
19f17f15
...
...
@@ -16,14 +16,6 @@
import
sys
import
os
import
subprocess
import
csv
import
numpy
as
np
from
scipy
import
signal
from
bitstring
import
BitArray
class
snippets
:
...
...
@@ -63,17 +55,17 @@ class snippets:
return
entityString
%
entityDict
def
architectureHeader
(
self
,
name
,
realization
):
def
architectureHeader
(
self
,
name
,
structure
):
#TBD: include the appropriated realization diagram
archHeadDict
=
{
'Name'
:
name
,
'
Realization'
:
realization
'
Structure'
:
structure
}
archHeadString
=
(
'architecture
%(
Realization
)
s of
%(Name)
s is
\n\n
'
'architecture
%(
Structure
)
s of
%(Name)
s is
\n\n
'
'-- Architecture depends on selected realization
\n
'
'-- TODO: this header should each different realization
\n
'
'--
\n
'
...
...
@@ -213,9 +205,9 @@ class snippets:
' if (rst =
\'
%(boolRst)
s
\'
) then
\n
'
)
for
ii
in
range
(
M
-
1
):
seqString
+=
' zb
%(ii)
u <=
0
;
\n
'
%
{
'ii'
:
ii
}
seqString
+=
' zb
%(ii)
u <=
(others =>
\'
0
\'
)
;
\n
'
%
{
'ii'
:
ii
}
for
ii
in
range
(
N
-
1
):
seqString
+=
' za
%(ii)
u <=
0
;
\n
'
%
{
'ii'
:
ii
}
seqString
+=
' za
%(ii)
u <=
(others =>
\'
0
\'
)
;
\n
'
%
{
'ii'
:
ii
}
seqString
+=
' else
\n
'
for
ii
in
range
(
M
-
1
):
seqString
+=
' zb
%(ii)
u <= zb
%(ii)
u_next;
\n
'
%
{
'ii'
:
ii
}
...
...
@@ -230,4 +222,390 @@ class snippets:
return
seqString
%
seqDict
def
signalZ
(
self
,
M
,
N
,
busX
,
busY
,
busC
,
structure
):
signalZString
=
'-- Z^-1 delay blocks
\n
'
if
structure
==
'Direct_Form_I_Transposed'
:
# Z blocks in the b coefficients side
for
ii
in
range
(
M
-
1
):
signalZString
+=
'signal zb
%(ii)
u, zb
%(ii)
u_next: signed(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
busX
+
busC
-
1
-
1
}
signalZString
+=
'
\n
'
# Z blocks in the a coefficients side
for
ii
in
range
(
N
-
1
):
if
ii
!=
0
:
bus
=
busX
+
busC
-
1
-
1
else
:
bus
=
busX
-
1
signalZString
+=
'signal za
%(ii)
u, za
%(ii)
u_next: signed(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
bus
}
signalZString
+=
'
\n
'
return
signalZString
def
signalC
(
self
,
M
,
N
,
busC
,
structure
):
signalCString
=
'-- Filter constants
\n
'
if
structure
==
'Direct_Form_I_Transposed'
:
# Z blocks in the b coefficients side
for
ii
in
range
(
M
):
signalCString
+=
'signal b
%(ii)
u: signed(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
busC
-
1
}
signalCString
+=
'
\n
'
# Z blocks in the a coefficients side
for
ii
in
range
(
N
):
if
ii
!=
0
:
signalCString
+=
'signal a
%(ii)
u: signed(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
busC
-
1
}
signalCString
+=
'
\n
'
return
signalCString
def
signalProducts
(
self
,
M
,
N
,
busX
,
busY
,
busC
,
structure
):
signalProductString
=
'-- Filter Products
\n
'
if
structure
==
'Direct_Form_I_Transposed'
:
# Products from B coeff
for
ii
in
range
(
M
):
signalProductString
+=
'signal pb
%(ii)
u: signed(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
busX
+
busC
-
1
-
1
}
signalProductString
+=
'
\n
'
# Products from A coeff
for
ii
in
range
(
N
):
if
ii
!=
0
:
signalProductString
+=
'signal pa
%(ii)
u: signed(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
busX
+
busC
-
1
-
1
}
signalProductString
+=
'
\n
'
# Temporal Products from B coeff
for
ii
in
range
(
M
):
signalProductString
+=
'signal pb
%(ii)
u_temp: signed(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
busX
+
busC
-
1
}
signalProductString
+=
'
\n
'
# Temporal Products from A coeff
for
ii
in
range
(
N
):
if
ii
!=
0
:
signalProductString
+=
'signal pa
%(ii)
u_temp: signed(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
busX
+
busC
-
1
}
signalProductString
+=
'
\n
'
return
signalProductString
def
signalAdders
(
self
,
M
,
N
,
busX
,
busY
,
busC
,
structure
):
signalAddString
=
'-- Filter Adders
\n
'
if
structure
==
'Direct_Form_I_Transposed'
:
# Adders in the b coefficients side
for
ii
in
range
(
M
-
1
):
signalAddString
+=
'signal sb
%(ii)
u: signed(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
busX
+
busC
-
1
-
1
}
signalAddString
+=
'
\n
'
# Adders in the a coefficients side
for
ii
in
range
(
N
-
1
):
if
ii
!=
0
:
bus
=
busX
+
busC
-
1
-
1
else
:
bus
=
busX
-
1
signalAddString
+=
'signal sa
%(ii)
u: signed(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
bus
}
signalAddString
+=
'
\n
'
return
signalAddString
def
signalFeedback
(
self
,
M
,
N
,
busX
,
busY
,
busC
,
structure
):
signalFeedbackString
=
''
if
structure
==
'Direct_Form_I_Transposed'
:
signalFeedbackString
+=
'-- Feedback loop accumulator
\n
'
signalFeedbackString
+=
'signal v: signed(
%(bus)
u downto 0);
\n
'
%
{
'bus'
:
busX
-
1
}
return
signalFeedbackString
def
assignCoeff
(
self
,
M
,
N
,
bCoeffBin
,
bCoeffFloat
,
aCoeffBin
,
aCoeffFloat
,
structure
):
assignCoeffString
=
'-- Assign Coefficient
\n
'
if
structure
==
'Direct_Form_I_Transposed'
:
# Z blocks in the b coefficients side
for
ii
in
range
(
M
):
assignCoeffString
+=
'b
%(ii)
u <=
\"
%(BCoeffBin)
s
\"
; --
%(BCoeffFloat)
f
\n
'
%
{
'ii'
:
ii
,
'BCoeffBin'
:
bCoeffBin
[
ii
],
'BCoeffFloat'
:
bCoeffFloat
[
ii
]}
assignCoeffString
+=
'
\n
'
# Z blocks in the a coefficients side
for
ii
in
range
(
N
):
if
ii
!=
0
:
assignCoeffString
+=
'a
%(ii)
u <=
\"
%(ACoeffBin)
s
\"
; --
%(ACoeffFloat)
f
\n
'
%
{
'ii'
:
ii
,
'ACoeffBin'
:
aCoeffBin
[
ii
],
'ACoeffFloat'
:
aCoeffFloat
[
ii
]}
assignCoeffString
+=
'
\n
'
return
assignCoeffString
def
arithProduct
(
self
,
M
,
N
,
busX
,
busY
,
busC
,
structure
):
arithProductString
=
'-- Product Arithmetics
\n
'
if
structure
==
'Direct_Form_I_Transposed'
:
# Execute products from B side
for
ii
in
range
(
M
):
arithProductString
+=
'pb
%(ii)
u_temp <= v * b
%(ii)
u;
\n
'
%
{
'ii'
:
ii
}
arithProductString
+=
'pb
%(ii)
u <= pb
%(ii)
u_temp(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
busX
+
busC
-
1
-
1
}
arithProductString
+=
'
\n
'
# Execute products from A side
for
ii
in
range
(
N
):
if
ii
!=
0
:
arithProductString
+=
'pa
%(ii)
u_temp <= v * a
%(ii)
u;
\n
'
%
{
'ii'
:
ii
}
arithProductString
+=
'pa
%(ii)
u <= pa
%(ii)
u_temp(
%(bus)
u downto 0);
\n
'
%
{
'ii'
:
ii
,
'bus'
:
busX
+
busC
-
1
-
1
}
arithProductString
+=
'
\n
'
return
arithProductString
def
arithAdders
(
self
,
M
,
N
,
structure
):
arithAddersString
=
'-- Adder Arithmetics
\n
'
if
structure
==
'Direct_Form_I_Transposed'
:
# Z blocks in the b coefficients side
for
ii
in
range
(
M
-
1
):
arithAddersString
+=
'sb
%(ii)
u <= pb
%(ii)
u + zb
%(ii)
u;
\n
'
%
{
'ii'
:
ii
}
arithAddersString
+=
'
\n
'
# Z blocks in the a coefficients side
for
ii
in
range
(
N
-
1
):
if
ii
==
0
:
arithAddersString
+=
'sa
%(ii)
u <= sig_in + za
%(ii)
u;
\n
'
%
{
'ii'
:
ii
}
else
:
arithAddersString
+=
'sa
%(ii)
u <= pa
%(ii)
u + za
%(ii)
u;
\n
'
%
{
'ii'
:
ii
}
arithAddersString
+=
'
\n
'
return
arithAddersString
def
connectBlocks
(
self
,
M
,
N
,
busX
,
busY
,
busC
,
structure
):
connectBlocksString
=
'-- Structure Blocks Connection
\n
'
if
structure
==
'Direct_Form_I_Transposed'
:
# Z blocks in the b coefficients side
for
ii
in
range
(
M
):
if
ii
==
0
:
if
M
==
1
:
outputStage
=
'pb0'
else
:
outputStage
=
'sb0'
connectBlocksString
+=
self
.
convertFixedPoint
(
outputStage
,
busC
[
0
]
+
busX
[
0
]
-
1
,
busC
[
1
]
+
busX
[
1
],
'sig_out'
,
busY
[
0
],
busY
[
1
])
else
:
if
ii
==
(
M
-
1
):
sourceTypeB
=
'p'
else
:
sourceTypeB
=
's'
bConnectDict
=
{
'SrcType'
:
sourceTypeB
,
'Src'
:
ii
,
'Dst'
:
ii
-
1
}
connectBlocksString
+=
'zb
%(Dst)
u_next <=
%(SrcType)
sb
%(Src)
u;
\n
'
%
bConnectDict
connectBlocksString
+=
'
\n
'
for
ii
in
range
(
N
):
if
ii
==
0
:
if
N
==
1
:
connectBlocksString
+=
'v <= sig_in;
\n
'
else
:
connectBlocksString
+=
'v <= sa0;
\n
'
elif
ii
==
1
:
if
ii
==
(
N
-
1
):
firstStageA
=
'pa1'
else
:
firstStageA
=
'sa1'
connectBlocksString
+=
self
.
convertFixedPoint
(
firstStageA
,
busC
[
0
]
+
busX
[
0
]
-
1
,
busC
[
1
]
+
busX
[
1
],
'za0_next'
,
busX
[
0
],
busX
[
1
])
else
:
if
ii
==
(
N
-
1
):
sourceTypeA
=
'p'
else
:
sourceTypeA
=
's'
aConnectDict
=
{
'SrcType'
:
sourceTypeA
,
'Src'
:
ii
,
'Dst'
:
ii
-
1
}
connectBlocksString
+=
'za
%(Dst)
u_next <=
%(SrcType)
sa
%(Src)
u;
\n
'
%
aConnectDict
connectBlocksString
+=
'
\n
'
return
connectBlocksString
def
verilogTestBench
(
self
,
name
,
stimulusBin
,
stimulusFloat
,
busX
,
busY
):
# Write Verilog Dictionary
testBenchDict
=
{
'Name'
:
name
,
'BusX'
:
busX
,
'BusY'
:
busY
,
'MSBX'
:
busX
-
1
,
'MSBY'
:
busY
-
1
,
'ClkPosWidth'
:
25
,
'ClkNegWidth'
:
25
,
}
testBenchString
=
(
'module tb_
%(Name)
s;
\n
'
'
\n
'
'reg clk;
\n
'
'reg rst;
\n
'
'reg [
%(MSBX)
u:0] sig_in;
\n
'
'wire [
%(MSBY)
u:0] sig_out;
\n
'
'
\n
'
'
%(Name)
s dut(
\n
'
' clk,
\n
'
' rst,
\n
'
' sig_in,
\n
'
' sig_out
\n
'
');
\n
'
'
\n
'
'always begin
\n
'
' clk <= 1;
\n
'
' #
%(ClkPosWidth)
u;
\n
'
' clk <= 0;
\n
'
' #
%(ClkNegWidth)
u;
\n
'
'end
\n
'
'
\n
'
'
\n
'
'initial begin
\n
'
' $dumpfile(
\"
%(Name)
s.vcd
\"
);
\n
'
' $dumpvars;
\n
'
'end
\n
'
'
\n
'
'initial begin
\n
'
' sig_in <=
%(BusX)
u
\'
b0; // signal is initially zero
\n
'
' rst <= 1
\'
b1; #100;
\n
'
' rst <= 1
\'
b0; #150;
\n
'
)
%
testBenchDict
for
ii
in
range
(
len
(
stimulusBin
)):
stimulusDict
=
{
'BusX'
:
busX
,
'CoeffBin'
:
stimulusBin
[
ii
],
'CoeffFloat'
:
stimulusFloat
[
ii
]
}
tempString
=
' #50; sig_in <=
%(BusX)
u
\'
b
%(CoeffBin)
s; //
%(CoeffFloat)
f
\n
'
testBenchString
+=
tempString
%
stimulusDict
testBenchString
+=
(
' $finish;
\n
'
'end
\n
'
'
\n
'
'
\n
'
'endmodule
\n
'
)
return
testBenchString
def
vhdlTestBench
(
self
,
name
,
stimulusBin
,
stimulusFloat
,
busX
,
busY
):
# Write Verilog Dictionary
testBenchDict
=
{
'Name'
:
name
,
'BusX'
:
busX
,
'BusY'
:
busY
,
'MSBX'
:
busX
-
1
,
'MSBY'
:
busY
-
1
,
'ClkPosWidth'
:
25
,
'ClkNegWidth'
:
25
,
}
testBenchString
=
(
'LIBRARY ieee;
\n
'
'USE ieee.std_logic_1164.ALL;
\n
'
'USE ieee.numeric_std.all;
\n
'
'
\n
'
'ENTITY tb_
%(Name)
s IS
\n
'
'END tb_
%(Name)
s;
\n
'
'
\n
'
'ARCHITECTURE behavior OF tb_
%(Name)
s IS
\n
'
' -- Component Declaration for the Unit Under Test (UUT)
\n
'
' COMPONENT
%(Name)
s
\n
'
' PORT(
\n
'
' clk: IN std_logic;
\n
'
' rst: IN std_logic;
\n
'
' sig_in: IN signed(
%(MSBX)
u downto 0);
\n
'
' sig_out: OUT signed(
%(MSBY)
u downto 0)
\n
'
' );
\n
'
' END COMPONENT;
\n
'
'
\n
'
' -- Signal Declaration
\n
'
' signal clk : std_logic :=
\'
0
\'
;
\n
'
' signal rst : std_logic :=
\'
1
\'
;
\n
'
' signal sig_in : signed(
%(MSBX)
u downto 0);
\n
'
' signal sig_out : signed(
%(MSBY)
u downto 0);
\n
'
'
\n
'
'BEGIN
\n
'
' -- Instantiate the Unit Under Test (UUT)
\n
'
' uut:
%(Name)
s PORT MAP (
\n
'
' clk => clk,
\n
'
' rst => rst,
\n
'
' sig_in => sig_in,
\n
'
' sig_out => sig_out
\n
'
' );
\n
'
'
\n
'
' clk_process : process
\n
'
' begin
\n
'
' clk <=
\'
1
\'
;
\n
'
' wait for
%(ClkPosWidth)
u ns;
\n
'
' clk <=
\'
0
\'
;
\n
'
' wait for
%(ClkNegWidth)
u ns;
\n
'
' end process;'
'
\n
'
' stimulus_process : process
\n
'
' begin
\n
'
' sig_in <= (others =>
\'
0
\'
);
\n
'
' rst <=
\'
1
\'
; wait for 100 ns;
\n
'
' rst <=
\'
0
\'
; wait for 125 ns;
\n
'
)
%
testBenchDict
for
ii
in
range
(
len
(
stimulusBin
)):
stimulusDict
=
{
'BusX'
:
busX
,
'CoeffBin'
:
stimulusBin
[
ii
],
'CoeffFloat'
:
stimulusFloat
[
ii
]
}
tempString
=
' wait for 50 ns; sig_in <= "
%(CoeffBin)
s"; --
%(CoeffFloat)
f
\n
'
testBenchString
+=
tempString
%
stimulusDict
testBenchString
+=
(
#' wait;\n'
#' -- Terminate Simulation\n'
#' assert false report "Simulation completed" severity failure;\n'
' end process;
\n
'
'
\n
'
'END;
\n
'
)
return
testBenchString
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment