OpenCores
URL https://opencores.org/ocsvn/ssbcc/ssbcc/trunk

Subversion Repositories ssbcc

[/] [ssbcc/] [trunk/] [ssbccConfig.py] - Blame information for rev 10

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 2 sinclairrf
################################################################################
2
#
3
# Copyright 2012-2013, Sinclair R.F., Inc.
4
#
5
# Utilities required by ssbcc
6
#
7
################################################################################
8
 
9
import math
10
import os
11
import re
12
import sys
13
 
14
from ssbccUtil import *
15
 
16
class SSBCCconfig():
17
  """
18
  Container for ssbcc configuration commands, the associated parsing, and
19
  program generation.
20
  """
21
 
22
  def __init__(self):
23
    """
24
    Initialize the empty dictionaries holding the processor configuration
25
    parameters.  Initialize the paths to search for peripherals.
26
    """
27
    self.config         = dict();               # various settings, etc.
28
    self.constants      = dict();               # CONSTANTs
29 9 sinclairrf
    self.defines        = dict();               # defines
30 2 sinclairrf
    self.functions      = dict();               # list of functions to define
31
    self.inports        = list();               # INPORT definitions
32
    self.ios            = list();               # List of I/Os
33
    self.outports       = list();               # OUTPORT definitions (see AddOutport)
34
    self.parameters     = list();               # PARAMETERs and LOCALPARAMs
35
    self.peripheral     = list();               # PERIPHERALs
36
    self.signals        = list();               # internal signals
37
    self.symbols        = list();               # constant, I/O, inport, etc.  names
38
 
39
    # list of memories
40
    self.memories = dict(name=list(), type=list(), maxLength=list());
41
 
42
    # list of how the memories will be instantiated
43
    self.config['combine'] = list();
44
 
45 3 sinclairrf
    # initial search path for .INCLUDE configuration commands
46
    self.includepaths = list();
47
    self.includepaths.append('.');
48
 
49 2 sinclairrf
    # initial search paths for peripherals
50 3 sinclairrf
    self.peripheralpaths = list();
51 2 sinclairrf
    self.peripheralpaths.append('.');
52
    self.peripheralpaths.append('peripherals');
53
    self.peripheralpaths.append(os.path.join(sys.path[0],'core/peripherals'));
54
 
55
  def AddConstant(self,name,value,loc):
56
    """
57
    Add the constant for the "CONSTANT" configuration command to the "constants"
58
    dictionary.\n
59
    name        symbol for the constant
60
    value       value of the constant
61
    loc         file name and line number for error messages
62
    """
63
    self.AddSymbol(name,loc);
64
    if name in self.constants:
65
      raise SSBCCException('CONSTANT "%s" already declared at %s' % (name,loc,));
66 8 sinclairrf
    if not IsIntExpr(value):
67
      raise SSBCCException('Could not evaluate expression "%s" for constant at %s' % (value,loc,));
68
    self.constants[name] = ParseIntExpr(value);
69 2 sinclairrf
 
70 9 sinclairrf
  def AddDefine(self,name):
71
    """
72
    Add the defined symbol.\n
73
    name        name for the symbol (must start with "D_")\n
74
    Note:  This is only invoked for the command line arguments so there is no
75
           "loc" available.\n
76
    Note:  Defines can be declared more than once on the command line with no
77
           ill effects.
78
    """
79
    if not self.IsDefine(name):
80
      self.AddSymbol(name);
81
      self.defines[name] = 1;
82
 
83 2 sinclairrf
  def AddIO(self,name,nBits,iotype,loc):
84
    """
85
    Add an I/O signal to the processor interface to the system.\n
86
    name        name of the I/O signal
87
    nBits       number of bits in the I/O signal
88
    iotype      signal direction:  "input", "output", or "inout"
89
    """
90
    if iotype != 'comment':
91
      self.AddSymbol(name,loc);
92
    self.ios.append((name,nBits,iotype,));
93
 
94
  def AddInport(self,port,loc):
95
    """
96
    Add an INPORT symbol to the processor.\n
97
    port        name of the INPORT symbol
98
    loc         file name and line number for error messages
99
    """
100
    name = port[0];
101
    self.AddSymbol(name,loc);
102
    self.inports.append(port);
103
 
104
  def AddMemory(self,cmd,loc):
105
    """
106
    Add a memory to the list of memories.\n
107
    cmd         3-element list as follows:
108
                [0] ==> type:  "RAM" or "ROM"
109
                [1] ==> memory name
110
                [2] ==> memory length (must be a power of 2)
111
    loc         file name and line number for error messages
112
    """
113
    self.memories['type'].append(cmd[0]);
114
    self.memories['name'].append(cmd[1]);
115
    maxLength = eval(cmd[2]);
116
    if not IsPowerOf2(maxLength):
117
      raise SSBCCException('Memory length must be a power of 2, not "%s", at %s' % (cmd[2],loc,));
118
    self.memories['maxLength'].append(eval(cmd[2]));
119
 
120
  def AddOutport(self,port,loc):
