1 |
2 |
sinclairrf |
#!/usr/bin/python2.7
|
2 |
|
|
|
3 |
|
|
################################################################################
|
4 |
|
|
#
|
5 |
|
|
# Copyright 2012, Sinclair R.F., Inc.
|
6 |
|
|
#
|
7 |
|
|
# Build an SSBCC system.
|
8 |
|
|
#
|
9 |
|
|
################################################################################
|
10 |
|
|
|
11 |
|
|
import math
|
12 |
|
|
import os
|
13 |
|
|
import re
|
14 |
|
|
import sys
|
15 |
|
|
import tempfile
|
16 |
|
|
|
17 |
|
|
from ssbccUtil import *;
|
18 |
|
|
from ssbccConfig import SSBCCconfig;
|
19 |
|
|
|
20 |
|
|
################################################################################
|
21 |
|
|
#
|
22 |
|
|
# Surround the program with a try ... except clause
|
23 |
|
|
#
|
24 |
|
|
################################################################################
|
25 |
|
|
|
26 |
|
|
try:
|
27 |
|
|
|
28 |
|
|
################################################################################
|
29 |
|
|
#
|
30 |
|
|
# Parse the command line arguments
|
31 |
|
|
#
|
32 |
|
|
################################################################################
|
33 |
|
|
|
34 |
|
|
#
|
35 |
|
|
# Construct the command-line argument list parser
|
36 |
|
|
#
|
37 |
|
|
|
38 |
|
|
def validateFile(filename):
|
39 |
|
|
if filename == '-':
|
40 |
|
|
filename = '/dev/stdin';
|
41 |
|
|
try:
|
42 |
|
|
return file(filename,'r');
|
43 |
|
|
except:
|
44 |
|
|
raise SSBCCException('Error opening "%s"' % filename);
|
45 |
|
|
|
46 |
|
|
import argparse
|
47 |
|
|
argListParser = argparse.ArgumentParser(description='SSBCC system builder');
|
48 |
9 |
sinclairrf |
argListParser.add_argument('-D', metavar='D_name', type=str, action='append', help='Define symbol (must start with "D_")');
|
49 |
2 |
sinclairrf |
argListParser.add_argument('-G', metavar='parameter_name=value', type=str, action='append', help='Override parameter value');
|
50 |
|
|
argListParser.add_argument('-I', metavar='include_dir', type=str, action='append', help='Add search directory for included files and peripherals');
|
51 |
3 |
sinclairrf |
argListParser.add_argument('-M', metavar='macropath', action='append', help='Macro search path');
|
52 |
2 |
sinclairrf |
argListParser.add_argument('-P', metavar='peripheral_name[="parameters"]', type=str, action='append', help='Add peripheral');
|
53 |
|
|
argListParser.add_argument('-o', metavar='outCoreName', type=str, help='output core name');
|
54 |
|
|
argListParser.add_argument('-q', action='store_true', help='quiet');
|
55 |
|
|
argListParser.add_argument('--define-clog2', action='store_true', help='define clog2 instead of using built-in $clog2');
|
56 |
|
|
argListParser.add_argument('--display-opcode', action='store_true', help='add 3-letter decode of opcode (for trace viewer)');
|
57 |
11 |
sinclairrf |
argListParser.add_argument('--help-macro', metavar='macroName', type=str, help='Display usage message for the specified macro (passed on to the assembler)');
|
58 |
|
|
argListParser.add_argument('--list-macros', action='store_true', help='list the built-in and user-defined macros (passed on to the assembler)');
|
59 |
3 |
sinclairrf |
argListParser.add_argument('--rand-instr-mem', action='store_true', help='fill unused instruction memory with random values');
|
60 |
|
|
argListParser.add_argument('--synth-instr-mem', type=str, help='synthesis constraint for instruction memory');
|
61 |
2 |
sinclairrf |
argListParser.add_argument('--verilator-tracing-on', action='store_true', help='show all signals in verilator waveform files');
|
62 |
|
|
argListParser.add_argument('filename', metavar='filename', type=validateFile, help='SSBCC configuration file');
|
63 |
|
|
argList = argListParser.parse_args();
|
64 |
|
|
|
65 |
|
|
#
|
66 |
|
|
# Set the command-line dependent configuration parameters.
|
67 |
|
|
#
|
68 |
|
|
|
69 |
|
|
config = SSBCCconfig();
|
70 |
|
|
|
71 |
|
|
config.Set('define_clog2',argList.define_clog2);
|
72 |
3 |
sinclairrf |
config.Set('rand_instr_mem',argList.rand_instr_mem);
|
73 |
2 |
sinclairrf |
config.Set('verilator_tracing_on',argList.verilator_tracing_on);
|
74 |
|
|
|
75 |
|
|
if argList.display_opcode:
|
76 |
|
|
config.functions['display_opcode'] = True;
|
77 |
|
|
|
78 |
|
|
if argList.D:
|
79 |
9 |
sinclairrf |
for name in argList.D:
|
80 |
|
|
if not re.match('D_',name):
|
81 |
|
|
raise SSBCCException('Bad define name "%s" should start with "D_"' % name);
|
82 |
|
|
config.AddDefine(name);
|
83 |
2 |
sinclairrf |
|
84 |
3 |
sinclairrf |
if argList.I:
|
85 |
|
|
for pathString in argList.I:
|
86 |
|
|
if not os.path.isdir(pathString):
|
87 |
9 |
sinclairrf |
raise SSBCCException('Bad path string: "%s/%s"' % (os.getcwd(),pathString,));
|
88 |
3 |
sinclairrf |
config.AppendIncludePath(pathString);
|
89 |
|
|
config.InsertPeripheralPath(pathString);
|
90 |
|
|
|
91 |
2 |
sinclairrf |
if argList.o:
|
92 |
|
|
config.Set('outCoreName',argList.o);
|
93 |
|
|
else:
|
94 |
|
|
config.Set('outCoreName',os.path.splitext(os.path.basename(argList.filename.name))[0]);
|
95 |
|
|
|
96 |
3 |
sinclairrf |
if argList.synth_instr_mem:
|
97 |
|
|
config.Set('synth_instr_mem',argList.synth_instr_mem);
|
98 |
|
|
else:
|
99 |
|
|
config.Set('synth_instr_mem',None);
|
100 |
|
|
|
101 |
2 |
sinclairrf |
#
|
102 |
|
|
# Read the configuration file into a line-by-line buffer.
|
103 |
3 |
sinclairrf |
# Note: argList.filename is a file handle, so no paths will be searched by
|
104 |
|
|
# LoadFile. This is ensured by setting config to None.
|
105 |
2 |
sinclairrf |
#
|
106 |
|
|
|
107 |
|
|
filename = argList.filename.name;
|
108 |
3 |
sinclairrf |
configList = LoadFile(argList.filename,None);
|
109 |
2 |
sinclairrf |
ifstack = list();
|
110 |
|
|
|
111 |
|
|
configListStack = list();
|
112 |
|
|
|
113 |
|
|
#
|
114 |
|
|
# Read the configuration file.
|
115 |
|
|
#
|
116 |
|
|
|
117 |
|
|
bufLine = "";
|
118 |
|
|
compiler = [];
|
119 |
|
|
user_header = list();
|
120 |
|
|
while configList or configListStack:
|
121 |
|
|
# If the current file has ended, then proceed to the next file.
|
122 |
|
|
if not configList:
|
123 |
|
|
if not len(bufLine) == 0:
|
124 |
|
|
raise SSBCCException('Malformed configuration command at the end of %s' % filename);
|
125 |
|
|
if ifstack:
|
126 |
|
|
raise SSBCCException('%d unmatched conditional(s) at end of %s' % (len(ifstack),filename,));
|
127 |
|
|
(filename,configList,ifstack) = configListStack.pop();
|
128 |
|
|
continue;
|
129 |
|
|
# Get the next line to process and its line number.
|
130 |
|
|
(tmpLine,ixLine) = configList.pop(0);
|
131 |
|
|
# Use the start line of a sequence of lines for error messages.
|
132 |
|
|
if not bufLine:
|
133 |
|
|
loc = '%s:%d' % (filename,ixLine,);
|
134 |
|
|
# Merge continuation lines.
|
135 |
|
|
bufLine += tmpLine;
|
136 |
|
|
if bufLine and bufLine[-1] == '\\':
|
137 |
|
|
bufLine = bufLine[:-1];
|
138 |
|
|
continue;
|
139 |
|
|
line = bufLine;
|
140 |
|
|
bufLine = "";
|
141 |
|
|
# Reject blank and comment lines
|
142 |
|
|
if re.match(r'\s*(#.*)?$',line):
|
143 |
|
|
pass;
|
144 |
|
|
# .ELSE
|
145 |
|
|
elif re.match(r'\s*\.ELSE\b',line):
|
146 |
|
|
if not ifstack:
|
147 |
|
|
raise SSBCCException('unmatched ".ELSE" at %s' % loc);
|
148 |
|
|
ifstack[-1] = not ifstack[-1];
|
149 |
|
|
# .ENDIF
|
150 |
|
|
elif re.match(r'\s*\.ENDIF\b',line):
|
151 |
|
|
if not ifstack:
|
152 |
|
|
raise SSBCCException('unmatched ".ENDIF" at %s' % loc);
|
153 |
|
|
ifstack.pop();
|
154 |
|
|
# .IFDEF conditional
|
155 |
|
|
elif re.match(r'\s*\.IFDEF\b',line):
|
156 |
|
|
cmd = re.findall(r'\s*\.IFDEF\s+(\w+)\s*$',line);
|
157 |
|
|
if not cmd:
|
158 |
|
|
raise SSBCCException('Malformed ".IFDEF" configuration command on %s' % loc);
|
159 |
|
|
cmd = cmd[0];
|
160 |
|
|
ifstack.append(config.IsSymbol(cmd));
|
161 |
|
|
# .IFNDEF conditional
|
162 |
|
|
elif re.match(r'\s*\.IFNDEF\b',line):
|
163 |
|
|
cmd = re.findall(r'\s*\.IFNDEF\s+(\w+)\s*$',line);
|
164 |
|
|
if not cmd:
|
165 |
|
|
raise SSBCCException('Malformed ".IFNDEF" configuration command on %s' % loc);
|
166 |
|
|
cmd = cmd[0];
|
167 |
|
|
ifstack.append(not config.IsSymbol(cmd));
|
168 |
|
|
elif re.match(r'\s*.INCLUDE\b',line):
|
169 |
|
|
cmd = re.findall(r'\s*\.INCLUDE\s+(\S+)\s*$',line);
|
170 |
|
|
if not cmd:
|
171 |
|
|
raise SSBCCException('Malformed ".INCLUDE" configuration command on %s' % loc);
|
172 |
|
|
configListStack.append((filename,configList,ifstack,));
|
173 |
|
|
filename = cmd[0];
|
174 |
3 |
sinclairrf |
configList = LoadFile(filename,config);
|
175 |
2 |
sinclairrf |
ifstack = list();
|
176 |
|
|
# Consume configuration commands disabled by conditionals
|
177 |
|
|
elif ifstack and not ifstack[-1]:
|
178 |
|
|
pass;
|
179 |
|
|
# ARCHITECTURE
|
180 |
|
|
elif re.match(r'\s*ARCHITECTURE\b',line):
|
181 |
|
|
if config.Exists('architecture'):
|
182 |
|
|
raise SSBCCException('ARCHITECTURE already specified before %s' % loc);
|
183 |
|
|
cmd = re.findall(r'\s*ARCHITECTURE\s+(\S+)\s+(\S+)$',line);
|
184 |
|
|
if not cmd:
|
185 |
|
|
raise SSBCCException('Malformed ARCHITECTURE configuration command at %s: "%s"' % (loc,line,));
|
186 |
|
|
cmd = cmd[0];
|
187 |
|
|
config.Set('architecture',cmd[0]);
|
188 |
|
|
config.Set('hdl',cmd[1]);
|
189 |
|
|
config.Set('corepath',os.path.join(sys.path[0],config.Get('architecture')));
|
190 |
|
|
if not os.path.isdir(config.Get('corepath')):
|
191 |
|
|
raise SSBCCException('Architecture "%s" does not exist at %s' % (cmd,loc,));
|
192 |
|
|
config.InsertPeripheralPath(os.path.join(config.Get('corepath'),'peripherals'));
|
193 |
|
|
# TODO -- move these assignments into an object
|
194 |
|
|
config.Set('data_width',8);
|
195 |
|
|
# ASSEMBLY language for processor code
|
196 |
|
|
elif re.match(r'\s*ASSEMBLY\b',line):
|
197 |
|
|
cmd = re.findall(r'\s*ASSEMBLY\s+(\S.*)',line);
|
198 |
|
|
compiler = ('asm',cmd[0],);
|
199 |
|
|
# COMBINE
|
200 |
|
|
elif re.match(r'\s*COMBINE\b',line):
|
201 |
|
|
config.ProcessCombine(loc,line);
|
202 |
|
|
# CONSTANTS
|
203 |
|
|
elif re.match(r'\s*CONSTANT\b',line):
|
204 |
|
|
if not config.Exists('architecture'):
|
205 |
|
|
raise SSBCCException('"CONSTANT"s cannot be defined before the "ARCHITECTURE" is defined at %s' % loc);
|
206 |
11 |
sinclairrf |
cmd = re.findall(r'\s*CONSTANT\s+(C_\w+)\s+(0|-?[1-9]\d*|\w+)\s*$',line);
|
207 |
2 |
sinclairrf |
if not cmd:
|
208 |
|
|
raise SSBCCException('Malformed "CONSTANT" configuration command on %s: "%s"' % (loc,line,));
|
209 |
|
|
cmd = cmd[0];
|
210 |
|
|
config.AddConstant(cmd[0],cmd[1],loc);
|
211 |
|
|
# DATA_STACK
|
212 |
|
|
elif re.match(r'\s*DATA_STACK\b',line):
|
213 |
|
|
if config.Exists('data_stack'):
|
214 |
|
|
raise SSBCCException('DATA_STACK already defined before %s' % loc);
|
215 |
|
|
cmd = re.findall(r'\s*DATA_STACK\s+([1-9]\d*)',line);
|
216 |
|
|
if not cmd:
|
217 |
|
|
raise SSBCCException('Malformed "DATA_STACK" configuration command on %s: "%s"' % (loc,line,));
|
218 |
|
|
x = int(cmd[0]);
|
219 |
|
|
if math.modf(math.log(x,2))[0] != 0:
|
220 |
|
|
raise SSBCCException('DATA_STACK must be set to a power of 2, not %d, at %s' % (x,loc,));
|
221 |
|
|
if x < 8:
|
222 |
|
|
raise SSBCCException('DATA_STACK must be at least 8, not %d, at %s' % (x,loc,));
|
223 |
|
|
config.Set('data_stack',int(cmd[0]));
|
224 |
|
|
# INPORT
|
225 |
|
|
elif re.match(r'\s*INPORT\b',line):
|
226 |
|
|
if not config.Exists('architecture'):
|
227 |
|
|
raise SSBCCException('"INPORT"s cannot be defined before the "ARCHITECTURE" is defined at %s' % loc);
|
228 |
|
|
config.ProcessInport(loc,line);
|
229 |
|
|
# INSTRUCTION
|
230 |
|
|
elif re.match(r'\s*INSTRUCTION\b',line):
|
231 |
|
|
if config.Exists('nInstructions'):
|
232 |
|
|
raise SSBCCException('INSTRUCTION already specified before %s' % loc);
|
233 |
|
|
cmd = re.findall(r'\s*INSTRUCTION\s+([1-9]\d*\*?[1-9]?\d*)\s*$',line);
|
234 |
|
|
if not cmd:
|
235 |
|
|
raise SSBCCException('Malformed "INSTRUCTION" configuration command at %s: "%s"' % (loc,line,));
|
236 |
|
|
config.SetMemoryBlock('nInstructions',cmd[0],(loc,line,));
|
237 |
|
|
# INVERT_RESET
|
238 |
|
|
elif re.match(r'\s*INVERT_RESET\s*$',line):
|
239 |
|
|
if (config.Exists('invertReset')):
|
240 |
|
|
raise SSBCCException('INVERT_RESET already specified before %s' % loc);
|
241 |
|
|
config.Set('invertReset',True);
|
242 |
|
|
# LOCALPARM
|
243 |
|
|
elif re.match(r'\s*LOCALPARAM\b',line):
|
244 |
|
|
cmd = re.findall(r'\s*LOCALPARAM\s+(L_\w+)\s+(\S+)$',line);
|
245 |
|
|
if (not cmd) or (len(cmd[0]) != 2):
|
246 |
|
|
raise SSBCCException('Malformed LOCALPARAM configuration command at %s: "%s"' % (loc,line,));
|
247 |
|
|
cmd = cmd[0];
|
248 |
|
|
config.AddParameter(cmd[0],cmd[1],loc);
|
249 |
|
|
# MEMORY
|
250 |
|
|
elif re.match(r'\s*MEMORY\b',line):
|
251 |
|
|
if not config.Exists('architecture'):
|
252 |
|
|
raise SSBCCException('"MEMORY"s cannot be defined before the "ARCHITECTURE" is defined at %s' % loc);
|
253 |
|
|
# TODO -- make the maximum number of memories architecture dependent
|
254 |
|
|
if config.NMemories() >= 4:
|
255 |
|
|
raise SSBCCException('Program is limited to 4 memories');
|
256 |
|
|
cmd = re.findall(r'\s*MEMORY\s+(RAM|ROM)\s+([A-Za-z]\w*)\s+(\d+)\s*$',line);
|
257 |
|
|
if (not cmd) or (len(cmd[0]) != 3):
|
258 |
|
|
raise SSBCCException('Malformed MEMORY configuration command at %s: "%s"' % (loc,line,));
|
259 |
|
|
config.AddMemory(cmd[0],loc);
|
260 |
|
|
# OUTPORT
|
261 |
|
|
elif re.match(r'\s*OUTPORT\b',line):
|
262 |
|
|
if not config.Exists('architecture'):
|
263 |
|
|
raise SSBCCException('"OUTPORT"s cannot be defined before the "ARCHITECTURE" is defined at %s' % loc);
|
264 |
|
|
config.ProcessOutport(line,loc);
|
265 |
|
|
# PARAMETER
|
266 |
|
|
elif re.match(r'\s*PARAMETER\b',line):
|
267 |
|
|
cmd = re.findall(r'\s*PARAMETER\s+(G_\w+)\s+(\S+)$',line);
|
268 |
|
|
if (not cmd) or (len(cmd[0]) != 2):
|
269 |
|
|
raise SSBCCException('Malformed PARAMETER configuration command at %s: "%s"' % (loc,line,));
|
270 |
|
|
cmd = cmd[0];
|
271 |
|
|
config.AddParameter(cmd[0],cmd[1],loc);
|
272 |
|
|
# PERIPHERAL
|
273 |
|
|
elif re.match(r'\s*PERIPHERAL\b',line):
|
274 |
|
|
if not config.Exists('architecture'):
|
275 |
|
|
raise SSBCCException('"PERIPHERAL"s cannot be defined before the "ARCHITECTURE" is defined at %s' % loc);
|
276 |
|
|
config.ProcessPeripheral(loc,line);
|
277 |
|
|
# PORTCOMMENT
|
278 |
|
|
elif re.match(r'\s*PORTCOMMENT\b',line):
|
279 |
|
|
cmd = re.findall(r'\s*PORTCOMMENT\s+(.*)',line);
|
280 |
|
|
config.AddIO(cmd[0],0,'comment',loc);
|
281 |
|
|
# RETURN_STACK
|
282 |
|
|
elif re.match(r'\s*RETURN_STACK\b',line):
|
283 |
|
|
if config.Exists('return_stack'):
|
284 |
|
|
raise SSBCCException('RETURN_STACK already specified before %s' % loc);
|
285 |
|
|
cmd = re.findall(r'\s*RETURN_STACK\s+([1-9]\d*)',line);
|
286 |
|
|
if not cmd:
|
287 |
|
|
raise SSBCCException('Malformed "RETURN_STACK" configuration command at %s: "%s"' % (loc,line,));
|
288 |
|
|
config.Set('return_stack',int(cmd[0]));
|
289 |
|
|
# SRAM_WIDTH
|
290 |
|
|
elif re.match(r'\s*SRAM_WIDTH\b',line):
|
291 |
|
|
if config.Exists('sram_width'):
|
292 |
|
|
raise SSBCCException('SRAM_WIDTH already specified before %s' % loc);
|
293 |
|
|
cmd = re.findall(r'\s*SRAM_WIDTH\s+([1-9]\d*)',line);
|
294 |
|
|
if not cmd:
|
295 |
|
|
raise SSBCCException('Malformed "SRAM_WIDTH" configuration command %s: "%s"' % (loc,line,));
|
296 |
|
|
config.Set('sram_width',int(cmd[0]));
|
297 |
|
|
# USER_HEADER
|
298 |
|
|
elif re.match(r'\s*USER_HEADER\b',line):
|
299 |
|
|
user_header_done = False;
|
300 |
3 |
sinclairrf |
while configList:
|
301 |
|
|
(line,ixLine) = configList.pop(0);
|
302 |
|
|
if re.match(r'\s*END_USER_HEADER\b',line):
|
303 |
2 |
sinclairrf |
user_header_done = True;
|
304 |
|
|
break;
|
305 |
|
|
user_header.append(line);
|
306 |
|
|
if not user_header_done:
|
307 |
|
|
raise SSBCCException('No "END_USER_HEADER" found for "USER_HEADER" at %s' % loc);
|
308 |
|
|
# error
|
309 |
|
|
else:
|
310 |
|
|
raise SSBCCException('Unrecognized configuration command at %s: "%s"' % (loc,line,));
|
311 |
|
|
if bufLine:
|
312 |
|
|
raise SSBCCException('Malformed last line(s): "%s"' % bufLine);
|
313 |
|
|
|
314 |
|
|
if ifstack:
|
315 |
|
|
raise SSBCCException('%d unmatched conditional(s) at end of %s' % (len(ifstack),filename,));
|
316 |
|
|
|
317 |
|
|
#
|
318 |
|
|
# Incorporate command-line specified parameter and localparam values.
|
319 |
|
|
#
|
320 |
|
|
|
321 |
|
|
if argList.G:
|
322 |
|
|
for parameter in argList.G:
|
323 |
|
|
if not re.match(r'[LG]_\w+=\S+$',parameter):
|
324 |
|
|
raise SSBCCException('Malformed parameter specification: "%s"' % parameter);
|
325 |
|
|
cmd = re.findall(r'([LG]_\w+)=(\S+)',parameter);
|
326 |
|
|
cmd = cmd[0];
|
327 |
|
|
config.OverrideParameter(cmd[0],cmd[1]);
|
328 |
|
|
|
329 |
|
|
#
|
330 |
|
|
# Append peripherals from command-line.
|
331 |
|
|
#
|
332 |
|
|
|
333 |
|
|
if argList.P:
|
334 |
|
|
for peripheral in argList.P:
|
335 |
|
|
config.ProcessPeripheral(-1,'PERIPHERAL '+peripheral);
|
336 |
|
|
|
337 |
|
|
#
|
338 |
|
|
# Set unspecified default values
|
339 |
|
|
#
|
340 |
|
|
|
341 |
|
|
if not config.Exists('sram_width'):
|
342 |
|
|
config.Set('sram_width',9);
|
343 |
|
|
if not config.Exists('invertReset'):
|
344 |
|
|
config.Set('invertReset',False);
|
345 |
|
|
|
346 |
|
|
#
|
347 |
|
|
# end-of-file processing
|
348 |
|
|
#
|
349 |
|
|
|
350 |
|
|
if not config.Exists('architecture'):
|
351 |
|
|
raise SSBCCException('Required ARCHITECTURE configuration command missing');
|
352 |
|
|
if not config.Exists('data_stack'):
|
353 |
|
|
raise SSBCCException('Required DATA_STACK configuration command missing');
|
354 |
|
|
if not config.Exists('nInstructions'):
|
355 |
|
|
raise SSBCCException('Required INSTRUCTION configuration command missing');
|
356 |
|
|
if not config.Exists('return_stack'):
|
357 |
|
|
raise SSBCCException('Required RETURN_STACK configuration command missing');
|
358 |
|
|
|
359 |
|
|
# Ensure reasonable values
|
360 |
|
|
if config.Get('nInstructions')['length'] > 2**13:
|
361 |
|
|
raise SSBCCException('Instruction space cannot exceed %d at %s: "%s"' % (2**13,loc,line,));
|
362 |
|
|
|
363 |
|
|
# Add memories that are not combined into singleton entries in the "combined"
|
364 |
|
|
# list and complete the address range assignments.
|
365 |
|
|
config.CompleteCombines();
|
366 |
|
|
|
367 |
|
|
################################################################################
|
368 |
|
|
#
|
369 |
|
|
# Compile the processor code and read the tables it generated.
|
370 |
|
|
#
|
371 |
|
|
################################################################################
|
372 |
|
|
|
373 |
|
|
# Generate peripheral libraries (if any).
|
374 |
9 |
sinclairrf |
for p in config.peripheral:
|
375 |
|
|
p.GenAssembly(config);
|
376 |
2 |
sinclairrf |
|
377 |
|
|
# Compute the file name to store the assembler output
|
378 |
|
|
assemblerOutput = os.path.splitext(argList.filename.name)[0]+'.9x8-meta'
|
379 |
|
|
|
380 |
|
|
# Compute the command to invoke the compiler.
|
381 |
|
|
if not compiler:
|
382 |
|
|
raise SSBCCException('ASSEMBLY configuration command is missing');
|
383 |
|
|
cmd = os.path.join(config.Get('corepath'), compiler[0]);
|
384 |
11 |
sinclairrf |
if argList.help_macro:
|
385 |
|
|
cmd += ' --help-macro %s' % argList.help_macro
|
386 |
|
|
if argList.list_macros:
|
387 |
|
|
cmd += ' --list-macros'
|
388 |
2 |
sinclairrf |
for name in config.constants:
|
389 |
|
|
cmd += (' -C %s=%s' % (name,config.constants[name],));
|
390 |
9 |
sinclairrf |
for name in config.defines:
|
391 |
|
|
cmd += (' -D %s' % name);
|
392 |
2 |
sinclairrf |
for ix in range(len(config.parameters)):
|
393 |
|
|
cmd += (' -G %s' % config.parameters[ix][0]);
|
394 |
|
|
for ix in range(config.NInports()):
|
395 |
|
|
cmd += (' -I %s=%d' % (config.inports[ix][0],ix));
|
396 |
|
|
for ix in range(config.NOutports()):
|
397 |
|
|
if config.IsStrobeOnlyOutport(config.outports[ix]):
|
398 |
|
|
cmd += (' -R %s=%d' % (config.outports[ix][0],ix));
|
399 |
|
|
else:
|
400 |
|
|
cmd += (' -O %s=%d' % (config.outports[ix][0],ix));
|
401 |
|
|
for memNameLength in config.MemoryNameLengthList():
|
402 |
|
|
cmd += (' -S %s=%d' % memNameLength);
|
403 |
|
|
for signalNameLength in config.SignalLengthList():
|
404 |
|
|
cmd += (' -S %s=%d' % signalNameLength);
|
405 |
|
|
cmd += ' -o ' + assemblerOutput;
|
406 |
|
|
for stack_name in ('data_stack','return_stack',):
|
407 |
|
|
cmd += ' -s %s=%d' % (stack_name,config.config[stack_name],);
|
408 |
3 |
sinclairrf |
cmd += ' -L %s' % os.path.join(sys.path[0],'lib','9x8');
|
409 |
|
|
if argList.M:
|
410 |
|
|
for path in argList.M:
|
411 |
|
|
cmd += ' -M %s' % path;
|
412 |
|
|
cmd += ' -M %s' % os.path.join(sys.path[0],'macros','9x8');
|
413 |
2 |
sinclairrf |
if argList.I:
|
414 |
|
|
for pathString in argList.I:
|
415 |
|
|
cmd += ' -L %s' % pathString;
|
416 |
|
|
cmd += ' ' + compiler[1];
|
417 |
|
|
|
418 |
|
|
# Invoke the compiler and exit if it failed.
|
419 |
|
|
if not argList.q:
|
420 |
11 |
sinclairrf |
print 'Invoking the assembler with the following command: ' + cmd;
|
421 |
2 |
sinclairrf |
cmdStatus = os.system(cmd);
|
422 |
|
|
if cmdStatus != 0:
|
423 |
|
|
raise SSBCCException('Running the assembler');
|
424 |
|
|
|
425 |
|
|
# Read the assembler output tables.
|
426 |
11 |
sinclairrf |
fpAssemberOutput = open(assemblerOutput,'rt');
|
427 |
2 |
sinclairrf |
ixLine = 0;
|
428 |
|
|
programBody = list();
|
429 |
|
|
programBodyLength = 0;
|
430 |
|
|
for line in fpAssemberOutput:
|
431 |
|
|
ixLine = ixLine + 1;
|
432 |
|
|
# blank line
|
433 |
|
|
if re.match('^\s*$',line):
|
434 |
|
|
continue;
|
435 |
|
|
# memory type, name, index, and length
|
436 |
|
|
elif re.match(':memory',line):
|
437 |
|
|
cmd = re.findall(':memory (\S+) (\S+) (\S+) (\S+)',line);
|
438 |
|
|
cmd = cmd[0];
|
439 |
|
|
memName = cmd[1];
|
440 |
|
|
if not config.IsMemory(memName):
|
441 |
|
|
raise SSBCCException('%s "%s" not declared in %s' % (cmd[0],memName,argList.filename,));
|
442 |
|
|
memParam = config.GetMemoryParameters(memName);
|
443 |
|
|
if cmd[0] != memParam['type']:
|
444 |
|
|
raise SSBCCException('Type of memory "%s" is inconsistent' % memName);
|
445 |
|
|
if int(cmd[3]) > memParam['maxLength']:
|
446 |
|
|
raise SSBCCException('Length of memory "%s" is %s which exceeds limit of %d' % (memName,cmd[3],memParam['maxLength'],));
|
447 |
|
|
memoryBody = list();
|
448 |
|
|
for line in fpAssemberOutput:
|
449 |
|
|
ixLine = ixLine + 1;
|
450 |
|
|
if len(line) > 1:
|
451 |
|
|
memoryBody.append(line)
|
452 |
|
|
else:
|
453 |
|
|
break;
|
454 |
|
|
config.SetMemoryParameters(memParam,dict(bank=int(cmd[2]),length=int(cmd[3]),body=memoryBody));
|
455 |
|
|
# program .main, optional .interrupt, and length
|
456 |
|
|
elif re.match(':program',line):
|
457 |
|
|
cmd = re.findall(':program (\d+) (\S+) (\d+)',line);
|
458 |
|
|
mainStart = int(cmd[0][0]);
|
459 |
|
|
if cmd[0][1] == '[]':
|
460 |
|
|
interruptStart = -1;
|
461 |
|
|
else:
|
462 |
|
|
interruptStart = int(cmd[0][1]);
|
463 |
|
|
mainLength = int(cmd[0][2]);
|
464 |
|
|
for line in fpAssemberOutput:
|
465 |
|
|
ixLine = ixLine + 1;
|
466 |
|
|
while line and line[-1] in ('\n','\r',):
|
467 |
|
|
line = line[:-1];
|
468 |
|
|
if not line:
|
469 |
|
|
break;
|
470 |
|
|
programBody.append(line);
|
471 |
|
|
if line[0] != '-':
|
472 |
|
|
programBodyLength = programBodyLength + 1;
|
473 |
|
|
if programBodyLength != mainLength:
|
474 |
|
|
raise SSBCCException('Program Bug: program length doesn\'t match declared length');
|
475 |
|
|
maxProgramBodyLength = config.Get('nInstructions')['length'];
|
476 |
|
|
if programBodyLength > maxProgramBodyLength:
|
477 |
|
|
raise SSBCCException('Program body length = %d is longer than the allocated instruction table = %d' % (programBodyLength,maxProgramBodyLength,));
|
478 |
|
|
else:
|
479 |
|
|
raise Exception('Program Bug: Unrecognized line at %s:%d : "%s"' % (fpAssemberOutput.filename,ixLine,line,));
|
480 |
|
|
|
481 |
|
|
################################################################################
|
482 |
|
|
#
|
483 |
|
|
# Ensure the processor has been consistently defined.
|
484 |
|
|
#
|
485 |
|
|
################################################################################
|
486 |
|
|
|
487 |
|
|
# Ensure all memories are used.
|
488 |
|
|
for ixMem in range(config.NMemories()):
|
489 |
|
|
memParam = config.GetMemoryParameters(ixMem);
|
490 |
|
|
if 'length' not in memParam:
|
491 |
|
|
raise SSBCCException('Memory "%s" not used in program' % memParam['name']);
|
492 |
|
|
|
493 |
|
|
################################################################################
|
494 |
|
|
#
|
495 |
|
|
# Generate the processor core.
|
496 |
|
|
#
|
497 |
|
|
################################################################################
|
498 |
|
|
|
499 |
|
|
#
|
500 |
|
|
# Access the language-specific core generator and core.
|
501 |
|
|
#
|
502 |
|
|
|
503 |
|
|
if config.Get('hdl') == 'Verilog':
|
504 |
|
|
ssbccGenFile = 'ssbccGenVerilog.py';
|
505 |
|
|
elif config.Get('hdl') == 'VHDL':
|
506 |
|
|
ssbccGenFile = 'ssbccGenVHDL.py';
|
507 |
|
|
else:
|
508 |
|
|
raise SSBCCException('Unrecognized hdl = "%s"' % config.Get('hdl'));
|
509 |
|
|
|
510 |
|
|
ssbccGenFile = os.path.join(config.Get('corepath'),ssbccGenFile);
|
511 |
|
|
if not os.path.isfile(ssbccGenFile):
|
512 |
|
|
raise SSBCCException('Core generator "%s" missing for hdl = "%s"' % (ssbccGenFile,config.Get('hdl'),));
|
513 |
|
|
execfile(ssbccGenFile);
|
514 |
|
|
|
515 |
|
|
rawCoreName = os.path.join(config.Get('corepath'),genCoreName());
|
516 |
|
|
if not os.path.isfile(rawCoreName):
|
517 |
|
|
raise SSBCCException('Core "%s% missing for hdl = "%s"' % (rawCoreName,config.Get('hdl'),));
|
518 |
11 |
sinclairrf |
fpRawCore = open(rawCoreName,'rt');
|
519 |
2 |
sinclairrf |
|
520 |
|
|
outName = genOutName(config.Get('outCoreName'));
|
521 |
|
|
fpOutCore = open(outName,'wt');
|
522 |
|
|
|
523 |
|
|
memFileName = re.sub(r'\.v.*','.mem',outName);
|
524 |
|
|
fpMemFile = open(memFileName,'wt');
|
525 |
|
|
|
526 |
|
|
#
|
527 |
|
|
# Loop through the core, copying or filling in the file as required.
|
528 |
|
|
#
|
529 |
|
|
|
530 |
|
|
for line in fpRawCore:
|
531 |
|
|
if not re.match(r'..@SSBCC@',line):
|
532 |
|
|
if re.match(r'\s*(reg|wire)\s',line):
|
533 |
|
|
cmd = re.findall(r'\s*(reg|wire)\s+([[][^]]+]\s+)?(\w+)\b',line);
|
534 |
|
|
if config.IsSymbol(cmd[0][-1]):
|
535 |
|
|
raise SSBCCException('Symbol "%s" is used by the core and cannot be used by peripherals, etc.' % cmd[0][-1]);
|
536 |
|
|
fpOutCore.write(line);
|
537 |
|
|
continue;
|
538 |
|
|
fillCommand = re.findall(r'..@SSBCC@\s+(\S+)',line)[0];
|
539 |
|
|
# functions and tasks
|
540 |
|
|
if fillCommand == "functions":
|
541 |
|
|
genFunctions(fpOutCore,config);
|
542 |
|
|
# inports
|
543 |
|
|
elif fillCommand == 'inports':
|
544 |
|
|
genInports(fpOutCore,config);
|
545 |
|
|
# localparam
|
546 |
|
|
elif fillCommand == 'localparam':
|
547 |
|
|
genLocalParam(fpOutCore,config);
|
548 |
|
|
# memories
|
549 |
|
|
elif fillCommand == 'memories':
|
550 |
|
|
genMemories(fpOutCore,fpMemFile,config,programBody);
|
551 |
|
|
# module
|
552 |
|
|
elif fillCommand == 'module':
|
553 |
|
|
genModule(fpOutCore,config);
|
554 |
|
|
# outports
|
555 |
|
|
elif fillCommand == 'outports':
|
556 |
|
|
genOutports(fpOutCore,config);
|
557 |
|
|
# peripherals
|
558 |
|
|
elif fillCommand == 'peripherals':
|
559 |
|
|
if not config.peripheral:
|
560 |
|
|
fpOutCore.write('//\n// No peripherals\n//\n');
|
561 |
9 |
sinclairrf |
for p in config.peripheral:
|
562 |
|
|
if p != config.peripheral[0]:
|
563 |
2 |
sinclairrf |
fpOutCore.write('\n');
|
564 |
9 |
sinclairrf |
p.GenHDL(fpOutCore,config);
|
565 |
2 |
sinclairrf |
# "s_memory" declaration
|
566 |
|
|
elif fillCommand == 's_memory':
|
567 |
|
|
if config.NMemories() == 0:
|
568 |
|
|
fpOutCore.write('wire [7:0] s_memory = 8\'h00;\n');
|
569 |
|
|
else:
|
570 |
|
|
fpOutCore.write('wire [7:0] s_memory;\n');
|
571 |
|
|
# additional signals
|
572 |
|
|
elif fillCommand == 'signals':
|
573 |
|
|
genSignals(fpOutCore,config);
|
574 |
|
|
# user_header
|
575 |
|
|
elif fillCommand == 'user_header':
|
576 |
|
|
genUserHeader(fpOutCore,user_header);
|
577 |
|
|
# Verilator tracing on/off
|
578 |
|
|
elif fillCommand == "verilator_tracing":
|
579 |
|
|
if config.Get('verilator_tracing_on'):
|
580 |
|
|
fpOutCore.write('/* verilator tracing_on */\n');
|
581 |
|
|
else:
|
582 |
|
|
fpOutCore.write('/* verilator tracing_off */\n');
|
583 |
|
|
# error
|
584 |
|
|
else:
|
585 |
|
|
print 'WARNING: Unimplemented command ' + fillCommand;
|
586 |
|
|
|
587 |
|
|
#
|
588 |
|
|
# Write package file (for use in VHDL or mixed-language projects)
|
589 |
|
|
#
|
590 |
|
|
|
591 |
|
|
import ssbccGenVhdlPkg
|
592 |
|
|
ssbccGenVhdlPkg.genVhdlPkg(config);
|
593 |
|
|
|
594 |
|
|
################################################################################
|
595 |
|
|
#
|
596 |
|
|
# Terminating except clause
|
597 |
|
|
#
|
598 |
|
|
################################################################################
|
599 |
|
|
|
600 |
|
|
except SSBCCException, msg:
|
601 |
|
|
print >> sys.stderr, 'FATAL ERROR: ' + str(msg);
|
602 |
|
|
exit(1);
|