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

Subversion Repositories ssbcc

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

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

powered by: WebSVN 2.1.0

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