121
    """
122
    Add an OUTPORT symbol to the processor.\n
123
    port        tuple as follows:
124
                port[0] - name of the OUTPORT symbol
125
                port[1] - True if the outport is a strobe-only outport, false
126
                          otherwise
127
                port[2:] - zero or more tuples as follows:
128
                  (o_signal,width,type,[initialization],)
129
                where
130
                  o_signal is the name of the output signal
131
                  width is the number of bits in the signal
132
                  type is 'data' or 'strobe'
133
                  initialization is an optional initial/reset value for the
134
                    output signal
135
    loc         file name and line number for error messages
136
    """
137
    self.AddSymbol(port[0],loc);
138
    self.outports.append(port);
139
 
140
  def AddParameter(self,name,value,loc):
141
    """
142
    Add a PARAMETER to the processor.\n
143
    name        name of the PARAMETER
144
    value       value of the PARAMETER
145
    loc         file name and line number for error messages
146
    """
147
    if not re.match(r'[LG]_\w+$',name):
148
      raise Exception('Program Bug -- bad parameter name at %s' % loc);
149
    self.AddSymbol(name,loc);
150
    self.parameters.append((name,value,));
151
 
152
  def AddSignal(self,name,nBits,loc):
153
    """
154
    Add a signal without an initial value to the processor.\n
155
    name        name of the signal
156
    nBits       number of bits in the signal
157
    loc         file name and line number for error messages
158
    """
159
    self.AddSymbol(name,loc);
160
    self.signals.append((name,nBits,));
161
 
162
  def AddSignalWithInit(self,name,nBits,init,loc):
163
    """
164
    Add a signal with an initial/reset value to the processor.\n
165
    name        name of the signal
166
    nBits       number of bits in the signal
167
    init        initial/reset value of the signal
168
    loc         file name and line number for error messages
169
    """
170
    self.AddSymbol(name,loc);
171
    self.signals.append((name,nBits,init,));
172
 
173
  def AddSymbol(self,name,loc=None):
174
    """
175
    Add the specified name to the list of symbols.\n
176
    Note:  This symbol has no associated functionality and is only used for
177
           ".ifdef" conditionals.
178
    """
179
    if name in self.symbols:
180
      if loc == None:
181
        raise SSBCCException('Symbol "%s" already defined, no line number provided');
182
      else:
183
        raise SSBCCException('Symbol "%s" already defined before %s' % (name,loc,));
184
    self.symbols.append(name);
185
 
186 3 sinclairrf
  def AppendIncludePath(self,path):
187
    """
188
    Add the specified path to the end of the paths to search for .INCLUDE
189
    configuration commands.\n
190
    path        path to add to the list
191
    """
192
    self.includepaths.insert(-1,path);
193
 
194 2 sinclairrf
  def CompleteCombines(self):
195
    """
196
    Ensure all memories are assigned addresses.\n
197
    This modifies config['combine'] to include singleton entries for any
198
    memories not subject to the COMBINE configuration command.  It then computes
199
    how the memories will be packed together as well as properites for the
200
    packed memories.  These properties are:
201
      packing   how the memories will be packed as per PackCombinedMemory
202
      memName   HDL name of the memory
203
      memLength number of words in the memory
204
      memWidth  bit width of the memory words
205
    """
206
    # Create singleton entries for memory types and memories that aren't already listed in 'combine'.
207
    if not self.IsCombined('INSTRUCTION'):
208
      self.config['combine'].append({'mems':['INSTRUCTION',], 'memArch':'sync'});
209
    for memType in ('DATA_STACK','RETURN_STACK',):
210
      if not self.IsCombined(memType):
211
        self.config['combine'].append({'mems':[memType,], 'memArch':'LUT'});
212
    for memName in self.memories['name']:
213
      if not self.IsCombined(memName):
214
        self.config['combine'].append({'mems':[memName,], 'memArch':'LUT'});
215
    # Determine the HDL names for the memories.
216
    nRAMROMs = 0;
217
    for combined in self.config['combine']:
218
      if combined['mems'][0] == 'INSTRUCTION':
219
        combined['memName'] = 's_opcodeMemory';
220
      elif combined['mems'][0] == 'DATA_STACK':
221
        combined['memName'] = 's_data_stack';
222
      elif combined['mems'][0] == 'RETURN_STACK':
223
        combined['memName'] = 's_R_stack';
224
      else:
225
        nRAMROMs += 1;
226
    if nRAMROMs > 0:
227
      memNameFormat = 's_mem_%%0%dx' % ((CeilLog2(nRAMROMs)+3)/4);
228
    ixRAMROM = 0;
229
    for combined in self.config['combine']:
230
      if 'memName' in combined:
231
        continue;
232
      if nRAMROMs == 1:
233
        combined['memName'] = 's_mem';
234
      else:
235
        combined['memName'] = memNameFormat % ixRAMROM;
236
        ixRAMROM += 1;
237
    # Perform packing for all memories.
238
    for combined in self.config['combine']:
