diff --git a/compiler/characterizer/lib.py b/compiler/characterizer/lib.py index 5bf6f579..3909f202 100644 --- a/compiler/characterizer/lib.py +++ b/compiler/characterizer/lib.py @@ -509,32 +509,120 @@ class lib: return datasheet = open(OPTS.openram_temp +'/datasheet.info', 'a+') - datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},{14},{15},{16},{17},{18},{19},{20},{21}\n".format("sram_{0}_{1}_{2}".format(OPTS.word_size, OPTS.num_words, OPTS.tech_name), - OPTS.num_words, - OPTS.num_banks, - OPTS.num_rw_ports, - OPTS.num_w_ports, - OPTS.num_r_ports, - OPTS.tech_name, - corner[1], - corner[2], - corner[0], - round_time(self.char_sram_results["min_period"]), - self.out_dir, - lib_name, - OPTS.word_size, - min(list(map(round_time,self.times["setup_times_LH"]))), - max(list(map(round_time,self.times["setup_times_LH"]))), + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},{9},{10},{11},{12},{13},".format( + "sram_{0}_{1}_{2}".format(OPTS.word_size, OPTS.num_words, OPTS.tech_name), + OPTS.num_words, + OPTS.num_banks, + OPTS.num_rw_ports, + OPTS.num_w_ports, + OPTS.num_r_ports, + OPTS.tech_name, + corner[2], + corner[1], + corner[0], + round_time(self.char_sram_results["min_period"]), + self.out_dir, + lib_name, + OPTS.word_size + + )) - min(list(map(round_time,self.times["setup_times_HL"]))), - max(list(map(round_time,self.times["setup_times_HL"]))), + for port in self.all_ports: + #DIN timings + if port in self.write_ports: + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( + "DIN{1}[{0}:0]".format(self.sram.word_size - 1, port), + min(list(map(round_time,self.times["setup_times_LH"]))), + max(list(map(round_time,self.times["setup_times_LH"]))), - min(list(map(round_time,self.times["hold_times_LH"]))), - max(list(map(round_time,self.times["hold_times_LH"]))), + min(list(map(round_time,self.times["setup_times_HL"]))), + max(list(map(round_time,self.times["setup_times_HL"]))), - min(list(map(round_time,self.times["hold_times_HL"]))), - max(list(map(round_time,self.times["hold_times_HL"]))))) + min(list(map(round_time,self.times["hold_times_LH"]))), + max(list(map(round_time,self.times["hold_times_LH"]))), + + min(list(map(round_time,self.times["hold_times_HL"]))), + max(list(map(round_time,self.times["hold_times_HL"]))) + + )) + + for port in self.all_ports: + #DOUT timing + if port in self.read_ports: + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( + "DOUT{1}[{0}:0]".format(self.sram.word_size - 1, port), + min(list(map(round_time,self.char_port_results[port]["delay_lh"]))), + max(list(map(round_time,self.char_port_results[port]["delay_lh"]))), + + min(list(map(round_time,self.char_port_results[port]["delay_hl"]))), + max(list(map(round_time,self.char_port_results[port]["delay_hl"]))), + + min(list(map(round_time,self.char_port_results[port]["slew_lh"]))), + max(list(map(round_time,self.char_port_results[port]["slew_lh"]))), + + min(list(map(round_time,self.char_port_results[port]["slew_hl"]))), + max(list(map(round_time,self.char_port_results[port]["slew_hl"]))) + + + )) + + for port in self.all_ports: + #CSb timings + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( + "CSb{1}[{0}:0]".format(self.sram.word_size - 1, port), + min(list(map(round_time,self.times["setup_times_LH"]))), + max(list(map(round_time,self.times["setup_times_LH"]))), + + min(list(map(round_time,self.times["setup_times_HL"]))), + max(list(map(round_time,self.times["setup_times_HL"]))), + + min(list(map(round_time,self.times["hold_times_LH"]))), + max(list(map(round_time,self.times["hold_times_LH"]))), + + min(list(map(round_time,self.times["hold_times_HL"]))), + max(list(map(round_time,self.times["hold_times_HL"]))) + + )) + + for port in self.all_ports: + #ADDR timings + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( + "ADDR{1}[{0}:0]".format(self.sram.addr_size - 1, port), + min(list(map(round_time,self.times["setup_times_LH"]))), + max(list(map(round_time,self.times["setup_times_LH"]))), + + min(list(map(round_time,self.times["setup_times_HL"]))), + max(list(map(round_time,self.times["setup_times_HL"]))), + + min(list(map(round_time,self.times["hold_times_LH"]))), + max(list(map(round_time,self.times["hold_times_LH"]))), + + min(list(map(round_time,self.times["hold_times_HL"]))), + max(list(map(round_time,self.times["hold_times_HL"]))) + + )) + + + for port in self.all_ports: + if port in self.readwrite_ports: + + #WEb timings + datasheet.write("{0},{1},{2},{3},{4},{5},{6},{7},{8},".format( + "WEb{1}[{0}:0]".format(self.sram.word_size - 1, port), + min(list(map(round_time,self.times["setup_times_LH"]))), + max(list(map(round_time,self.times["setup_times_LH"]))), + + min(list(map(round_time,self.times["setup_times_HL"]))), + max(list(map(round_time,self.times["setup_times_HL"]))), + + min(list(map(round_time,self.times["hold_times_LH"]))), + max(list(map(round_time,self.times["hold_times_LH"]))), + + min(list(map(round_time,self.times["hold_times_HL"]))), + max(list(map(round_time,self.times["hold_times_HL"]))) + + )) - + datasheet.write("END\n") datasheet.close() diff --git a/compiler/datasheet/datasheet_gen.py b/compiler/datasheet/datasheet_gen.py index 5f163904..b48c2510 100644 --- a/compiler/datasheet/datasheet_gen.py +++ b/compiler/datasheet/datasheet_gen.py @@ -51,41 +51,70 @@ def parse_characterizer_csv(sram,f,pages): csv_reader = csv.reader(csv_file, delimiter=',') line_count = 0 for row in csv_reader: + found = 0 + col = 0 #defines layout of csv file - NAME = row[0] - NUM_WORDS = row[1] - NUM_BANKS = row[2] - NUM_RW_PORTS = row[3] - NUM_W_PORTS = row[4] - NUM_R_PORTS = row[5] - TECH_NAME = row[6] - TEMP = row[8] - VOLT = row[7] - PROC = row[9] - MIN_PERIOD = row[10] - OUT_DIR = row[11] - LIB_NAME = row[12] - WORD_SIZE = row[13] + NAME = row[col] + col += 1 - FF_SETUP_LH_MIN = row[14] - FF_SETUP_LH_MAX = row[15] + NUM_WORDS = row[col] + col += 1 - FF_SETUP_HL_MIN = row[16] - FF_SETUP_HL_MAX = row[17] + NUM_BANKS = row[col] + col += 1 - FF_HOLD_LH_MIN = row[18] - FF_HOLD_LH_MAX = row[19] + NUM_RW_PORTS = row[col] + col += 1 - FF_HOLD_HL_MIN = row[20] - FF_HOLD_HL_MAX = row[21] - + NUM_W_PORTS = row[col] + col += 1 + + NUM_R_PORTS = row[col] + col += 1 + + TECH_NAME = row[col] + col += 1 + + TEMP = row[col] + col += 1 + + VOLT = row[col] + col += 1 + + PROC = row[col] + col += 1 + + MIN_PERIOD = row[col] + col += 1 + + OUT_DIR = row[col] + col += 1 + + LIB_NAME = row[col] + col += 1 + + WORD_SIZE = row[col] + col += 1 + + FF_SETUP_LH_MIN = "1" + FF_SETUP_LH_MAX = "2" + + FF_SETUP_HL_MIN = "3" + FF_SETUP_HL_MAX = "4" + + FF_HOLD_LH_MIN = "5" + FF_HOLD_LH_MAX = "6" + + FF_HOLD_HL_MIN = "7" + FF_HOLD_HL_MAX = "8" + for sheet in pages: - if sheet.name == row[0]: + if sheet.name == NAME: found = 1 #if the .lib information is for an existing datasheet compare timing data @@ -117,31 +146,208 @@ def parse_characterizer_csv(sram,f,pages): #pass if MIN_PERIOD is zero (not supported by analyitcal model) pass - for item in sheet.timing: - if item.parameter == "CSb setup rising": - if float(FF_SETUP_LH_MIN) < float(item.min): - item.min = FF_SETUP_LH_MIN - elif float(FF_SETUP_LH_MAX) > float(item.max): - item.max = FF_SETUP_LH_MAX - if item.parameter == "CSb setup falling": - if float(FF_SETUP_HL_MIN) < float(item.min): - item.min = FF_SETUP_HL_MIN - elif float(FF_SETUP_HL_MAX) > float(item.max): - item.max = FF_SETUP_HL_MAX - if item.parameter == "CSb hold rising": - if float(FF_HOLD_HL_MIN) < float(item.min): - item.min = FF_SETUP_HL_MIN - elif float(FF_HOLD_HL_MAX) > float(item.max): - item.max = FF_SETUP_HL_MAX + while(True): + if(row[col].startswith('DIN')): + start = col + for item in sheet.timing: + if item.parameter.startswith(row[col]): + + if item.parameter.endswith('setup rising'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('setup falling'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('hold rising'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('hold falling'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + col += 1 + + elif(row[col].startswith('DOUT')): + start = col + for item in sheet.timing: + if item.parameter.startswith(row[col]): + + if item.parameter.endswith('cell rise'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('cell fall'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('rise transition'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('fall transition'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + col += 1 + + elif(row[col].startswith('CSb')): + start = col + for item in sheet.timing: + if item.parameter.startswith(row[col]): + + if item.parameter.endswith('setup rising'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('setup falling'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('hold rising'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('hold falling'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + col += 1 + + + elif(row[col].startswith('WEb')): + start = col + for item in sheet.timing: + if item.parameter.startswith(row[col]): + + if item.parameter.endswith('setup rising'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('setup falling'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('hold rising'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('hold falling'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + col += 1 + + + elif(row[col].startswith('ADDR')): + start = col + for item in sheet.timing: + if item.parameter.startswith(row[col]): + + if item.parameter.endswith('setup rising'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('setup falling'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('hold rising'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + elif item.parameter.endswith('hold falling'): + if float(row[col+1]) < float(item.min): + item.min = row[col+1] + if float(row[col+2]) > float(item.max): + item.max = row[col+2] + + col += 2 + + col += 1 + else: + break - if item.parameter == "CSb hold falling": - if float(FF_HOLD_HL_MIN) < float(item.min): - item.min = FF_SETUP_HL_MIN - elif float(FF_HOLD_HL_MAX) > float(item.max): - item.max = FF_SETUP_HL_MAX - #regardless of if there is already a corner for the current sram, append the new corner to the datasheet new_sheet.corners.append(characterization_corners_item(PROC,process_name(PROC),VOLT,TEMP,LIB_NAME.replace(OUT_DIR,'').replace(NAME,''))) @@ -163,14 +369,98 @@ def parse_characterizer_csv(sram,f,pages): new_sheet.operating.append(operating_conditions_item('Operating Frequency (F)*','','',"unknown",'MHz')) #analytical model fails to provide MIN_PERIOD #place holder timing and current data + new_sheet.timing.append(timing_and_current_data_item('Cycle time','2','3','4')) new_sheet.timing.append(timing_and_current_data_item('Access time','2','3','4')) + new_sheet.timing.append(timing_and_current_data_item('Positive clk setup','2','3','4')) new_sheet.timing.append(timing_and_current_data_item('Positive clk hold','2','3','4')) - new_sheet.timing.append(timing_and_current_data_item('CSb setup rising',FF_SETUP_LH_MIN,FF_SETUP_LH_MAX,'ns')) - new_sheet.timing.append(timing_and_current_data_item('CSb setup falling',FF_SETUP_HL_MIN,FF_SETUP_HL_MAX,'ns')) - new_sheet.timing.append(timing_and_current_data_item('CSb hold rising',FF_HOLD_LH_MIN,FF_HOLD_LH_MAX,'ns')) - new_sheet.timing.append(timing_and_current_data_item('CSb hold falling',FF_HOLD_HL_MIN,FF_HOLD_HL_MAX,'ns')) + + while(True): + if(row[col].startswith('DIN')): + start = col + new_sheet.timing.append(timing_and_current_data_item('{0} setup rising'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} setup falling'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} hold rising'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} hold falling'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + col +=1 + + elif(row[col].startswith('DOUT')): + start = col + new_sheet.timing.append(timing_and_current_data_item('{0} cell rise'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} cell fall'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} rise transition'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} fall transition'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + col +=1 + + elif(row[col].startswith('CSb')): + start = col + new_sheet.timing.append(timing_and_current_data_item('{0} setup rising'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} setup falling'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} hold rising'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} hold falling'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + col +=1 + + elif(row[col].startswith('WEb')): + start = col + new_sheet.timing.append(timing_and_current_data_item('{0} setup rising'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} setup falling'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} hold rising'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} hold falling'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + col +=1 + + elif(row[col].startswith('ADDR')): + start = col + new_sheet.timing.append(timing_and_current_data_item('{0} setup rising'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} setup falling'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} hold rising'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + new_sheet.timing.append(timing_and_current_data_item('{0} hold falling'.format(row[start]),row[col+1],row[col+2],'ns')) + col += 2 + + col +=1 + else: + break + + + new_sheet.timing.append(timing_and_current_data_item('AC current','2','3','4')) new_sheet.timing.append(timing_and_current_data_item('Standby current','2','3','4')) diff --git a/compiler/datasheet/wavedrom.py b/compiler/datasheet/wavedrom.py new file mode 100644 index 00000000..e8c68c56 --- /dev/null +++ b/compiler/datasheet/wavedrom.py @@ -0,0 +1,827 @@ +#!/usr/bin/python +# The MIT License (MIT) +# +# Copyright (c) 2011-2016 Aliaksei Chapyzhenka +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# +# Translated to Python from original file: +# https://github.com/drom/wavedrom/blob/master/src/WaveDrom.js +# + +import sys +import json +import math +import waveskin + +font_width = 7 + +lane = { + "xs" : 20, # tmpgraphlane0.width + "ys" : 20, # tmpgraphlane0.height + "xg" : 120, # tmpgraphlane0.x + "yg" : 0, # head gap + "yh0" : 0, # head gap title + "yh1" : 0, # head gap + "yf0" : 0, # foot gap + "yf1" : 0, # foot gap + "y0" : 5, # tmpgraphlane0.y + "yo" : 30, # tmpgraphlane1.y - y0 + "tgo" : -10, # tmptextlane0.x - xg + "ym" : 15, # tmptextlane0.y - y0 + "xlabel" : 6, # tmptextlabel.x - xg + "xmax" : 1, + "scale" : 1, + "head" : {}, + "foot" : {} +} + +def genBrick (texts, extra, times) : + + R = [] + if len( texts ) == 4 : + for j in range( times ): + + R.append(texts[0]) + + for i in range ( extra ): + R.append(texts[1]) + + R.append(texts[2]) + for i in range ( extra ): + R.append(texts[3]) + + return R + + if len( texts ) == 1 : + texts.append(texts[0]) + + R.append(texts[0]) + for i in range (times * (2 * (extra + 1)) - 1) : + R.append(texts[1]) + return R + +def genFirstWaveBrick (text, extra, times) : + + pattern = { + 'p': ['pclk', '111', 'nclk', '000'], + 'n': ['nclk', '000', 'pclk', '111'], + 'P': ['Pclk', '111', 'nclk', '000'], + 'N': ['Nclk', '000', 'pclk', '111'], + 'l': ['000'], + 'L': ['000'], + '0': ['000'], + 'h': ['111'], + 'H': ['111'], + '1': ['111'], + '=': ['vvv-2'], + '2': ['vvv-2'], + '3': ['vvv-3'], + '4': ['vvv-4'], + '5': ['vvv-5'], + 'd': ['ddd'], + 'u': ['uuu'], + 'z': ['zzz'] + } + + return genBrick( pattern.get( text, ['xxx'] ) , extra, times ); + +def genWaveBrick (text, extra, times) : + + x1 = {'p':'pclk', 'n':'nclk', 'P':'Pclk', 'N':'Nclk', 'h':'pclk', 'l':'nclk', 'H':'Pclk', 'L':'Nclk'} + x2 = {'0':'0', '1':'1', 'x':'x', 'd':'d', 'u':'u', 'z':'z', '=':'v', '2':'v', '3':'v', '4':'v', '5':'v' } + x3 = {'0': '', '1': '', 'x': '', 'd': '', 'u': '', 'z': '', '=':'-2', '2':'-2', '3':'-3', '4':'-4', '5':'-5'} + y1 = { + 'p':'0', 'n':'1', + 'P':'0', 'N':'1', + 'h':'1', 'l':'0', + 'H':'1', 'L':'0', + '0':'0', '1':'1', 'x':'x', 'd':'d', 'u':'u', 'z':'z', '=':'v', '2':'v', '3':'v', '4':'v', '5':'v'} + + y2 = { + 'p': '', 'n': '', + 'P': '', 'N': '', + 'h': '', 'l': '', + 'H': '', 'L': '', + '0': '', '1': '', 'x': '', 'd': '', 'u': '', 'z': '', '=':'-2', '2':'-2', '3':'-3', '4':'-4', '5':'-5'} + + x4 = { + 'p': '111', 'n': '000', + 'P': '111', 'N': '000', + 'h': '111', 'l': '000', + 'H': '111', 'L': '000', + '0': '000', '1': '111', 'x': 'xxx', 'd': 'ddd', 'u': 'uuu', 'z': 'zzz', + '=': 'vvv-2', '2': 'vvv-2', '3': 'vvv-3', '4': 'vvv-4', '5': 'vvv-5'} + + x5 = {'p':'nclk', 'n':'pclk', 'P':'nclk', 'N':'pclk'} + x6 = {'p': '000', 'n': '111', 'P': '000', 'N': '111'} + xclude = {'hp':'111', 'Hp':'111', 'ln': '000', 'Ln': '000', 'nh':'111', 'Nh':'111', 'pl': '000', 'Pl':'000'} + + #atext = text.split() + atext = text + + tmp0 = x4.get(atext[1]) + tmp1 = x1.get(atext[1]) + if tmp1 == None : + tmp2 = x2.get(atext[1]) + if tmp2 == None : + # unknown + return genBrick(['xxx'], extra, times) + else : + tmp3 = y1.get(atext[0]) + if tmp3 == None : + # unknown + return genBrick(['xxx'], extra, times) + + # soft curves + return genBrick([tmp3 + 'm' + tmp2 + y2[atext[0]] + x3[atext[1]], tmp0], extra, times) + + else : + tmp4 = xclude.get(text) + if tmp4 != None : + tmp1 = tmp4 + + # sharp curves + tmp2 = x5.get(atext[1]) + if tmp2 == None : + # hlHL + return genBrick([tmp1, tmp0], extra, times) + else : + # pnPN + return genBrick([tmp1, tmp0, tmp2, x6[atext[1]]], extra, times) + +def parseWaveLane (text, extra) : + + R = [] + Stack = text + Next = Stack[0] + Stack = Stack[1:] + + Repeats = 1 + while len(Stack) and ( Stack[0] == '.' or Stack[0] == '|' ): # repeaters parser + Stack=Stack[1:] + Repeats += 1 + + R.extend(genFirstWaveBrick(Next, extra, Repeats)) + + while len(Stack) : + Top = Next + Next = Stack[0] + Stack = Stack[1:] + Repeats = 1 + while len(Stack) and ( Stack[0] == '.' or Stack[0] == '|' ) : # repeaters parser + Stack=Stack[1:] + Repeats += 1 + R.extend(genWaveBrick((Top + Next), extra, Repeats)) + + for i in range( lane['phase'] ): + R = R[1:] + return R + +def parseWaveLanes (sig) : + + def data_extract (e) : + tmp = e.get('data') + if tmp == None : return None + if is_type_str (tmp) : tmp=tmp.split() + return tmp + + content = [] + for sigx in sig : + lane['period'] = sigx.get('period',1) + lane['phase'] = int( sigx.get('phase',0 ) * 2 ) + sub_content=[] + sub_content.append( [sigx.get('name',' '), sigx.get('phase',0 ) ] ) + sub_content.append( parseWaveLane( sigx['wave'], int(lane['period'] * lane['hscale'] - 1 ) ) if sigx.get('wave') else None ) + sub_content.append( data_extract(sigx) ) + content.append(sub_content) + + return content + +def findLaneMarkers (lanetext) : + + lcount = 0 + gcount = 0 + ret = [] + for i in range( len( lanetext ) ) : + if lanetext[i] == 'vvv-2' or lanetext[i] == 'vvv-3' or lanetext[i] == 'vvv-4' or lanetext[i] == 'vvv-5' : + lcount += 1 + else : + if lcount !=0 : + ret.append(gcount - ((lcount + 1) / 2)) + lcount = 0 + + gcount += 1 + + if lcount != 0 : + ret.append(gcount - ((lcount + 1) / 2)) + + return ret + +def renderWaveLane (root, content, index) : + + xmax = 0 + xgmax = 0 + glengths = [] + svgns = 'http://www.w3.org/2000/svg' + xlinkns = 'http://www.w3.org/1999/xlink' + xmlns = 'http://www.w3.org/XML/1998/namespace' + for j in range( len(content) ): + name = content[j][0][0] + if name : # check name + g = [ + 'g', + { + 'id': 'wavelane_' + str(j) + '_' + str(index), + 'transform': 'translate(0,' + str(lane['y0'] + j * lane['yo']) + ')' + } + ] + root.append(g) + title = [ + 'text', + { + 'x': lane['tgo'], + 'y': lane['ym'], + 'class': 'info', + 'text-anchor': 'end', + 'xml:space': 'preserve' + }, + ['tspan', name] + ] + g.append(title) + + glengths.append( len(name) * font_width + font_width ) + + xoffset = content[j][0][1] + xoffset = math.ceil(2 * xoffset) - 2 * xoffset if xoffset > 0 else -2 * xoffset + gg = [ + 'g', + { + 'id': 'wavelane_draw_' + str(j) + '_' + str(index), + 'transform': 'translate(' + str( xoffset * lane['xs'] ) + ', 0)' + } + ] + g.append(gg) + + if content[j][1] : + for i in range( len(content[j][1]) ) : + b = [ + 'use', + { + #'id': 'use_' + str(i) + '_' + str(j) + '_' + str(index), + 'xmlns:xlink':xlinkns, + 'xlink:href': '#' + str( content[j][1][i] ), + 'transform': 'translate(' + str(i * lane['xs']) + ')' + } + ] + gg.append(b) + + if content[j][2] and len(content[j][2]) : + labels = findLaneMarkers(content[j][1]) + if len(labels) != 0 : + for k in range( len(labels) ) : + if content[j][2] and k < len(content[j][2]) : + title = [ + 'text', + { + 'x': int(labels[k]) * lane['xs'] + lane['xlabel'], + 'y': lane['ym'], + 'text-anchor': 'middle', + 'xml:space': 'preserve' + }, + ['tspan',content[j][2][k]] + ] + gg.append(title) + + + if len(content[j][1]) > xmax : + xmax = len(content[j][1]) + + lane['xmax'] = xmax + lane['xg'] = xgmax + 20 + return glengths + +def renderMarks (root, content, index) : + + def captext ( g, cxt, anchor, y ) : + + if cxt.get(anchor) and cxt[anchor].get('text') : + tmark = [ + 'text', + { + 'x': float( cxt['xmax'] ) * float( cxt['xs'] ) / 2, + 'y': y, + 'text-anchor': 'middle', + 'fill': '#000', + 'xml:space': 'preserve' + }, cxt[anchor]['text'] + ] + g.append(tmark) + + def ticktock ( g, cxt, ref1, ref2, x, dx, y, length ) : + L = [] + + if cxt.get(ref1) == None or cxt[ref1].get(ref2) == None : + return + + val = cxt[ref1][ref2] + if is_type_str( val ) : + val = val.split() + elif type( val ) is int : + offset = val + val = [] + for i in range ( length ) : + val.append(i + offset) + + if type( val ) is list : + if len( val ) == 0 : + return + elif len( val ) == 1 : + offset = val[0] + if is_type_str(offset) : + L = val + else : + for i in range ( length ) : + L[i] = i + offset + + elif len( val ) == 2: + offset = int(val[0]) + step = int(val[1]) + tmp = val[1].split('.') + if len( tmp ) == 2 : + dp = len( tmp[1] ) + + if is_type_str(offset) or is_type_str(step) : + L = val + else : + offset = step * offset + for i in range( length ) : + L[i] = "{0:.",dp,"f}".format(step * i + offset) + + else : + L = val + + else : + return + + for i in range( length ) : + tmp = L[i] + tmark = [ + 'text', + { + 'x': i * dx + x, + 'y': y, + 'text-anchor': 'middle', + 'class': 'muted', + 'xml:space': 'preserve' + }, str(tmp) + ] + g.append(tmark) + + mstep = 2 * int(lane['hscale']) + mmstep = mstep * lane['xs'] + marks = int( lane['xmax'] / mstep ) + gy = len( content ) * int(lane['yo']) + + g = ['g', {'id': 'gmarks_' + str(index)}] + root.insert(0,g) + + for i in range( marks + 1): + gg = [ + 'path', + { + 'id': 'gmark_' + str(i) + '_' + str(index), + 'd': 'm ' + str(i * mmstep) + ',' + '0' + ' 0,' + str(gy), + 'style': 'stroke:#888;stroke-width:0.5;stroke-dasharray:1,3' + } + ] + g.append( gg ) + + captext(g, lane, 'head', -33 if lane['yh0'] else -13 ) + captext(g, lane, 'foot', gy + ( 45 if lane['yf0'] else 25 ) ) + + ticktock( g, lane, 'head', 'tick', 0, mmstep, -5, marks + 1) + ticktock( g, lane, 'head', 'tock', mmstep / 2, mmstep, -5, marks) + ticktock( g, lane, 'foot', 'tick', 0, mmstep, gy + 15, marks + 1) + ticktock( g, lane, 'foot', 'tock', mmstep / 2, mmstep, gy + 15, marks) + +def renderArcs (root, source, index, top) : + + Stack = [] + Edge = {'words': [], 'frm': 0, 'shape': '', 'to': 0, 'label': ''} + Events = {} + svgns = 'http://www.w3.org/2000/svg' + xmlns = 'http://www.w3.org/XML/1998/namespace' + + if source : + for i in range (len (source) ) : + lane['period'] = source[i].get('period',1) + lane['phase'] = int( source[i].get('phase',0 ) * 2 ) + text = source[i].get('node') + if text: + Stack = text + pos = 0 + while len( Stack ) : + eventname = Stack[0] + Stack=Stack[1:] + if eventname != '.' : + Events[eventname] = { + 'x' : str( int( float( lane['xs'] ) * (2 * pos * lane['period'] * lane['hscale'] - lane['phase'] ) + float( lane['xlabel'] ) ) ), + 'y' : str( int( i * lane['yo'] + lane['y0'] + float( lane['ys'] ) * 0.5 ) ) + } + pos += 1 + + gg = [ 'g', { 'id' : 'wavearcs_' + str( index ) } ] + root.append(gg) + + if top.get('edge') : + for i in range( len ( top['edge'] ) ) : + Edge['words'] = top['edge'][i].split() + Edge['label'] = top['edge'][i][len(Edge['words'][0]):] + Edge['label'] = Edge['label'][1:] + Edge['frm'] = Edge['words'][0][0] + Edge['to'] = Edge['words'][0][-1] + Edge['shape'] = Edge['words'][0][1:-1] + frm = Events[Edge['frm']] + to = Events[Edge['to']] + gmark = [ + 'path', + { + 'id': 'gmark_' + Edge['frm'] + '_' + Edge['to'], + 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + to['x'] + ',' + to['y'], + 'style': 'fill:none;stroke:#00F;stroke-width:1' + } + ] + gg.append(gmark) + dx = float( to['x'] ) - float( frm['x'] ) + dy = float( to['y'] ) - float( frm['y'] ) + lx = (float(frm['x']) + float(to['x'])) / 2 + ly = (float(frm['y']) + float(to['y'])) / 2 + pattern = { + '~' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) }, + '-~' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) }, + '~-' : {'d': 'M ' + frm['x'] + ',' + frm['y'] + ' c ' + '0' + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy) }, + '-|' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)}, + '|-' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' 0,' + str(dy) + ' ' + str(dx) + ',0'}, + '-|-' : {'d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'}, + '->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none'}, + '~>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)}, + '-~>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)}, + '~->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + '0' + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)}, + '-|>' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)}, + '|->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' 0,' + str(dy) + ' ' + str(dx) + ',0'}, + '-|->' : {'style': 'marker-end:url(#arrowhead);stroke:#0041c4;stroke-width:1;fill:none', 'd': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'}, + '<->' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none'}, + '<~>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(0.3 * dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)}, + '<-~>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'M ' + frm['x'] + ',' + frm['y'] + ' ' + 'c ' + str(0.7 * dx) + ', 0 ' + str(dx) + ', ' + str(dy) + ' ' + str(dx) + ', ' + str(dy)}, + '<-|>' : {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx) + ',0 0,' + str(dy)}, + '<-|->': {'style': 'marker-end:url(#arrowhead);marker-start:url(#arrowtail);stroke:#0041c4;stroke-width:1;fill:none','d': 'm ' + frm['x'] + ',' + frm['y'] + ' ' + str(dx / 2) + ',0 0,' + str(dy) + ' ' + str(dx / 2) + ',0'} + } + gmark[1].update( pattern.get( Edge['shape'], { 'style': 'fill:none;stroke:#00F;stroke-width:1' } ) ) + + if Edge['label']: + if Edge['shape'] == '-~' : + lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75 + if Edge['shape'] == '~-' : + lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.25 + if Edge['shape'] == '-|' : + lx = float(to['x']) + if Edge['shape'] == '|-' : + lx = float(frm['x']) + if Edge['shape'] == '-~>': + lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75 + if Edge['shape'] == '~->': + lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.25 + if Edge['shape'] == '-|>' : + lx = float(to['x']) + if Edge['shape'] == '|->' : + lx = float(frm['x']) + if Edge['shape'] == '<-~>': + lx = float(frm['x']) + (float(to['x']) - float(frm['x'])) * 0.75 + if Edge['shape'] =='<-|>' : + lx = float(to['x']) + + lwidth = len( Edge['label'] ) * font_width + label = [ + 'text', + { + 'style': 'font-size:10px;', + 'text-anchor': 'middle', + 'xml:space': 'preserve', + 'x': int( lx ), + 'y': int( ly + 3 ) + }, + [ 'tspan', Edge['label'] ] + ] + underlabel = [ + 'rect', + { + 'height': 9, + 'style': 'fill:#FFF;', + 'width': lwidth, + 'x': int( lx - lwidth / 2 ), + 'y': int( ly - 5 ) + } + ] + gg.append(underlabel) + gg.append(label) + + for k in Events: + if k.islower() : + if int( Events[k]['x'] ) > 0 : + lwidth = len( k ) * font_width + underlabel = [ + 'rect', + { + 'x': float( Events[k]['x'] ) - float(lwidth) / 2, + 'y': int( Events[k]['y'] ) - 4, + 'height': 8, + 'width': lwidth, + 'style': 'fill:#FFF;' + } + ] + gg.append(underlabel) + label = [ + 'text', + { + 'style': 'font-size:8px;', + 'x': int( Events[k]['x'] ), + 'y': int( Events[k]['y'] ) + 2, + 'width': lwidth, + 'text-anchor': 'middle' + }, + k + ] + gg.append(label) + +def parseConfig (source) : + + lane['hscale'] = 1 + if lane.get('hscale0') : + lane['hscale'] = lane['hscale0'] + + if source and source.get('config') and source.get('config').get('hscale'): + hscale = round(source.get('config').get('hscale')) + if hscale > 0 : + if hscale > 100 : hscale = 100 + lane['hscale'] = hscale + + lane['yh0'] = 0 + lane['yh1'] = 0 + if source and source.get('head') : + lane['head'] = source['head'] + if source.get('head').get('tick',0) == 0 : lane['yh0'] = 20 + if source.get('head').get('tock',0) == 0 : lane['yh0'] = 20 + if source.get('head').get('text') : lane['yh1'] = 46; lane['head']['text'] = source['head']['text'] + + lane['yf0'] = 0 + lane['yf1'] = 0 + if source and source.get('foot') : + lane['foot'] = source['foot'] + if source.get('foot').get('tick',0) == 0 : lane['yf0'] = 20 + if source.get('foot').get('tock',0) == 0 : lane['yf0'] = 20 + if source.get('foot').get('text') : lane['yf1'] = 46; lane['foot']['text'] = source['foot']['text'] + +def rec (tmp, state) : + + name = str( tmp[0] ) + delta_x = 25 + + state['x'] += delta_x + for i in range( len( tmp ) ) : + if type( tmp[i] ) is list : + old_y = state['y'] + rec( tmp[i], state ) + state['groups'].append( {'x':state['xx'], 'y':old_y, 'height':state['y'] - old_y, 'name': state['name'] } ) + elif type( tmp[i] ) is dict : + state['lanes'].append(tmp[i]) + state['width'].append(state['x']) + state['y'] += 1 + + state['xx'] = state['x'] + state['x'] -= delta_x + state['name'] = name + +def insertSVGTemplate (index, parent, source) : + + e = waveskin.WaveSkin['default'] + + if source.get('config') and source.get('config').get('skin') : + if waveskin.WaveSkin.get( source.get('config').get('skin') ) : + e = waveskin.WaveSkin[ source.get('config').get('skin') ] + + if index == 0 : + lane['xs'] = int( e[3][1][2][1]['width'] ) + lane['ys'] = int( e[3][1][2][1]['height'] ) + lane['xlabel'] = int( e[3][1][2][1]['x'] ) + lane['ym'] = int( e[3][1][2][1]['y'] ) + + else : + e = ['svg', {'id': 'svg', 'xmlns': 'http://www.w3.org/2000/svg', 'xmlns:xlink': 'http://www.w3.org/1999/xlink', 'height': '0'}, + ['g', {'id': 'waves'}, + ['g', {'id': 'lanes'}], + ['g', {'id': 'groups'}] + ] + ] + + e[-1][1]['id'] = 'waves_' + str(index) + e[-1][2][1]['id'] = 'lanes_' + str(index) + e[-1][3][1]['id'] = 'groups_' + str(index) + e[1]['id'] = 'svgcontent_' + str(index) + e[1]['height'] = 0 + + parent.extend(e) + +def renderWaveForm (index, source, output) : + + xmax = 0 + root = [] + groups = [] + + if source.get('signal'): + insertSVGTemplate(index, output, source) + parseConfig( source ) + ret = {'x':0, 'y':0, 'xmax':0, 'width':[], 'lanes':[], 'groups':[] } + rec( source['signal'], ret ) + content = parseWaveLanes(ret['lanes']) + glengths = renderWaveLane(root, content, index) + for i in range( len( glengths ) ): + xmax = max( xmax, ( glengths[i] + ret['width'][i] ) ) + renderMarks(root, content, index) + renderArcs(root, ret['lanes'], index, source) + renderGaps(root, ret['lanes'], index) + renderGroups(groups, ret['groups'], index) + lane['xg'] = int( math.ceil( float( xmax - lane['tgo'] ) / float(lane['xs'] ) ) ) * lane['xs'] + width = (lane['xg'] + lane['xs'] * (lane['xmax'] + 1) ) + height = len(content) * lane['yo'] + lane['yh0'] + lane['yh1'] + lane['yf0'] + lane['yf1'] + output[1]={ + 'id' :'svgcontent_' + str(index), + 'xmlns' :"http://www.w3.org/2000/svg", + 'xmlns:xlink':"http://www.w3.org/1999/xlink", + 'width' :str(width), + 'height' :str(height), + 'viewBox' :'0 0 ' + str(width) + ' ' + str(height), + 'overflow' :"hidden" + } + output[-1][2][1]['transform']='translate(' + str(lane['xg'] + 0.5) + ', ' + str((float(lane['yh0']) + float(lane['yh1'])) + 0.5) + ')' + + output[-1][2].extend(root) + output[-1][3].extend(groups) + +def renderGroups (root, groups, index) : + + svgns = 'http://www.w3.org/2000/svg', + xmlns = 'http://www.w3.org/XML/1998/namespace' + + for i in range( len( groups ) ) : + group = [ + 'path', + { + 'id': 'group_' + str(i) + '_' + str(index), + 'd': 'm ' + str( groups[i]['x'] + 0.5 ) + ',' + str( groups[i]['y']* lane['yo'] + 3.5 + lane['yh0'] + lane['yh1'] ) + ' c -3,0 -5,2 -5,5 l 0,' + str( int( groups[i]['height'] * lane['yo'] - 16 ) ) + ' c 0,3 2,5 5,5', + 'style': 'stroke:#0041c4;stroke-width:1;fill:none' + } + ] + root.append(group) + + name = groups[i]['name'] + x = str( int( groups[i]['x'] - 10 ) ) + y = str( int( lane['yo'] * (groups[i]['y'] + (float(groups[i]['height']) / 2)) + lane['yh0'] + lane['yh1'] ) ) + label = [ + ['g', + {'transform': 'translate(' + x + ',' + y + ')'}, + ['g', {'transform': 'rotate(270)'}, + 'text', + { + 'text-anchor': 'middle', + 'class': 'info', + 'xml:space' : 'preserve' + }, + ['tspan',name] + ] + ] + ] + root.append(label) + +def renderGaps (root, source, index) : + + Stack = [] + svgns = 'http://www.w3.org/2000/svg', + xlinkns = 'http://www.w3.org/1999/xlink' + + if source: + + gg = [ + 'g', + { 'id': 'wavegaps_' + str(index) } + ] + + for i in range( len( source )): + lane['period'] = source[i].get('period',1) + lane['phase'] = int( source[i].get('phase',0 ) * 2 ) + + g = [ + 'g', + { + 'id': 'wavegap_' + str(i) + '_' + str(index), + 'transform': 'translate(0,' + str(lane['y0'] + i * lane['yo']) + ')' + } + ] + gg.append(g) + + if source[i].get('wave'): + text = source[i]['wave'] + Stack = text + pos = 0 + while len( Stack ) : + c = Stack [0] + Stack = Stack[1:] + if c == '|' : + b = [ + 'use', + { + 'xmlns:xlink':xlinkns, + 'xlink:href':'#gap', + 'transform': 'translate(' + str(int(float(lane['xs']) * ((2 * pos + 1) * float(lane['period']) * float(lane['hscale']) - float(lane['phase'])))) + ')' + } + ] + g.append(b) + pos += 1 + + root.append( gg ) + +def is_type_str( var ) : + if sys.version_info[0] < 3: + return type( var ) is str or type( var ) is unicode + else: + return type( var ) is str + +def convert_to_svg( root ) : + + svg_output = '' + + if type( root ) is list: + if len(root) >= 2 and type( root[1] ) is dict: + if len( root ) == 2 : + svg_output += '<' + root[0] + convert_to_svg( root[1] ) + '/>\n' + elif len( root ) >= 3 : + svg_output += '<' + root[0] + convert_to_svg( root[1] ) + '>\n' + if len( root ) == 3: + svg_output += convert_to_svg( root[2] ) + else: + svg_output += convert_to_svg( root[2:] ) + svg_output += '\n' + elif type( root[0] ) is list: + for eleml in root: + svg_output += convert_to_svg( eleml ) + else: + svg_output += '<' + root[0] + '>\n' + for eleml in root[1:]: + svg_output += convert_to_svg( eleml ) + svg_output += '\n' + elif type( root ) is dict: + for elemd in root : + svg_output += ' ' + elemd + '="' + str(root[elemd]) + '"' + else: + svg_output += root + + return svg_output + +if __name__ == '__main__': + + if len( sys.argv ) != 5: + print ( 'Usage : ' + sys.argv[0] + ' source svg ' ) + exit(1) + + if sys.argv[3] != 'svg' : + print ( 'Error: only SVG format supported.' ) + exit(1) + + output=[] + inputfile = sys.argv[2] + outputfile = sys.argv[4] + + with open(inputfile,'r') as f: + jinput = json.load(f) + + renderWaveForm(0,jinput,output) + svg_output = convert_to_svg(output) + + with open(outputfile,'w') as f: + f.write( svg_output )