/
writer.go
202 lines (179 loc) · 6.21 KB
/
writer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
package vcd
import (
"bufio"
"fmt"
"os"
"strconv"
//"strings"
"time"
)
type VcdError struct {
msg string
}
func (error *VcdError) Error() string {
return error.msg
}
// Valid Timescale numbers.
var supportedTimescale = []int{1, 10, 100}
// Valid Timescale units.
var supportedTimescaleUnit = []string{"s", "ms", "us", "ns", "ps", "fs"}
type VcdWriter struct {
loadedFile *os.File
buffered *bufio.Writer
variableDefiner int
stringIdentifierMap map[string]VcdDataType
previousTime uint64
headerFinalized bool
}
// Creates a new VCDWriter object
// The Date is set to the current Date
// Timescale can be one of the following: 1-10-100 combined with unit: s-ms-us-ns-ps-fs
func New(filename string, timeScale string) (VcdWriter, error) {
//if !strings.HasSuffix(filename, ".vcd") {
// filename = filename + ".vcd"
//}
f, err := os.Create(filename)
writer := VcdWriter{
loadedFile: f,
buffered: bufio.NewWriter(f),
variableDefiner: 33,
stringIdentifierMap: make(map[string]VcdDataType),
previousTime: 0,
headerFinalized: false,
}
if err == nil {
dat := time.Now().Format("01-02-2006 15:04:05")
check2(writer.buffered.WriteString("$date\n\t" + dat + "\n$end\n"))
check2(writer.buffered.WriteString("$timescale " + timeScale + " $end\n"))
check(writer.buffered.Flush())
}
return writer, err
}
func stringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
return true
}
}
return false
}
// Sets the correct marshaller for the type. Different types require different formatting
// Returns an error if a not-implemented datatype is used
func initVariable(variable *VcdDataType, identifier string) error {
if !stringInSlice(variable.VariableType, supportedTypes) {
return fmt.Errorf("unsupported data type: \"%s\" supported types: %v", variable.VariableType, supportedTypes)
}
variable.identifier = identifier
switch variable.VariableType {
case "real":
variable.marshal = VcdRealType{}
case "wire":
maxVal := uint64(2 << (variable.BitDepth - 1))
variable.marshal = VcdVectorType{bitDepth: variable.BitDepth, maxVal: maxVal}
case "vector":
variable.marshal = VcdVectorType{bitDepth: variable.BitDepth, maxVal: 2 << (variable.BitDepth - 1)}
case "string":
variable.marshal = &VcdStringType{}
default:
return fmt.Errorf("not implemented datatype: \"%s\"", variable.VariableType)
}
return nil
}
func check(e error) {
if e != nil {
panic(e)
}
}
func check2(data interface{}, e error) interface{} {
if e != nil {
panic(e)
}
return data
}
// Register variables
// Variables is an array of VcdDatatTypes
// See writer.go -> NewVariable
func (vcd *VcdWriter) RegisterVariableList(module string, variables []VcdDataType) (map[string]VcdDataType, error) {
check2(vcd.buffered.WriteString("$scope module " + module + " $end\n"))
for _, variable := range variables {
check(initVariable(&variable, string(vcd.variableDefiner)))
vcd.variableDefiner = vcd.variableDefiner + 1
response := fmt.Sprintf("%s %d %s %s", variable.VariableType, variable.BitDepth, variable.identifier, variable.VariableName)
vcd.stringIdentifierMap[variable.VariableName] = variable
check2(vcd.buffered.WriteString("$var " + response + " $end\n"))
}
check2(vcd.buffered.WriteString("$upscope $end\n"))
return vcd.stringIdentifierMap, nil
}
// Register variables
// Variables is an array of VcdDatatTypes
// See writer.go -> NewVariable
func (vcd *VcdWriter) RegisterVariables(module string, variables ...VcdDataType) (map[string]VcdDataType, error) {
check2(vcd.buffered.WriteString("$scope module " + module + " $end\n"))
for _, variable := range variables {
check(initVariable(&variable, string(vcd.variableDefiner)))
vcd.variableDefiner = vcd.variableDefiner + 1
response := fmt.Sprintf("%s %d %s %s", variable.VariableType, variable.BitDepth, variable.identifier, variable.VariableName)
vcd.stringIdentifierMap[variable.VariableName] = variable
check2(vcd.buffered.WriteString("$var " + response + " $end\n"))
}
check2(vcd.buffered.WriteString("$upscope $end\n"))
return vcd.stringIdentifierMap, nil
}
func (vcd *VcdWriter) DumpValues(identifierToValue map[string]string) {
_, e := vcd.buffered.WriteString("$dumpvars\n")
check(e)
for i, _ := range identifierToValue {
val, e := vcd.stringIdentifierMap[i].marshal.format(identifierToValue[i])
check(e)
check2(vcd.buffered.WriteString(val + " " + vcd.stringIdentifierMap[i].identifier + "\n"))
}
_, e = vcd.buffered.WriteString("$end\n")
check(e)
}
// Sets a valie for a specific variable
// Time in timeunits, always has to be the same, or larger as the previous time
// Panics when value can not be marshaled, or when there are problems with the time
func (vcd *VcdWriter) SetValue(time uint64, value string, variableName string) error {
if time < vcd.previousTime {
return fmt.Errorf("changing value from an earlier time: %d < %d", time, vcd.previousTime)
}
if time != vcd.previousTime {
_, _ = vcd.buffered.WriteString("#" + strconv.FormatUint(time, 10) + "\n")
vcd.previousTime = time
}
if !vcd.headerFinalized {
check2(vcd.buffered.WriteString("$enddefinitions $end\n"))
vcd.headerFinalized = true
}
format, e := vcd.stringIdentifierMap[variableName].marshal.format(value)
if e != nil {
if e == duplicateErr {
return nil
} else {
panic(e)
}
}
check2(vcd.buffered.WriteString(format + " " + vcd.stringIdentifierMap[variableName].identifier + "\n"))
return e
}
// Sets the Comment in the vcd. Can be used together with the SetVersion
// Can only be used before registering the variables
func (vcd *VcdWriter) SetComment(comment string) *VcdWriter {
check2(vcd.buffered.WriteString("$comment\n\t" + comment + "\n$end\n"))
return vcd
}
// Sets the Version in the vcd. Can be used together with the SetComment
// Can only be used before registering the variables
func (vcd *VcdWriter) SetVersion(version string) *VcdWriter {
check2(vcd.buffered.WriteString("$version\n\t" + version + "\n$end\n"))
return vcd
}
func (vcd *VcdWriter) SetTimestamp(time uint64) {
_, _ = vcd.buffered.WriteString("#" + strconv.FormatUint(time, 10) + "\n")
}
// Closes and flushes the files
func (vcd VcdWriter) Close() {
check(vcd.buffered.Flush())
check(vcd.loadedFile.Close())
}