OpenCores
URL https://opencores.org/ocsvn/a-z80/a-z80/trunk

Subversion Repositories a-z80

[/] [a-z80/] [trunk/] [cpu/] [control/] [genmatrix.py] - Blame information for rev 8

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 8 gdevic
#!/usr/bin/env python3
2 3 gdevic
#
3
# This script reads A-Z80 instruction timing data from a spreadsheet text file
4 8 gdevic
# 'Timings.csv' (which is a TAB-delimited text file exported from 'Timings.xlsm')
5 3 gdevic
# and generates a Verilog include file defining the control block execution matrix.
6 6 gdevic
# Token keywords in the timing spreadsheet are substituted using a list of keys
7 8 gdevic
# defined in 'timing_macros.i'.
8 3 gdevic
#
9
#-------------------------------------------------------------------------------
10 8 gdevic
#  Copyright (C) 2014,2016  Goran Devic
11 3 gdevic
#
12
#  This program is free software; you can redistribute it and/or modify it
13
#  under the terms of the GNU General Public License as published by the Free
14
#  Software Foundation; either version 2 of the License, or (at your option)
15
#  any later version.
16
#
17
#  This program is distributed in the hope that it will be useful, but WITHOUT
18
#  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19
#  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
20
#  more details.
21
#-------------------------------------------------------------------------------
22
import string
23
import sys
24
import csv
25
import os
26
 
27 8 gdevic
# Input file (exported from 'Timings.xlsm'):
28 3 gdevic
fname = "Timings.csv"
29
 
30
# Input file containing macro substitution keys
31
kname = "timing_macros.i"
32
 
33
# Set this to 1 if you want abbreviated matrix (no-action lines removed)
34
abbr = 1
35
 
36 8 gdevic
# Set this to 0 if you want to strip all comments from the resulting file
37
comment = 1
38
 
39 3 gdevic
# Set this to 1 if you want debug $display() printout on each PLA line
40
debug = 0
41
 
42
# Print this string in front of every line that starts with "ctl_". This helps
43
# formatting the output to be more readable.
44
ctl_prefix = "\n"+" "*19
45
 
46
# Read in the content of the macro substitution file
47
macros = []
48
with open(kname, 'r') as f:
49
    for line in f:
50
        if len(line.strip())>0 and line[0]!='/':
51
            # Wrap up non-starting //-style comments into /* ... */ so the
52
            # line can be concatenated while preserving comments
53 8 gdevic
            i = line.find("//")
54
            if i>0:
55
                if comment==1:
56
                    macros.append( line.rstrip().replace("//", "/*", 1) + " */" )
57
                else:
58
                    macros.append( line.rstrip()[0:i] )
59 3 gdevic
            else:
60
                macros.append(line.rstrip())
61
 
62
# List of errors / keys and macros that did not match. We stash them as we go
63
# and then print at the end so it is easier to find them
64
errors = []
65
 
66
# Returns a substitution string given the section name (key) and the macro token
67
# This is done by simply traversing macro substitution list of lines, finding a
68
# section that starts with a :key and copying the substitution lines verbatim.
69
def getSubst(key, token):
70
    subst = []
71
    multiline = False
72
    validset = False
73
    if key=="Comments":                 # Special case: ignore "Comments" column!
74
        return ""
75
    for l in macros:
76
        if multiline==True:
77
            # Multiline copies lines until a char at [0] is not a space
78
            if len(l.strip())==0 or l[0]!=' ':
79 8 gdevic
                return '\n' + "\n".join(subst).rstrip()
80 3 gdevic
            else:
81 8 gdevic
                subst.append(l.rstrip())
82 3 gdevic
        lx = l.split(' ')               # Split the string and then ignore (duplicate)
83 8 gdevic
        lx = list(filter(None, lx))     # spaces in the list left by the split()
84 3 gdevic
        if l.startswith(":"):           # Find and recognize a matching set (key) section
85
            if validset:                # Error if there is a new section going from the macthing one
86
                break                   # meaning we did not find our macro in there
87
            if l[1:]==key:
88
                validset = True
89
        elif validset and lx[0]==token:
90
            if len(lx)==1:
91
                return ""
92
            if lx[1]=='\\':             # Multi-line macro state starts with '\' character
93
                multiline = True
94
                continue
95
            lx.pop(0)
96
            s = " ".join(lx)
97
            return ' ' + s.strip()
98
    err = "{0} not in {1}".format(token, key)