239
      self.PackCombinedMemory(combined);
240
 
241
  def Exists(self,name):
242
    """
243
    Return true if the requested attribute has been created in the ssbccConfig
244
    object.
245
    """
246
    return name in self.config;
247
 
248
  def Get(self,name):
249
    """
250
    Return the requested attribute from the ssbccConfig object.
251
    """
252
    if not self.Exists(name):
253
      raise Exception('Program Bug:  "%s" not found in config' % name);
254
    return self.config[name];
255
 
256
  def GetMemoryByBank(self,ixBank):
257
    """
258
    Return the parameters for a memory by its bank address.\n
259
    ixBank      index of the requested memory bank
260
    """
261
    if not 'bank' in self.memories:
262
      return None;
263
    if ixBank not in self.memories['bank']:
264
      return None;
265
    ixMem = self.memories['bank'].index(ixBank);
266
    return self.GetMemoryParameters(ixMem);
267
 
268
  def GetMemoryByName(self,name):
269
    """
270
    Return the parameters for a memory by its name.\n
271
    name        name of the requested memory
272
    """
273
    if not name in self.memories['name']:
274
      return None;
275
    ixMem = self.memories['name'].index(name);
276
    return self.GetMemoryParameters(ixMem);
277
 
278
  def GetMemoryParameters(self,rawIndex):
279
    """
280
    Return the parameters for a memory by its index in the list of memories.\n
281
    rawIndex    index within the list of memories
282
    """
283
    if type(rawIndex) == str:
284
      if not self.IsMemory(rawIndex):
285
        raise Exception('Program Bug:  reference to non-existent memory');
286
      ix = self.memories['name'].index(rawIndex);
287
    elif type(rawIndex) == int:
288
      if (rawIndex < 0) or (rawIndex >= len(self.memories['name'])):
289
        raise Exception('Program Bug:  bad memory index %d' % rawIndex);
290
      ix = rawIndex;
291
    else:
292
      raise Exception('Program Bug:  unrecognized index type "%s"' % type(rawIndex));
293
    outvalue = dict();
294
    outvalue['index'] = ix;
295
    for field in self.memories:
296
      outvalue[field] = self.memories[field][ix];
297
    return outvalue;
298
 
299
  def GetPacking(self,name):
300
    """
301
    Get the memory packing for the provided memory.
302
    """
303
    for combined in self.config['combine']:
304
      if name not in combined['mems']:
305
        continue;
306
      for port in combined['port']:
307
        for packing in port['packing']:
308
          if packing['name'] == name:
309
            return (combined,port,packing,);
310
    else:
311
      raise Exception('Program Bug -- %s not found in combined memories' % name);
312
 
313
  def GetParameterValue(self,name):
314
    """
315
    Get the value associated with the named parameter.
316
    """
317
    if name.find('[') != -1:
318
      ix = name.index('[');
319
      thisSlice = name[ix:];
320
      name = name[:ix];
321
    else:
322
      thisSlice = '[0+:8]';
323
    for ix in range(len(self.parameters)):
324
      if self.parameters[ix][0] == name:
325 3 sinclairrf
        return ExtractBits(IntValue(self.parameters[ix][1]),thisSlice);
326 2 sinclairrf
    else:
327
      raise Exception('Program Bug:  Parameter "%s" not found' % name);
328
 
329
  def InsertPeripheralPath(self,path):
330
    """
331
    Add the specified path to the beginning of the paths to search for
332
    peripherals.\n
333
    path        path to add to the list
334
    """
335
    self.peripheralpaths.insert(-1,path);
336
 
337
  def IsCombined(self,name):
338
    """
339
    Indicate whether or not the specified memory type has already been listed
340
    in a "COMBINE" configuration command.  The memory type should be one of
341
    DATA_STACK, INSTRUCTION, or RETURN_STACK.\n
342
    name        name of the specified memory type\n
343
    """
344
    for combined in self.config['combine']:
345
      if name in combined['mems']:
346
        return True;
347
    else:
348
      return False;
349
 
350 4 sinclairrf
  def IsConstant(self,name):
351
    """
352
    Indicate whether or not the specified symbol is a recognized constant.
353
    """
354
    if re.match(r'C_\w+$',name) and name in self.constants:
355
      return True;
356
    else:
357
      return False;
358
 
359 9 sinclairrf
  def IsDefine(self,name):
360
    """
361
    Indicate whether or not the specified symbol is a recognized define.
362
    """
363
    if re.match(r'D_\w+$',name) and name in self.defines:
364
      return True;
365
    else:
366
      return False;
367
 
368 2 sinclairrf
  def IsMemory(self,name):
369
    """
370
    Indicate whether or not the specified symbol is the name of a memory.
371
    """
372
    return (name in self.memories['name']);
373
 
374
  def IsParameter(self,name):
375
    """
376
    Indicate whether or not the specified symbol is the name of a parameter.
377
    """
378 4 sinclairrf
    if re.match(r'[GL]_\w+$',name) and name in self.symbols:
379 2 sinclairrf
      return True;
