├── setup.py ├── vcdTest.py ├── README.md ├── demo1.py ├── LICENSE ├── demo.py └── wavedraw ├── WaveGroup.py ├── WaveDraw.py ├── WaveBool.py ├── WaveBits.py └── vcd └── VCDParser.py /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | 3 | setup( 4 | name="wavedraw", 5 | version="1.0.0", 6 | description="python tool used to generate wavedrom json", 7 | author="xiaochuang.lxc", 8 | packages=['wavedraw',"wavedraw.vcd"] 9 | ) -------------------------------------------------------------------------------- /vcdTest.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | #import sys 4 | #sys.path.append("./") 5 | from wavedraw.vcd.VCDParser import * 6 | 7 | vcdParser=VCDParser(fileName="./wave.vcd",halfClockPeriod=5) 8 | print(vcdParser.generateWave("wave",1000,1400,period=0.5)) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wavedraw 2 | draw interface wave by python,generate wavedrom json format 3 | 4 | # how to install 5 | ```bash 6 | sudo python3 setup.py sdist bdist_wheel 7 | sudo pip3 install dist/wavedraw-1.0.0.tar.gz 8 | ``` 9 | # DOC 10 | [告别繁琐JSON,wavedraw用Python重新定义时序图绘制](https://mp.weixin.qq.com/s/UKP6je7WAJ9fbn43Bo6G1Q) 11 | 12 | [VCD文件秒变专业时序图:wavedraw的V让仿真波形更美观](https://mp.weixin.qq.com/s/4144k4yHIODUy3oAGezDkg) -------------------------------------------------------------------------------- /demo1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | #import sys 4 | #sys.path.append("./") 5 | from wavedraw.WaveDraw import * 6 | from wavedraw.WaveGroup import * 7 | from wavedraw.WaveBool import * 8 | from wavedraw.WaveBits import * 9 | 10 | wave=WaveDraw(title="demo1") 11 | 12 | A=wave.generateBool(name="A") 13 | B=wave.generateBool(name="B") 14 | C=wave.generateBool(name="C") 15 | D=wave.generateBool(name="D") 16 | E=wave.generateBool(name="E") 17 | 18 | wave.incrClk(1) 19 | A.wave(wave="1",node="a",endNode="j",holdCycle=9) 20 | B.wave(wave="1",node="b",endNode="i",offset=1,holdCycle=8) 21 | C.wave(wave="1",node="c",endNode="h",offset=2,holdCycle=5) 22 | D.wave(wave="1",node="d",endNode="g",offset=3,holdCycle=3) 23 | E.wave(wave="1",node="d",endNode="g",offset=4,holdCycle=1) 24 | 25 | wave.incrClk(11) 26 | B.wave(wave="1111") 27 | C.wave(wave="1111") 28 | D.wave(wave="111",offset=1) 29 | E.wave(wave="1",offset=2) 30 | 31 | wave.addEdge('a~b t1', 'c-~a t2', 'c-~>d time 3', 'd~-e', 32 | 'e~>f', 'f->g', 'g-~>h', 'h~>i some text', 'h~->j') 33 | 34 | print(wave.generateJson()) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 yuqi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | #import sys 4 | #sys.path.append("./") 5 | from wavedraw.WaveDraw import * 6 | from wavedraw.WaveGroup import * 7 | from wavedraw.WaveBool import * 8 | from wavedraw.WaveBits import * 9 | 10 | wave=WaveDraw(title="demo") 11 | clk=wave.generateBool(name="clk",initWave="P") 12 | master=wave.generateGroup(name="Master") 13 | ctrl=master.generateGroup(name="ctrl") 14 | write=ctrl.generateBool(name="write",initWave="0") 15 | read=ctrl.generateBool(name="read",initWave="0") 16 | addr=master.generateBits(name="addr",initWave="x") 17 | wdata=master.generateBits(name="wdata",initWave="x") 18 | 19 | slave=wave.generateGroup(name="Slave") 20 | ctrl=slave.generateGroup(name="ctrl") 21 | ack=ctrl.generateBool(name="ack",initWave="0") 22 | rdata=slave.generateBits(name="rdata",initWave="x") 23 | 24 | # write operation 25 | write.wave(wave="110",node="a..") 26 | addr.wave(wave="33x",data=["A1","A1",None]) 27 | wdata.wave(wave="33x",data=["D1","D1",None]) 28 | ack.wave(wave="010",node="..b") 29 | wave.addEdge("a->b write") 30 | 31 | wave.incrClk(3) 32 | wave.addSplit() 33 | # read operation 34 | read.wave("1110",node="c...") 35 | addr.wave(wave="4",data=["A2"],node=".",endWave="x",holdCycle=3) 36 | ack.wave(wave="0010",node="...d") 37 | rdata.wave("5x",data=["Q2",None],offset=2) 38 | wave.addEdge("c->d read") 39 | 40 | print(wave.generateJson()) -------------------------------------------------------------------------------- /wavedraw/WaveGroup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | from wavedraw.WaveDraw import WaveDraw 4 | from wavedraw.WaveBits import WaveBits 5 | from wavedraw.WaveBool import WaveBool 6 | class WaveGroup(object): 7 | def __init__(self,name:str,wavedraw:WaveDraw,period:float=1,phase:int=0): 8 | self.name=name 9 | self.wavedraw=wavedraw 10 | self.period=period 11 | self.phase=phase 12 | self.signalList=[] 13 | 14 | def generateBool(self,name:str,initWave:str="0",initNode:str=".",period:float=None,phase:int=None): 15 | periodAdapt=period 16 | phaseAdapt=phase 17 | if periodAdapt==None: 18 | periodAdapt=self.period 19 | if phaseAdapt==None: 20 | phaseAdapt=self.phase 21 | waveBool= WaveBool(name=name,wavedraw=self.wavedraw,initWave=initWave,initNode=initNode,period=periodAdapt,phase=phaseAdapt) 22 | self.signalList.append(waveBool) 23 | return waveBool 24 | 25 | def generateBits(self,name:str,initWave:str="0",initNode:str=".",initData:str=None,period:float=None,phase:int=None): 26 | periodAdapt=period 27 | phaseAdapt=phase 28 | if periodAdapt==None: 29 | periodAdapt=self.period 30 | if phaseAdapt==None: 31 | phaseAdapt=self.phase 32 | waveBits= WaveBits(name=name,wavedraw=self.wavedraw,initWave=initWave,initNode=initNode,initData=initData,period=periodAdapt,phase=phaseAdapt) 33 | self.signalList.append(waveBits) 34 | return waveBits 35 | 36 | def generateGroup(self,name:str): 37 | waveGroup=WaveGroup(name=name,wavedraw=self.wavedraw,period=self.period,phase=self.phase) 38 | self.signalList.append(waveGroup) 39 | return waveGroup 40 | 41 | def addSplit(self): 42 | for signal in self.signalList: 43 | signal.addSplit() 44 | 45 | def getClockNum(self)->int: 46 | maxClock=0 47 | for signal in self.signalList: 48 | if signal.getClockNum()>maxClock: 49 | maxClock=signal.getClockNum() 50 | return maxClock 51 | 52 | def generateJson(self)->str: 53 | json=f"\"{self.name}\",\n" 54 | for signal in self.signalList: 55 | if isinstance(signal,WaveGroup): 56 | json+=f"[{signal.generateJson()}],\n" 57 | else: 58 | json+=f"{{{signal.generateJson()}}},\n" 59 | return json 60 | 61 | -------------------------------------------------------------------------------- /wavedraw/WaveDraw.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | class WaveDraw(object): 4 | def __init__(self,title=""): 5 | self.title=title 6 | self.currentClk=1 7 | self.signalList=[] 8 | self.hscale=1 9 | self.edgeList=[] 10 | 11 | def generateBool(self,name:str,initWave:str="0",initNode:str=".",period:float=1,phase:int=0): 12 | from wavedraw.WaveBool import WaveBool 13 | waveBool= WaveBool(name=name,wavedraw=self,initWave=initWave,initNode=initNode,period=period,phase=phase) 14 | self.signalList.append(waveBool) 15 | return waveBool 16 | 17 | def generateBits(self,name:str,initWave:str="0",initNode:str=".",initData:str=None,period:float=1,phase:int=0): 18 | from wavedraw.WaveBits import WaveBits 19 | waveBits= WaveBits(name=name,wavedraw=self,initWave=initWave,initNode=initNode,initData=initData,period=period,phase=phase) 20 | self.signalList.append(waveBits) 21 | return waveBits 22 | 23 | def generateGroup(self,name:str,period:float=1,phase:int=0): 24 | from wavedraw.WaveGroup import WaveGroup 25 | waveGroup=WaveGroup(name=name,wavedraw=self,period=period,phase=phase) 26 | self.signalList.append(waveGroup) 27 | return waveGroup 28 | 29 | def incrClk(self,delat:int=1): 30 | self.currentClk=self.currentClk+delat 31 | 32 | def addSplit(self): 33 | for signal in self.signalList: 34 | signal.addSplit() 35 | self.incrClk() 36 | 37 | def addEdge(self,*edges): 38 | for edge in edges: 39 | self.edgeList.append(edge) 40 | 41 | def getClockNum(self)->int: 42 | maxClock=0 43 | for signal in self.signalList: 44 | if signal.getClockNum()>maxClock: 45 | maxClock=signal.getClockNum() 46 | return maxClock 47 | 48 | def generateJson(self)->str: 49 | from wavedraw.WaveGroup import WaveGroup 50 | self.currentClk=self.getClockNum()+2 51 | wave="" 52 | for signal in self.signalList: 53 | if isinstance(signal,WaveGroup): 54 | wave+=f"[{signal.generateJson()}],\n" 55 | else: 56 | wave+=f"{{{signal.generateJson()}}},\n" 57 | edges="" 58 | for edge in self.edgeList: 59 | edges+=f"\'{edge}\'," 60 | if len(self.edgeList)>0: 61 | return f"{{\"signal\":[\n{wave}],\n\"head\":{{\"text\":\"{self.title}\",\"tick\":0}},\n\"edge\":[{edges}],\n\"config\":{{\"hscale\":{self.hscale}}}}}" 62 | else: 63 | return f"{{\"signal\":[\n{wave}],\n\"head\":{{\"text\":\"{self.title}\",\"tick\":0}},\n\"config\":{{\"hscale\":{self.hscale}}}}}" 64 | -------------------------------------------------------------------------------- /wavedraw/WaveBool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | import sys 4 | from wavedraw.WaveDraw import WaveDraw 5 | class WaveBool(object): 6 | def __init__(self,name:str,wavedraw:WaveDraw,initWave:str="0",initNode:str=".",period:float=1,phase:int=0): 7 | self.name=name 8 | self.wavedraw=wavedraw 9 | self.waveList=[initWave] 10 | self.nodeList=[initNode] 11 | self.clockTriggreDict={} 12 | self.period=period 13 | self.phase=phase 14 | 15 | def addTriggerAtClock(self,clock:int,wave:str,node:str="."): 16 | if wave not in ["0","1","z","u","d","p","P","n","N","|"]: 17 | print(f"WaveBool:{self.name} wave:{wave} at clock:{clock} is not a value value[0,1,z,u,d,p,P,n,N,|]") 18 | sys.exit(-1) 19 | if clock in self.clockTriggreDict.keys(): 20 | print(print(f"WaveBool:{self.name} at clock:{clock} has already registered wave:{self.clockTriggreDict[clock]}")) 21 | sys.exit(-1) 22 | self.clockTriggreDict[clock]=(wave,node) 23 | 24 | def addTrigger(self,wave:str,node:str=".",offset:int=0): 25 | self.addTriggerAtClock(clock=self.wavedraw.currentClk+offset,wave=wave,node=node) 26 | 27 | def wave(self,wave:str,node:str=None,offset:int=0,endWave:str="0",endNode:str=".",holdCycle:int=None): 28 | """ add wave to signal 29 | 30 | Args: 31 | wave (str): signal wave string 32 | node (str, optional): signal node define. Defaults to None. 33 | offset (int, optional): wave start offset based on current clock time. Defaults to 0. 34 | endWave (str, optional): end wave string,only used when holdCycle!=None. Defaults to "x". 35 | endNode (str, optional): end node string,only used when holdCycle!=None. Defaults to ".". 36 | holdCycle (int, optional): repeat wave holdCycle times and then end with endWave/enddata/endnode when holdCycle!=None. Defaults to None. 37 | """ 38 | if holdCycle==None: 39 | nodeAdapt=node 40 | if node==None: 41 | nodeAdapt="."*len(wave) 42 | else: 43 | if len(node)!= len(wave): 44 | print(f"WaveBool:{self.name} wave:{wave} len mismatch node:{node} len at clock:{self.wavedraw.currentClk+offset} ") 45 | sys.exit(-1) 46 | for index in range(len(wave)): 47 | self.addTrigger(wave=wave[index],node=nodeAdapt[index],offset=offset+index) 48 | else: 49 | if len(wave)!=1 or len(node)!=1: 50 | print(f"WaveBool:{self.name} wave:{wave} and node:{node} should be one symbol when holdCycle != None") 51 | sys.exit(-1) 52 | for index in range(holdCycle): 53 | if index==0: 54 | self.wave(wave=wave,node=node,offset=offset+index,holdCycle=None) 55 | else: 56 | self.wave(wave=wave,node=".",offset=offset+index,holdCycle=None) 57 | self.wave(wave=endWave,node=endNode,offset=offset+holdCycle,holdCycle=None) 58 | 59 | def addSplit(self): 60 | self.addTrigger(wave="|",node=".",offset=0) 61 | 62 | def getClockNum(self)->int: 63 | if len(self.clockTriggreDict.keys())!=0: 64 | return max(self.clockTriggreDict.keys()) 65 | else: 66 | return 0 67 | 68 | def generateJson(self)->str: 69 | lastValidWave=self.waveList[0] 70 | for clk in range(1,self.wavedraw.currentClk): 71 | if clk in self.clockTriggreDict.keys(): 72 | (wave,node)=self.clockTriggreDict[clk] 73 | if (wave== lastValidWave): 74 | self.waveList.append(".") 75 | else: 76 | self.waveList.append(wave) 77 | if not wave=="|": 78 | lastValidWave=wave 79 | if(node==None): 80 | self.nodeList.append(".") 81 | else: 82 | self.nodeList.append(node) 83 | else: 84 | self.waveList.append(".") 85 | self.nodeList.append(".") 86 | wave="".join(self.waveList) 87 | node="".join(self.nodeList) 88 | return f"\"name\":\"{self.name}\",\"wave\":\'{wave}\',\"node\":\'{node}\',\"period\":{self.period},\"phase\":{self.phase}" -------------------------------------------------------------------------------- /wavedraw/WaveBits.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | from wavedraw.WaveDraw import WaveDraw 4 | import sys 5 | class WaveBits(object): 6 | def __init__(self,name:str,wavedraw:WaveDraw,initWave:str="x",initData:str=None,initNode:str=".",period:float=1,phase:int=0): 7 | self.name=name 8 | self.waveList=[initWave] 9 | self.wavedraw=wavedraw 10 | self.dataList=[] 11 | if not (initWave== "x" or initWave=="."): 12 | if (initWave!="|"): 13 | if initData==None: 14 | print(f"WaveBits:{self.name} data:{wave} should not be None while wave={wave}:{node} len at clock:{clk}") 15 | sys.exit(-1) 16 | self.dataList.append(initData) 17 | self.nodeList=[] 18 | self.clockTriggreDict={} 19 | self.period=period 20 | self.phase=phase 21 | 22 | def addTriggerAtClock(self,clock:int,wave:str,data:str,node:str="."): 23 | if wave not in ["2","3","4","5","6","7","8","9","x","|","."]: 24 | print(f"WaveBits:{self.name} wave:{wave} at clock:{clock} is not a value value[2,3,4,5,6,7,8,9,x,.]") 25 | sys.exit(-1) 26 | if clock in self.clockTriggreDict.keys(): 27 | print(print(f"WaveBits:{self.name} at clock:{clock} has already registered wave:{self.clockTriggreDict[clock]}")) 28 | sys.exit(-1) 29 | dataAdapt=data 30 | if wave=="x" or wave==".": 31 | dataAdapt=None 32 | #elif wave!="|": 33 | # if data==None: 34 | # print("data should not be None while wave not in [x,|]") 35 | # sys.exit(-1) 36 | self.clockTriggreDict[clock]=(wave,node) 37 | self.clockTriggreDict[clock]=(wave,node,dataAdapt) 38 | 39 | def addTrigger(self,wave:str,node:str=".",data:str=None,offset:int=0): 40 | self.addTriggerAtClock(clock=self.wavedraw.currentClk+offset,wave=wave,data=data,node=node) 41 | 42 | def wave(self,wave:str,data:list[str],node:str=None,offset:int=0,endWave:str="x",endData:str=None,endNode:str=".",holdCycle:int=None): 43 | """ add wave to signal 44 | 45 | Args: 46 | wave (str): signal wave string 47 | data (list[str]): wave data list 48 | node (str, optional): signal node define. Defaults to None. 49 | offset (int, optional): wave start offset based on current clock time. Defaults to 0. 50 | endWave (str, optional): end wave string,only used when holdCycle!=None. Defaults to "x". 51 | endData (str, optional): end data string,only used when holdCycle!=None. Defaults to None. 52 | endNode (str, optional): end node string,only used when holdCycle!=None. Defaults to ".". 53 | holdCycle (int, optional): repeat wave holdCycle times and then end with endWave/enddata/endnode when holdCycle!=None. Defaults to None. 54 | """ 55 | if(holdCycle==None): 56 | #node process 57 | nodeAdapt=node 58 | if node==None: 59 | nodeAdapt="."*len(wave) 60 | else: 61 | if len(wave)!=len(node): 62 | print(f"WaveBits:{self.name} wave:{wave} len mismatch node:{node} len at clock:{self.wavedraw.currentClk+offset}") 63 | sys.exit(-1) 64 | # data process 65 | dataAdapt=data 66 | if len(wave)!=len(data): 67 | print(f"WaveBits:{self.name} wave:{wave} len mismatch data:{data} len at clock:{self.wavedraw.currentClk+offset}") 68 | sys.exit(-1) 69 | for index in range(len(wave)): 70 | self.addTrigger(wave=wave[index],node=nodeAdapt[index],data=dataAdapt[index],offset=offset+index) 71 | else: 72 | if len(wave)!=1 or len(data)!=1 or len(node)!=1: 73 | print(f"WaveBits:{self.name} wave:{wave} and node:{node} and data:{data} should be only one symbol when holdCycle != None") 74 | sys.exit(-1) 75 | for index in range(holdCycle): 76 | if index==0: 77 | self.wave(wave=wave,data=data,node=node,offset=offset+index,holdCycle=None) 78 | else: 79 | self.wave(wave=wave,data=data,node=".",offset=offset+index,holdCycle=None) 80 | self.wave(wave=endWave,data=[endData],node=endNode,offset=offset+holdCycle,holdCycle=None) 81 | 82 | 83 | def addSplit(self): 84 | self.addTrigger(wave="|",data=None,node=".",offset=0) 85 | 86 | def getClockNum(self)->int: 87 | if len(self.clockTriggreDict.keys())!=0: 88 | return max(self.clockTriggreDict.keys()) 89 | else: 90 | return 0 91 | 92 | def generateJson(self)->str: 93 | lastValidWave=self.waveList[0] 94 | for clk in range(1,self.wavedraw.currentClk): 95 | if clk in self.clockTriggreDict.keys(): 96 | (wave,node,data)=self.clockTriggreDict[clk] 97 | if(wave==lastValidWave): 98 | self.waveList.append(".") 99 | else: 100 | self.waveList.append(wave) 101 | if not (wave== "x" or wave=="."): 102 | if (wave!="|"): 103 | if data==None: 104 | print(f"WaveBits:{self.name} data:{wave} should not be None while wave={wave}:{node} len at clock:{clk}") 105 | sys.exit(-1) 106 | self.dataList.append(data) 107 | lastValidWave=wave 108 | if(node==None): 109 | self.nodeList.append(".") 110 | else: 111 | self.nodeList.append(node) 112 | else: 113 | self.waveList.append(".") 114 | self.nodeList.append(".") 115 | wave="".join(self.waveList) 116 | node="".join(self.nodeList) 117 | dataStr="" 118 | for index in range(len(self.dataList)): 119 | dataStr+=f"\"{self.dataList[index]}\"" 120 | if not index== (len(self.dataList)-1): 121 | dataStr+="," 122 | data=",".join(self.dataList) 123 | return f"\"name\":\"{self.name}\",\"wave\":\'{wave}\',\"data\":[{dataStr}],\"node\":\'{node}\',\"period\":{self.period},\"phase\":{self.phase}" 124 | 125 | -------------------------------------------------------------------------------- /wavedraw/vcd/VCDParser.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | from collections import deque 4 | import sys 5 | from wavedraw.WaveDraw import WaveDraw 6 | from wavedraw.WaveGroup import WaveGroup 7 | from wavedraw.WaveBits import WaveBits 8 | from wavedraw.WaveBool import WaveBool 9 | class Signal(object): 10 | def __init__(self,name:str,symbol:str,isBool:bool): 11 | self.name=name 12 | self.symbol=symbol 13 | self.isBool=isBool 14 | self.valueDict={} 15 | 16 | def addEvent(self,clock:int,value:str): 17 | self.valueDict[clock]=value 18 | #print(f"{self.name} register value:{value} at clock {clock}") 19 | 20 | def generateWave(self,wavedraw:WaveDraw,parentGroup:WaveGroup=None,startTime:int=None,endTime:int=None,period:float=1): 21 | if startTime!=None and endTime!=None: 22 | valueDict=self.valueDict 23 | self.valueDict={} 24 | nearestTime=startTime 25 | if not (startTime in valueDict.keys()): 26 | nearestTime=None 27 | times=sorted(valueDict.keys()) 28 | for time in times: 29 | if time=nearestTime) and (key<=endTime): 33 | if nearestTime!=startTime: 34 | if(key==nearestTime): 35 | self.valueDict[key-nearestTime]=valueDict[key] 36 | else: 37 | self.valueDict[key-startTime]=valueDict[key] 38 | else: 39 | self.valueDict[key-nearestTime]=valueDict[key] 40 | signalWave=None 41 | if self.isBool: 42 | if parentGroup==None: 43 | if 0 in self.valueDict.keys(): 44 | signalWave=wavedraw.generateBool(name=self.name,initWave=self.valueDict[0],period=period) 45 | else: 46 | signalWave=wavedraw.generateBool(name=self.name,period=period) 47 | else: 48 | if 0 in self.valueDict.keys(): 49 | signalWave=parentGroup.generateBool(name=self.name,initWave=self.valueDict[0],period=period) 50 | else: 51 | signalWave=parentGroup.generateBool(name=self.name,period=period) 52 | for (clock,value) in self.valueDict.items(): 53 | if clock !=0: 54 | signalWave.addTriggerAtClock(clock=clock,wave=value) 55 | else: 56 | currentValue=None 57 | currentWave=2 58 | if parentGroup==None: 59 | if 0 in self.valueDict.keys(): 60 | initWave="x" 61 | if self.valueDict[0]!="x": 62 | initWave="2" 63 | currentValue=currentValue 64 | signalWave=wavedraw.generateBits(name=self.name,initWave=initWave,initData=self.valueDict[0],period=period) 65 | else: 66 | signalWave=wavedraw.generateBits(name=self.name,initWave="x",period=period) 67 | else: 68 | if 0 in self.valueDict.keys(): 69 | initWave="x" 70 | if self.valueDict[0]!="x": 71 | initWave="2" 72 | currentValue=currentValue 73 | signalWave=parentGroup.generateBits(name=self.name,initWave=initWave,initData=self.valueDict[0],period=period) 74 | else: 75 | signalWave=parentGroup.generateBits(name=self.name,initWave="x",period=period) 76 | for clock in sorted(self.valueDict.keys()): 77 | if clock!=0: 78 | value=self.valueDict[clock] 79 | if value!="x": 80 | if value!=currentValue: 81 | currentWave+=1 82 | if currentWave==10: 83 | currentWave=2 84 | currentValue=value 85 | signalWave.addTriggerAtClock(clock=clock,wave=str(currentWave),data=value) 86 | else: 87 | signalWave.addTriggerAtClock(clock=clock,wave="x",data=value) 88 | 89 | class SignalSet(Signal): 90 | def __init__(self, name:str,symbol:str,isBool:bool): 91 | super().__init__(name, symbol, isBool) 92 | self.bitDict={} 93 | 94 | def addBoolSignal(self,bitId:int,signal:Signal): 95 | self.bitDict[bitId]=signal 96 | 97 | def getClockValue(self,oldValue:int,oldValueValid:bool,clock:int): 98 | #find if x in this signal 99 | invalidValue=True 100 | bitValueDict={} 101 | for bitId,signal in self.bitDict.items(): 102 | if clock in signal.valueDict.keys(): 103 | if signal.valueDict[clock] not in ["1","0"]: 104 | invalidValue=False 105 | else: 106 | bitValueDict[bitId]=int(signal.valueDict[clock]) 107 | if invalidValue: 108 | newValue=0 109 | if oldValueValid: 110 | newValue=oldValue 111 | for bitId,value in bitValueDict.items(): 112 | #clear old value 113 | if ((newValue>>bitId)&0x1)==1: 114 | newValue=newValue-(1<bool: 168 | signalFind=False 169 | for signal in self.signalDict.values(): 170 | if signal.symbol==symbol: 171 | signal.addEvent(clock=clock,value=value) 172 | for module in self.subModuleDIct.values(): 173 | state=module.registerEvent(clock=clock,symbol=symbol,value=value) 174 | if state: 175 | signalFind=True 176 | return signalFind 177 | 178 | def generateWave(self,wavedraw:WaveDraw,parentGroup:WaveGroup=None,startTime:int=None,endTime:int=None,period:float=1): 179 | for signal in self.signalDict.values(): 180 | if isinstance(signal,SignalSet): 181 | signal.updateValue() 182 | signal.generateWave(wavedraw,parentGroup,startTime,endTime,period) 183 | for module in self.subModuleDIct.values(): 184 | if parentGroup==None: 185 | module.generateWave(wavedraw,wavedraw.generateGroup(name=module.moduleName),startTime,endTime,period=period) 186 | else: 187 | module.generateWave(wavedraw,parentGroup.generateGroup(name=module.moduleName),startTime,endTime,period=period) 188 | 189 | 190 | 191 | class VCDParser(object): 192 | def __init__(self,fileName:str,halfClockPeriod:int): 193 | self.fileName=fileName 194 | self.moduleCtxt=[] 195 | self.signalCtxt=[] 196 | self.top=None 197 | self.symbolList=[] 198 | self.symbolDict={} 199 | self.halfClockPeriod=halfClockPeriod 200 | self.__parser() 201 | 202 | def __parser(self): 203 | modeInfoFinished=False 204 | moduleCtxt=[] 205 | signalCtxt=[] 206 | with open(self.fileName) as hdl: 207 | lines=hdl.readlines() 208 | for line in lines: 209 | curLine=line.strip() 210 | if curLine!="": 211 | if modeInfoFinished: 212 | signalCtxt.append(curLine) 213 | else: 214 | if curLine.startswith("$enddefinitions"): 215 | modeInfoFinished=True 216 | else: 217 | moduleCtxt.append(curLine) 218 | self.__parserModule(moduleCtxt=moduleCtxt) 219 | self.__parserSignal(signalCtxt) 220 | 221 | def __parserModule(self,moduleCtxt:list[str]): 222 | moduleStack=deque() 223 | currentModule=None 224 | symbolList=[] 225 | for line in moduleCtxt: 226 | if ("$scope" in line) and ("module" in line): 227 | moduleName=line.split()[2] 228 | module=Module(name=moduleName) 229 | if self.top==None: 230 | self.top=module 231 | if currentModule==None: 232 | currentModule=module 233 | else: 234 | moduleStack.append(currentModule) 235 | currentModule.addModule(module) 236 | currentModule=module 237 | if ("$var" in line): 238 | lineSplited=line.split() 239 | symbolList.append(lineSplited[3]) 240 | signal=None 241 | if lineSplited[2]=="1" and len(lineSplited)==7: # one bit signal 242 | signal=Signal(name=f"{lineSplited[4]}{lineSplited[5]}",symbol=lineSplited[3],isBool=lineSplited[2]=="1") 243 | if currentModule.hasSignal(signalName=lineSplited[4]): 244 | currentModule.signalDict[lineSplited[4]].addBoolSignal(bitId=int(lineSplited[5][1:-1]),signal=signal) 245 | else: 246 | signalSet=SignalSet(name=lineSplited[4],symbol=lineSplited[3],isBool=False) 247 | currentModule.addSignal(signalSet) 248 | signalSet.addBoolSignal(bitId=int(lineSplited[5][1:-1]),signal=signal) 249 | else: 250 | if lineSplited[2]!="1" and len(lineSplited)==7: 251 | if not lineSplited[5].endswith(":0]"): 252 | print(f"can't analyse line:{line}") 253 | sys.exit(-1) 254 | signal=Signal(name=lineSplited[4],symbol=lineSplited[3],isBool=lineSplited[2]=="1") 255 | currentModule.addSignal(signal) 256 | if lineSplited[3] in self.symbolDict.keys(): 257 | self.symbolDict[lineSplited[3]].append(signal) 258 | else: 259 | self.symbolDict[lineSplited[3]]=[signal] 260 | if "$upscope" in line: 261 | #current module finished 262 | if len(moduleStack)>0: #not only one module 263 | currentModule=moduleStack.pop() 264 | else: #only one module 265 | pass 266 | if len(moduleStack)>0: 267 | print(f"parser failed,moduleStack is Still Not emtpy:len:{len(moduleStack)}") 268 | sys.exit(-1) 269 | self.symbolList=list(set(symbolList)) 270 | 271 | def __registerEvent(self,clock:int,symbol:str,value:str): 272 | if symbol in self.symbolList: 273 | for signal in self.symbolDict[symbol]: 274 | signal.addEvent(clock=clock,value=value) 275 | else: 276 | print(f"symbol:{symbol} not find") 277 | sys.exit(-1) 278 | 279 | def __parserSignal(self,signalCtxt:list[str]): 280 | currentClock=0 281 | for line in signalCtxt: 282 | if line.startswith("$"): 283 | pass 284 | elif line.startswith("#"): 285 | currentClock=int(int(line[1:])/self.halfClockPeriod) 286 | elif line[0] in ['0','1','x','X','z','Z']: # single bit 287 | symbolName=line[1:] 288 | value=line[0] 289 | if value not in ['0','1']: 290 | self.__registerEvent(clock=currentClock,symbol=symbolName,value="z") 291 | else: 292 | self.__registerEvent(clock=currentClock,symbol=symbolName,value=value) 293 | elif line[0] in ['b','B','r','R']: # multi bit 294 | [value,symbolName]=line.split(" ") 295 | if "x" in value: 296 | self.__registerEvent(clock=currentClock,symbol=symbolName,value="x") 297 | else: 298 | self.__registerEvent(clock=currentClock,symbol=symbolName,value=str(hex(int(value[1:],2)))) 299 | else: 300 | print(f"can't analyse line:{line}") 301 | sys.exit(-1) 302 | 303 | def generateWave(self,title:str,startTime:int=None,endTime:int=None,period:float=1)->str: 304 | wave=WaveDraw(title=title) 305 | if startTime==None or endTime==None: 306 | self.top.generateWave(wavedraw=wave,parentGroup=None,startTime=None,endTime=None,period=period) 307 | else: 308 | self.top.generateWave(wavedraw=wave,parentGroup=None,startTime=int(startTime/self.halfClockPeriod),endTime=int(endTime/self.halfClockPeriod),period=period) 309 | return wave.generateJson() 310 | 311 | 312 | 313 | 314 | --------------------------------------------------------------------------------