99
    if err not in errors:
100
        errors.append(err)
101
    return " --- {0} ?? {1} --- ".format(token, key)
102
 
103
# Read the content of a file and using the csv reader and remove any quotes from the input fields
104
content = []                            # Content of the spreadsheet timing file
105 8 gdevic
with open(fname, 'r') as csvFile:
106 3 gdevic
    reader = csv.reader(csvFile, delimiter='\t', quotechar='"')
107
    for row in reader:
108
        content.append('\t'.join(row))
109
 
110
# The first line is special: it contains names of sets for our macro substitutions
111
tkeys = {}                              # Spreadsheet table column keys
112
tokens = content.pop(0).split('\t')
113
for col in range(len(tokens)):
114
    if len(tokens[col])==0:
115
        continue
116
    tkeys[col] = tokens[col]
117
 
118
# Process each line separately (stateless processor)
119
imatrix = []    # Verilog execution matrix code
120
for line in content:
121
    col = line.split('\t')              # Split the string into a list of columns
122 8 gdevic
    col_clean = list(filter(None, col)) # Removed all empty fields (between the separators)
123 3 gdevic
    if len(col_clean)==0:               # Ignore completely empty lines
124
        continue
125
 
126 8 gdevic
    if col_clean[0].startswith('//') and comment==1:
127
        imatrix.append(col_clean[0])    # Optionally print comment lines
128 3 gdevic
 
129
    if col_clean[0].startswith("#end"): # Print the end of a condition
130
        imatrix.append("end\n")
131
 
132
    if col_clean[0].startswith('#if'):  # Print the start of a condition
133
        s = col_clean[0]
134
        tag = s.find(":")
135
        condition = s[4:tag]
136
        imatrix.append("if ({0}) begin".format(condition.strip()))
137
        if debug and len(s[tag:])>1:    # Print only in debug and there is something to print
138
            imatrix.append("    $display(\"{0}\");".format(s[4:]))
139
 
140
    # We recognize 2 kinds of timing statements based on the starting characters:
141
    # "#0"..        common timings using M and T cycles (M being optional)
142
    # "#always"     timing that does not depend on M and T cycles (ex. ALU operations)
143
    if col_clean[0].startswith('#0') or col_clean[0].startswith('#always'):
144
        # M and T states are hard-coded in the table at the index 1 and 2
145
        if col_clean[0].startswith('#0'):
146
            if col[1]=='?':     # M is optional, use '?' to skip it
147 8 gdevic
                state = "    if (T{0}) begin".format(col[2])
148 3 gdevic
            else:
149 8 gdevic
                state = "    if (M{0} & T{1}) begin".format(col[1], col[2])
150 3 gdevic
        else:
151 8 gdevic
            state = "    begin"
152 3 gdevic
 
153
        # Loop over all other columns and perform verbatim substitution
154
        action = ""
155
        for i in range(3,len(col)):
156
            # There may be multiple tokens separated by commas
157
            tokList = col[i].strip().split(',')
158 8 gdevic
            tokList = list(filter(None, tokList)) # Filter out empty lines
159 3 gdevic
            for token in tokList:
160
                token = token.strip()
161
                if i in tkeys and len(token)>0:
162
                    macro = getSubst(tkeys[i], token)
163
                    if macro.strip().startswith("ctl_"):
164
                        action += ctl_prefix
165
                    action += macro
166
                    if state.find("ERROR")>=0:
167 8 gdevic
                        print ("{0} {1}".format(state, action))
168 3 gdevic
                        break
169
 
170
        # Complete and write out a line
171
        if abbr and len(action)==0:
172
            continue
173
        imatrix.append("{0}{1} end".format(state, action))
174
 
175
# Create a file containing the logic matrix code
176 6 gdevic
with open('exec_matrix.vh', 'w') as file:
177 8 gdevic
    if comment==1:
178
        file.write("// Automatically generated by genmatrix.py\n\n")
179 3 gdevic
    # If there were errors, print them first (and output to the console)
180
    if len(errors)>0:
181
        for error in errors:
182 8 gdevic
            print (error)
183 3 gdevic
            file.write(error + "\n")
184
        file.write("-" * 80 + "\n")
185
    for item in imatrix:
186
        file.write("{}\n".format(item))
187
 
188 6 gdevic
# Touch a file that includes 'exec_matrix.vh' to ensure it will recompile correctly
189 8 gdevic
os.utime("execute.v", None)

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.