380
    else:
381
      return False;
382
 
383
  def IsRAM(self,name):
384
    """
385
    Indicate whether or not the specified symbol is the name of a RAM.
386
    """
387
    if name not in self.memories['name']:
388
      return False;
389
    ix = self.memories['name'].index(name);
390
    return self.memories['type'][ix] == 'RAM';
391
 
392
  def IsROM(self,name):
393
    """
394
    Indicate whether or not the specified symbol is the name of a RAM.
395
    """
396
    if name not in self.memories['name']:
397
      return False;
398
    ix = self.memories['name'].index(name);
399
    return self.memories['type'][ix] == 'ROM';
400
 
401
  def IsStrobeOnlyOutport(self,outport):
402
    """
403
    Indicate whether or not the specified outport symbol only has strobes
404
    associated with it (i.e., it has no data signals).
405
    """
406
    return outport[1];
407
 
408
  def IsSymbol(self,name):
409
    """
410
    Indicate whether or not the specified name is a symbol.
411
    """
412
    return (name in self.symbols);
413
 
414
  def MemoryNameLengthList(self):
415
    """
416
    Return a list of tuples where each tuple is the name of a memory and its
417
    length.
418
    """
419
    outlist = list();
420
    for ix in range(len(self.memories['name'])):
421
      outlist.append((self.memories['name'][ix],self.memories['maxLength'][ix],));
422
    return outlist;
423
 
424
  def NInports(self):
425
    """
426
    Return the number of INPORTS.
427
    """
428
    return len(self.inports);
429
 
430
  def NMemories(self):
431
    """
432
    Return the number of memories.
433
    """
434
    return len(self.memories['name']);
435
 
436
  def NOutports(self):
437
    """
438
    Return the number of OUTPORTS.
439
    """
440
    return len(self.outports);
441
 
442
  def OverrideParameter(self,name,value):
443
    """
444
    Change the value of the specified parameter (based on the command line
445
    argument instead of the architecture file).\n
446
    name        name of the parameter to change
447
    value       new value of the parameter
448
    """
449
    for ix in range(len(self.parameters)):
450
      if self.parameters[ix][0] == name:
451
        break;
452
    else:
453
      raise SSBCCException('Command-line parameter or localparam "%s" not specified in the architecture file' % name);
454
    self.parameters[ix] = (name,value,);
455
 
456
  def PackCombinedMemory(self,combined):
457
    """
458
    Utility function for CompleteCombines.\n
459
    Determine packing strategy and resulting memory addresses and sizes.  This
460
    list has everything ssbccGenVerilog needs to construct the memory.\n
461
    The dual port memories can be used to do the following:
462
      1.  pack a single memory, either single-port or dual-port
463
      2.  pack two single-port memories sequentially, i.e., one at the start of
464
          the RAM and one toward the end of the RAM
465
      3.  pack one single-port memory at the start of the RAM and pack several
466
          compatible single-port memories in parallel toward the end of the RAM.
467
          Note:  Compatible means that they have the same address.
468
      4.  pack several compatible dual-port memories in parallel.\n
469
    These single-port or dual-port single or parallel packed memories are
470
    described in the 'port' list in combined.  Each entry in the port list has
471
    several parameters described below and a 'packing' list that describes the
472
    single or multiple memories attached to that port.\n
473
    The parameters for each of port is as follows:
474
      offset    start address of the memory in the packing
475
      nWords    number of RAM words reserved for the memory
476
                Note:  This can be larger than the aggregate number of words
477
                       required by the memory in order to align the memories to
478
                       power-of-2 address alignments.
479
      ratio     number of base memory entries for the memory
480
                Note:  This must be a power of 2.\n
481
    The contents of each entry in the packing are as follows:
482
      -- the following are from the memory declaration
483
      name      memory name
484
      length    number of elements in the memory based on the declared memory
485
                size
486
                Note:  This is based on the number of addresses required for
487
                       each memory entry (see ratio).
488
      nbits     width of the memory type
489
      -- the following are derived for the packing
490
      lane      start bit
491
                Note:  This is required in particular when memories are stacked
492
                       in parallel.
493
      nWords    number of memory addresses allocated for the memory based on
494
                the packing
495
                Note:  This will be larger than length when a small memory is
496
                       packed in parallel with a larger memory.  I.e., when
497
                       ratio is not one.
498
      ratio     number of base memory entries required to extract a single word
499
                for the memory type
500
                Note:  This allows return stack entries to occupy more than one
501
                       memory address when the return stack is combined with
502
                       other memory addresses.
503
                Note:  This must be a power of 2.\n
504
    The following entries are also added to "combined":
505
      nWords    number of words in the memory
506
      memWidth  bit width of the memory words\n
507
    Note:  If memories are being combined with the instructions space, they are
508
           always packed at the end of the instruction space, so the
509
           instruction space allocation is not included in the packing.
510
    """
511
    # Count how many memories of each type are being combined.
512
    nSinglePort = 0;
513
    nRAMs = 0;
514
    nROMs = 0;
515
    for memName in combined['mems']:
516
      if memName in ('INSTRUCTION','DATA_STACK','RETURN_STACK',):
517
        nSinglePort += 1;
518
      elif self.IsROM(memName):
519
        nROMs += 1;
520
      else:
521
        nRAMs += 1;
522
    if nRAMs > 0:
523
      nRAMs += nROMs;
524
      nROMs = 0;
525
    # Ensure the COMBINE configuration command is implementable in a dual-port RAM.
526
    if nSinglePort > 0 and nRAMs > 0:
527
      raise SSBCCException('Cannot combine RAMs with other memory types in COMBINE configuration command at %s' % combined['loc']);
528
    if nSinglePort > 2 or (nSinglePort > 1 and nROMs > 0):
529
      raise SSBCCException('Too many memory types in COMBINE configuration command at %s' % combined['loc']);
530
    # Start splitting the listed memories into the one or two output lists and ensure that single-port memories are listed in the correct order.
531
    mems = combined['mems'];
532
    ixMem = 0;
533
    split = list();
534
    if 'INSTRUCTION' in mems:
535
      if mems[0] != 'INSTRUCTION':
536
        raise SSBCCException('INSTRUCTION must be the first memory listed in the COMBINE configuration command at %s' % combined['loc']);
537
      split.append(['INSTRUCTION']);
538
      ixMem += 1;
539
    while len(mems[ixMem:]) > 0 and mems[ixMem] in ('DATA_STACK','RETURN_STACK',):
540
      split.append([mems[ixMem]]);
541
      ixMem += 1;
542
    for memName in ('DATA_STACK','RETURN_STACK',):
543
      if memName in mems[ixMem:]:
544
        raise SSBCCException('Single-port memory %s must be listed before ROMs in COMBINE configuration command at %s' % combined['loc']);
545
    if mems[ixMem:]:
546
      split.append(mems[ixMem:]);
547
    if not (1 <= len(split) <= 2):
548
      raise Exception('Program Bug -- bad COMBINE configuration command not caught');
549
    # Create the detailed packing information.
550
    combined['port'] = list();
551
    for thisSplit in split:
552
      packing = list();
553
      for memName in thisSplit:
554
        if memName == 'INSTRUCTION':
555
          packing.append({'name':memName, 'length':self.Get('nInstructions')['length'], 'nbits':9});
556
        elif memName == 'DATA_STACK':
557
          packing.append({'name':memName, 'length':self.Get('data_stack'), 'nbits':self.Get('data_width')});
558
        elif memName == 'RETURN_STACK':
559
          nbits = max(self.Get('data_width'),self.Get('nInstructions')['nbits']);
560
          packing.append({'name':memName, 'length':self.Get('return_stack'), 'nbits':nbits});
561
        else:
562
          thisMemory = self.GetMemoryParameters(memName);
563
          packing.append({'name':memName, 'length':CeilPow2(thisMemory['maxLength']), 'nbits':self.Get('data_width')});
564
      combined['port'].append({ 'packing':packing });
565
    # Calculate the width of the base memory.
566
    # Note:  This accommodates RETURN_STACK being an isolated memory.
567
    memWidth = combined['port'][0]['packing'][0]['nbits'] if len(combined['port']) == 1 else None;
568
    for port in combined['port']:
569
      for packing in port['packing']:
570
        tempMemWidth = packing['nbits'];
571
        if tempMemWidth > self.Get('sram_width'):
572
          tempMemWidth = self.Get('sram_width');
573
        if not memWidth:
574
          memWidth = tempMemWidth;
575
        elif tempMemWidth > memWidth:
576
          memWidth = tempMemWidth;
577
    combined['memWidth'] = memWidth;
578
    # Determine how the memories are packed.
579
    # Note:  "ratio" should be non-unity only for RETURN_STACK.
580
    for port in combined['port']:
581
      lane = 0;
582
      for packing in port['packing']:
583
        packing['lane'] = lane;
584
        ratio = CeilPow2((packing['nbits']+memWidth-1)/memWidth);
585
        packing['ratio'] = ratio;
586
        packing['nWords'] = ratio * packing['length'];
587
        lane += ratio;
588
    # Aggregate parameters each memory port.
589
    for port in combined['port']:
590
      ratio = CeilPow2(sum(packing['ratio'] for packing in port['packing']));
591
      maxLength = max(packing['length'] for packing in port['packing']);
592
      port['ratio'] = ratio;
593
      port['nWords'] = ratio * maxLength;
594
    combined['port'][0]['offset'] = 0;
595
    if len(combined['port']) > 1:
596
      if combined['mems'][0] == 'INSTRUCTION':
597
        nWordsTail = combined['port'][1]['nWords'];
598
        port0 = combined['port'][0];
599
        if port0['nWords'] <= nWordsTail:
600
          raise SSBCCException('INSTRUCTION length too small for "COMBINE INSTRUCTION,..." at %s' % combined['loc']);
601
        port0['nWords'] -= nWordsTail;
602
        port0['packing'][0]['nWords'] -= nWordsTail;
603
        port0['packing'][0]['length'] -= nWordsTail;
604
      else:
605
        maxNWords = max(port['nWords'] for port in combined['port']);
606
        for port in combined['port']:
607
          port['nWords'] = maxNWords;
608
      combined['port'][1]['offset'] = combined['port'][0]['nWords'];
609
    combined['nWords'] = sum(port['nWords'] for port in combined['port']);
610
 
611
  def ProcessCombine(self,loc,line):
612
    """
613
    Parse the "COMBINE" configuration command as follows:\n
614
    Validate the arguments to the "COMBINE" configuration command and append
615
    the list of combined memories and the associated arguments to "combine"
616
    property.\n
617
    The argument consists of one of the following:
618
      INSTRUCTION,{DATA_STACK,RETURN_STACK,rom_list}
619
      DATA_STACK
620
      DATA_STACK,{RETURN_STACK,rom_list}
621
      RETURN_STACK
622
      RETURN_STACK,{DATA_STACK,rom_list}
623
      mem_list
624
    where rom_list is a comma separated list of one or more ROMs and mem_list is
625
    a list of one or more RAMs or ROMs.
626
    """
627
    # Perform some syntax checking and get the list of memories to combine.
628
    cmd = re.findall(r'\s*COMBINE\s+(\S+)\s*$',line);
629
    if not cmd:
630
      raise SSBCCException('Malformed COMBINE configuration command on %s' % loc);
631
    mems = re.split(r',',cmd[0]);
632
    if (len(mems)==1) and ('INSTRUCTION' in mems):
633
      raise SSBCCException('"COMBINE INSTRUCTION" doesn\'t make sense at %s' % loc);
634
    if ('INSTRUCTION' in mems) and (mems[0] != 'INSTRUCTION'):
635
      raise SSBCCException('"INSTRUCTION" must be listed first in COMBINE configuration command at %s' % loc);
636
    recognized = ['INSTRUCTION','DATA_STACK','RETURN_STACK'] + self.memories['name'];
637
    unrecognized = [memName for memName in mems if memName not in recognized];
638
    if unrecognized:
639
      raise SSBCCException('"%s" not recognized in COMBINE configuration command at %s' % (unrecognized[0],loc,));
640
    alreadyUsed = [memName for memName in mems if self.IsCombined(memName)];
641
    if alreadyUsed:
642
      raise SSBCCException('"%s" already used in COMBINE configuration command before %s' % (alreadyUsed[0],loc,));
643 10 sinclairrf
    repeated = [mems[ix] for ix in range(len(mems)-1) if mems[ix] in mems[ix+1:]];
644 2 sinclairrf
    if repeated:
645
      raise SSBCCException('"%s" repeated in COMBINE configuration command on %s' % (repeated[0],loc,));
646
    # Count the number of the different memory types being combined and validate the combination.
647
    nSinglePort = sum([thisMemName in ('INSTRUCTION','DATA_STACK','RETURN_STACK',) for thisMemName in mems]);
648
    nROM = len([thisMemName for thisMemName in mems if self.IsROM(thisMemName)]);
649
    nRAM = len([thisMemName for thisMemName in mems if self.IsRAM(thisMemName)]);
650
    if nRAM > 0:
651
      nRAM += nROM;
652
      nROM = 0;
653
    if nROM > 0:
654
      nSinglePort += 1;
655
    nDualPort = 1 if nRAM > 0 else 0;
656
    if nSinglePort + 2*nDualPort > 2:
657
      raise SSBCCException('Too many ports required for COMBINE configuration command at %s' % loc);
658
    # Append the listed memory types to the list of combined memories.
659
    self.config['combine'].append({'mems':mems, 'memArch':'sync', 'loc':loc});
660
 
661
  def ProcessInport(self,loc,line):
662
    """
663
    Parse the "INPORT" configuration commands as follows:
664
      The configuration command is well formatted.
665
      The number of signals matches the corresponding list of signal declarations.
666
      The port name starts with 'I_'.
667
      The signal declarations are valid.
668
        n-bit where n is an integer
669
        set-reset
670
        strobe
671
      That no other signals are specified in conjunction with a "set-reset" signal.
672
      The total input data with does not exceed the maximum data width.\n
673
    The input port is appended to the list of inputs as a tuple.  The first
674
    entry in the tuple is the port name.  The subsequent entries are tuples
675
    consisting of the following:
676
      signal name
677
      signal width
678
      signal type
679
    """
680
    cmd = re.findall(r'\s*INPORT\s+(\S+)\s+(\S+)\s+(I_\w+)\s*$',line);
681
    if not cmd:
682
      raise SSBCCException('Malformed INPORT statement at %s: "%s"' % (loc,line[:-1],));
683
    modes = re.findall(r'([^,]+)',cmd[0][0]);
684
    names = re.findall(r'([^,]+)',cmd[0][1]);
685
    portName = cmd[0][2];
686
    if len(modes) != len(names):
687
      raise SSBCCException('Malformed INPORT configuration command -- number of options don\'t match on %s: "%s"' % (loc,line[:-1],));
688
    # Append the input signal names, mode, and bit-width to the list of I/Os.
689
    has__set_reset = False;
690
    nBits = 0;
691
    thisPort = (portName,);
692
    for ix in range(len(names)):
693
      if re.match(r'^\d+-bit$',modes[ix]):
694
        thisNBits = int(modes[ix][0:-4]);
695
        self.AddIO(names[ix],thisNBits,'input',loc);
696
        thisPort += ((names[ix],thisNBits,'data',),);
697
        nBits = nBits + thisNBits;
698
      elif modes[ix] == 'set-reset':
699
        has__set_reset = True;
700
        self.AddIO(names[ix],1,'input',loc);
701
        thisPort += ((names[ix],1,'set-reset',),);
702
        self.AddSignal('s_SETRESET_%s' % names[ix],1,loc);
703
      elif modes[ix] == 'strobe':
704
        self.AddIO(names[ix],1,'output',loc);
705
        thisPort += ((names[ix],1,'strobe',),);
706
      else:
707
        raise SSBCCException('Unrecognized INPORT signal type "%s"' % modes[ix]);
708
      if has__set_reset and len(names) > 1:
709
        raise SSBCCException('set-reset cannot be simultaneous with other signals in "%s"' % line[:-1]);
710
      if nBits > self.Get('data_width'):
711
        raise SSBCCException('Signal width too wide in "%s"' % line[:-1]);
712
    self.AddInport(thisPort,loc);
713
 
714
  def ProcessOutport(self,line,loc):
715
    """
716
    Parse the "OUTPORT" configuration commands as follows:
717
      The configuration command is well formatted.
718
      The number of signals matches the corresponding list of signal declarations.
719
      The port name starts with 'O_'.
720
      The signal declarations are valid.
721
        n-bit[=value]
722
        strobe
723
      The total output data with does not exceed the maximum data width.\n
724
    The output port is appended to the list of outports as a tuple.  The first
725
    entry in this tuple is the port name.  The subsequent entries are tuples
726
    consisting of the following:
727
      signal name
728
      signal width
729
      signal type
730
      initial value (optional)
731
    """
732
    cmd = re.findall(r'^\s*OUTPORT\s+(\S+)\s+(\S+)\s+(O_\w+)\s*$',line);
733
    if not cmd:
734
      raise SSBCCException('Malformed OUTPUT configuration command on %s: "%s"' % (loc,line[:-1],));
735
    modes = re.findall(r'([^,]+)',cmd[0][0]);
736
    names = re.findall(r'([^,]+)',cmd[0][1]);
737
    portName = cmd[0][2];
738
    if len(modes) != len(names):
739
      raise SSBCCException('Malformed OUTPORT configuration command -- number of widths/types and signal names don\'t match on %s: "%s"' % (loc,line[:-1],));
740
    # Append the input signal names, mode, and bit-width to the list of I/Os.
741
    nBits = 0;
742
    isStrobeOnly = True;
743
    thisPort = tuple();
744
    for ix in range(len(names)):
745
      if re.match(r'\d+-bit',modes[ix]):
746
        isStrobeOnly = False;
747
        a = re.match(r'(\d+)-bit(=\S+)?$',modes[ix]);
748
        if not a:
749
          raise SSBCCException('Malformed bitwith/bitwidth=initialization on %s:  "%s"' % (loc,modes[ix],));
750
        thisNBits = int(a.group(1));
751
        self.AddIO(names[ix],thisNBits,'output',loc);
752
        if a.group(2):
753
          thisPort += ((names[ix],thisNBits,'data',a.group(2)[1:],),);
754
        else:
755
          thisPort += ((names[ix],thisNBits,'data',),);
756
        nBits = nBits + thisNBits;
757
        self.config['haveBitOutportSignals'] = 'True';
758
      elif modes[ix] == 'strobe':
759
        self.AddIO(names[ix],1,'output',loc);
760
        thisPort += ((names[ix],1,'strobe',),);
761
      else:
762
        raise SSBCCException('Unrecognized OUTPORT signal type on %s: "%s"' % (loc,modes[ix],));
763
      if nBits > 8:
764
        raise SSBCCException('Signal width too wide on %s:  in "%s"' % (loc,line[:-1],));
765
    self.AddOutport((portName,isStrobeOnly,)+thisPort,loc);
766
 
767
  def ProcessPeripheral(self,loc,line):
768
    """
769
    Process the "PERIPHERAL" configuration command as follows:
770
      Validate the format of the configuration command.
771
      Find the peripheral in the candidate list of paths for peripherals.
772
      Execute the file declaring the peripheral.
773
        Note:  This is done since I couldn't find a way to "import" the
774
               peripheral.  Executing the peripheral makes its definition local
775
               to this invokation of the ProcessPeripheral function, but the
776
               object subsequently created retains the required functionality
777
               to instantiate the peripheral
778
      Go through the parameters for the peripheral and do the following for each:
779
        If the argument for the peripheral is the string "help", then print the
780
          docstring for the peripheral and exit.
781
        Append the parameter name and its argument to the list of parameters
782
          (use "None" as the argument if no argument was provided).
783
      Append the instantiated peripheral to the list of peripherals.
784
        Note:  The "exec" function dynamically executes the instruction to
785
               instantiate the peripheral and append it to the list of
786
               peripherals.
787
    """
788
    # Validate the format of the peripheral configuration command and the the name of the peripheral.
789
    cmd = re.findall(r'\s*PERIPHERAL\s+(\w+)\s*(.*)$',line);
790
    if not cmd:
791
      raise SSBCCException('Missing peripheral name in %s:  %s' % (loc,line[:-1],));
792
    peripheral = cmd[0][0];
793
    # Find and execute the peripheral Python script.
794
    # Note:  Because "execfile" and "exec" method are used to load the
795
    #        peripheral python script, the __file__ object is set to be this
796
    #        file, not the peripheral source file.
797
    for testPath in self.peripheralpaths:
798
      fullperipheral = os.path.join(testPath,'%s.py' % peripheral);
799
      if os.path.isfile(fullperipheral):
800
        break;
801
    else:
802
      raise SSBCCException('Peripheral "%s" not found' % peripheral);
803
    execfile(fullperipheral);
804
    # Convert the space delimited parameters to a list of tuples.
805
    param_list = list();
806
    for param_string in re.findall(r'(\w+="[^"]*"|\w+=\S+|\w+)\s*',cmd[0][1]):
807
      if param_string == "help":
808
        exec('helpmsg = %s.__doc__' % peripheral);
809
        if not helpmsg:
810
          raise SSBCCException('No help for peripheral %s is provided' % fullperipheral);
811
        print;
812
        print 'Help message for peripheral:  %s' % peripheral;
813
        print 'Located at:  %s' % fullperipheral;
814
        print;
815
        print helpmsg;
816
        raise SSBCCException('Terminated by "help" for peripheral %s' % peripheral);
817
      ix = param_string.find('=');
818
      if param_string.find('="') > 0:
819
        param_list.append((param_string[:ix],param_string[ix+2:-1],));
820
      elif param_string.find('=') > 0:
821
        param_list.append((param_string[:ix],param_string[ix+1:],));
822
      else:
823
        param_list.append((param_string,None));
824
    # Add the peripheral to the micro controller configuration.
825
    exec('self.peripheral.append(%s(fullperipheral,self,param_list,loc));' % peripheral);
826
 
827
  def Set(self,name,value):
828
    """
829
    Create or override the specified attribute in the ssbccConfig object.
830
    """
831
    self.config[name] = value;
832
 
833
  def SetMemoryBlock(self,name,value,errorInfo):
834
    """
835
    Set an attribute in the ssbccConfig object for the specified memory with
836
    the specified memory architecture.\n
837
    "value" must be a string with the format "\d+" or "\d+*\d+" where "\d+" is
838
    an integer.  The first format specifies a single memory with the stated
839
    size and the size must be a power of two.  The second format specified
840
    allocation of multiple memory blocks where the size is given by the first
841
    integer and must be a power of 2 and the number of blocks is given by the
842
    second integer and doesn't need to be a power of 2.
843
    """
844
    findStar = value.find('*');
845
    if findStar == -1:
846
      blockSize = int(value);
847
      nBlocks = 1;
848
    else:
849
      blockSize = int(value[0:findStar]);
850
      nBlocks = int(value[findStar+1:]);
851
    nbits_blockSize = int(round(math.log(blockSize,2)));
852
    if blockSize != 2**nbits_blockSize:
853 3 sinclairrf
      raise SSBCCException('block size must be a power of 2 at %s: "%s"' % errorInfo);
854 2 sinclairrf
    nbits_nBlocks = CeilLog2(nBlocks);
855
    self.Set(name, dict(
856
                   length=blockSize*nBlocks,
857
                   nbits=nbits_blockSize+nbits_nBlocks,
858
                   blockSize=blockSize,
859
                   nbits_blockSize=nbits_blockSize,
860
                   nBlocks=nBlocks,
861
                   nbits_nBlocks=nbits_nBlocks));
862
 
863
  def SetMemoryParameters(self,memParam,values):
864
    """
865
    Record the body of the specified memory based on the assembler output.
866
    """
867
    index = memParam['index'];
868
    for field in values:
869
      if field not in self.memories:
870
        self.memories[field] = list();
871
        for ix in range(len(self.memories['name'])):
872
          self.memories[field].append(None);
873
      self.memories[field][index] = values[field];
874
 
875
  def SignalLengthList(self):
876
    """
877
    Generate a list of the I/O signals and their lengths.
878
    """
879
    outlist = list();
880
    for io in self.ios:
881
      if io[2] == 'comment':
882
        continue;
883
      outlist.append((io[0],io[1],));
884
    return outlist;

powered by: WebSVN 2.1.0

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